Merge pull request #6166 from whymarrh/bye-bye-old-ui

Delete the old UI
feature/default_network_editable
Whymarrh Whitby 6 years ago committed by GitHub
commit 65bfdeedc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      .circleci/config.yml
  2. 2
      README.md
  3. 2
      docs/README.md
  4. 6
      docs/ui-dev-mode.md
  5. 8
      docs/ui-mock-mode.md
  6. 66
      old-ui/.gitignore
  7. 297
      old-ui/app/account-detail.js
  8. 86
      old-ui/app/account-qr.js
  9. 119
      old-ui/app/accounts/import/index.js
  10. 124
      old-ui/app/accounts/import/json.js
  11. 69
      old-ui/app/accounts/import/private-key.js
  12. 30
      old-ui/app/accounts/import/seed.js
  13. 202
      old-ui/app/add-suggested-token.js
  14. 241
      old-ui/app/add-token.js
  15. 315
      old-ui/app/app.js
  16. 321
      old-ui/app/components/account-dropdowns.js
  17. 132
      old-ui/app/components/account-export.js
  18. 86
      old-ui/app/components/account-panel.js
  19. 436
      old-ui/app/components/app-bar.js
  20. 96
      old-ui/app/components/balance.js
  21. 46
      old-ui/app/components/binary-renderer.js
  22. 181
      old-ui/app/components/bn-as-decimal-input.js
  23. 261
      old-ui/app/components/buy-button-subview.js
  24. 63
      old-ui/app/components/coinbase-form.js
  25. 59
      old-ui/app/components/copyButton.js
  26. 46
      old-ui/app/components/copyable.js
  27. 60
      old-ui/app/components/custom-radio-list.js
  28. 98
      old-ui/app/components/dropdown.js
  29. 57
      old-ui/app/components/editable-label.js
  30. 176
      old-ui/app/components/ens-input.js
  31. 95
      old-ui/app/components/eth-balance.js
  32. 64
      old-ui/app/components/fiat-value.js
  33. 154
      old-ui/app/components/hex-as-decimal-input.js
  34. 74
      old-ui/app/components/identicon.js
  35. 55
      old-ui/app/components/loading.js
  36. 59
      old-ui/app/components/mascot.js
  37. 132
      old-ui/app/components/menu-droppo.js
  38. 74
      old-ui/app/components/mini-account-panel.js
  39. 131
      old-ui/app/components/network.js
  40. 145
      old-ui/app/components/notice.js
  41. 50
      old-ui/app/components/pending-msg-details.js
  42. 70
      old-ui/app/components/pending-msg.js
  43. 60
      old-ui/app/components/pending-personal-msg-details.js
  44. 47
      old-ui/app/components/pending-personal-msg.js
  45. 519
      old-ui/app/components/pending-tx.js
  46. 60
      old-ui/app/components/pending-typed-msg-details.js
  47. 46
      old-ui/app/components/pending-typed-msg.js
  48. 58
      old-ui/app/components/range-slider.js
  49. 306
      old-ui/app/components/shapeshift-form.js
  50. 204
      old-ui/app/components/shift-list-item.js
  51. 37
      old-ui/app/components/tab-bar.js
  52. 18
      old-ui/app/components/template.js
  53. 72
      old-ui/app/components/token-cell.js
  54. 205
      old-ui/app/components/token-list.js
  55. 22
      old-ui/app/components/tooltip.js
  56. 68
      old-ui/app/components/transaction-list-item-icon.js
  57. 258
      old-ui/app/components/transaction-list-item.js
  58. 87
      old-ui/app/components/transaction-list.js
  59. 69
      old-ui/app/components/typed-message-renderer.js
  60. 247
      old-ui/app/conf-tx.js
  61. 383
      old-ui/app/config.js
  62. 21
      old-ui/app/css/debug.css
  63. 372
      old-ui/app/css/fonts.css
  64. 939
      old-ui/app/css/index.css
  65. 306
      old-ui/app/css/lib.css
  66. 5722
      old-ui/app/css/output/index.css
  67. 48
      old-ui/app/css/reset.css
  68. 42
      old-ui/app/css/transitions.css
  69. 179
      old-ui/app/first-time/init-menu.js
  70. BIN
      old-ui/app/img/identicon-tardigrade.png
  71. BIN
      old-ui/app/img/identicon-walrus.png
  72. 154
      old-ui/app/info.js
  73. 653
      old-ui/app/infura-conversion.json
  74. 91
      old-ui/app/keychains/hd/create-vault-complete.js
  75. 121
      old-ui/app/keychains/hd/recover-seed/confirmation.js
  76. 162
      old-ui/app/keychains/hd/restore-vault.js
  77. 29
      old-ui/app/new-keychain.js
  78. 85
      old-ui/app/new-ui-annoucement.js
  79. 65
      old-ui/app/provider-approval.js
  80. 309
      old-ui/app/send.js
  81. 59
      old-ui/app/settings.js
  82. 30
      old-ui/app/template.js
  83. 122
      old-ui/app/unlock.js
  84. 241
      old-ui/app/util.js
  85. 29
      old-ui/css.js
  86. BIN
      old-ui/design/00-metamask-SignIn.jpg
  87. BIN
      old-ui/design/01-metamask-SelectAcc.jpg
  88. BIN
      old-ui/design/02-metamask-AccDetails.jpg
  89. BIN
      old-ui/design/02a-metamask-AccDetails-OverToken.jpg
  90. BIN
      old-ui/design/02a-metamask-AccDetails-OverTransaction.jpg
  91. BIN
      old-ui/design/02a-metamask-AccDetails.jpg
  92. BIN
      old-ui/design/02b-metamask-AccDetails-Send.jpg
  93. BIN
      old-ui/design/03-metamask-Qr.jpg
  94. BIN
      old-ui/design/05-metamask-Menu.jpg
  95. BIN
      old-ui/design/chromeStorePics/final_screen_dao_accounts.png
  96. BIN
      old-ui/design/chromeStorePics/final_screen_dao_locked.png
  97. BIN
      old-ui/design/chromeStorePics/final_screen_dao_notification.png
  98. BIN
      old-ui/design/chromeStorePics/final_screen_wei_account.png
  99. BIN
      old-ui/design/chromeStorePics/final_screen_wei_notification.png
  100. BIN
      old-ui/design/chromeStorePics/icon-128.png
  101. Some files were not shown because too many files have changed in this diff Show More

@ -20,11 +20,11 @@ workflows:
# - test-deps:
# requires:
# - prep-deps-npm
- test-e2e-beta-chrome:
- test-e2e-chrome:
requires:
- prep-deps-npm
- prep-build
- test-e2e-beta-firefox:
- test-e2e-firefox:
requires:
- prep-deps-npm
- prep-build
@ -52,8 +52,8 @@ workflows:
- test-lint
- test-unit
- test-mozilla-lint
- test-e2e-beta-chrome
- test-e2e-beta-firefox
- test-e2e-chrome
- test-e2e-firefox
# - test-e2e-beta-drizzle
- test-integration-flat-chrome
- test-integration-flat-firefox
@ -189,7 +189,7 @@ jobs:
# - store_artifacts:
# path: test-artifacts
# destination: test-artifacts
test-e2e-beta-chrome:
test-e2e-chrome:
docker:
- image: circleci/node:8.11.3-browsers
steps:
@ -197,13 +197,13 @@ jobs:
- attach_workspace:
at: .
- run:
name: test:e2e:chrome:beta
command: npm run test:e2e:chrome:beta
name: test:e2e:chrome
command: npm run test:e2e:chrome
- store_artifacts:
path: test-artifacts
destination: test-artifacts
test-e2e-beta-firefox:
test-e2e-firefox:
docker:
- image: circleci/node:8.11.3-browsers
steps:
@ -214,8 +214,8 @@ jobs:
- attach_workspace:
at: .
- run:
name: test:e2e:firefox:beta
command: npm run test:e2e:firefox:beta
name: test:e2e:firefox
command: npm run test:e2e:firefox
- store_artifacts:
path: test-artifacts
destination: test-artifacts

@ -65,11 +65,9 @@ To write tests that will be run in the browser using QUnit, add your test files
- [How to add custom build to Chrome](./docs/add-to-chrome.md)
- [How to add custom build to Firefox](./docs/add-to-firefox.md)
- [How to develop a live-reloading UI](./docs/ui-dev-mode.md)
- [How to add a new translation to MetaMask](./docs/translating-guide.md)
- [Publishing Guide](./docs/publishing.md)
- [The MetaMask Team](./docs/team.md)
- [How to develop an in-browser mocked UI](./docs/ui-mock-mode.md)
- [How to live reload on local dependency changes](./docs/developing-on-deps.md)
- [How to add new networks to the Provider Menu](./docs/adding-new-networks.md)
- [How to manage notices that appear when the app starts up](./docs/notices.md)

@ -12,9 +12,7 @@ To learn how to develop MetaMask-compatible applications, visit our [Developer D
- [How to add custom build to Chrome](./add-to-chrome.md)
- [How to add custom build to Firefox](./add-to-firefox.md)
- [How to develop a live-reloading UI](./ui-dev-mode.md)
- [Publishing Guide](./publishing.md)
- [How to develop an in-browser mocked UI](./ui-mock-mode.md)
- [How to live reload on local dependency changes](./developing-on-deps.md)
- [How to add new networks to the Provider Menu](./adding-new-networks.md)
- [How to manage notices that appear when the app starts up](./notices.md)

@ -1,6 +0,0 @@
# Running UI Dev Mode
You can run `npm run ui`, and your browser should open a live-reloading demo version of the plugin UI.
Some actions will crash the app, so this is only for tuning aesthetics, but it allows live-reloading styles, which is a much faster feedback loop than reloading the full extension.

@ -1,8 +0,0 @@
### Developing on UI with Mocked Background Process
You can run `npm run mock` and your browser should open a live-reloading demo version of the plugin UI, just like the `npm run ui`, except that it tries to actually perform all normal operations.
It does not yet connect to a real blockchain (this could be a good test feature later, connecting to a test blockchain), so only local operations work.
You can reset the mock ui at any time with the `Reset` button at the top of the screen.

66
old-ui/.gitignore vendored

@ -1,66 +0,0 @@
# Created by https://www.gitignore.io/api/osx,node
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history

@ -1,297 +0,0 @@
const inherits = require('util').inherits
const extend = require('xtend')
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
const valuesFor = require('./util').valuesFor
const Identicon = require('./components/identicon')
const EthBalance = require('./components/eth-balance')
const TransactionList = require('./components/transaction-list')
const ExportAccountView = require('./components/account-export')
const ethUtil = require('ethereumjs-util')
const EditableLabel = require('./components/editable-label')
const TabBar = require('./components/tab-bar')
const TokenList = require('./components/token-list')
const AccountDropdowns = require('./components/account-dropdowns').AccountDropdowns
module.exports = connect(mapStateToProps)(AccountDetailScreen)
function mapStateToProps (state) {
return {
metamask: state.metamask,
identities: state.metamask.identities,
accounts: state.metamask.accounts,
address: state.metamask.selectedAddress,
accountDetail: state.appState.accountDetail,
network: state.metamask.network,
unapprovedMsgs: valuesFor(state.metamask.unapprovedMsgs),
shapeShiftTxList: state.metamask.shapeShiftTxList,
transactions: state.metamask.selectedAddressTxList || [],
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
currentAccountTab: state.metamask.currentAccountTab,
tokens: state.metamask.tokens,
suggestedTokens: state.metamask.suggestedTokens,
computedBalances: state.metamask.computedBalances,
}
}
inherits(AccountDetailScreen, Component)
function AccountDetailScreen () {
Component.call(this)
}
AccountDetailScreen.prototype.render = function () {
var props = this.props
var selected = props.address || Object.keys(props.accounts)[0]
var checksumAddress = selected && ethUtil.toChecksumAddress(selected)
var identity = props.identities[selected]
var account = props.accounts[selected]
const { network, conversionRate, currentCurrency } = props
if (Object.keys(props.suggestedTokens).length > 0) {
this.props.dispatch(actions.showAddSuggestedTokenPage())
}
return (
h('.account-detail-section.full-flex-height', [
// identicon, label, balance, etc
h('.account-data-subsection', {
style: {
margin: '0 20px',
flex: '1 0 auto',
},
}, [
// header - identicon + nav
h('div', {
style: {
paddingTop: '20px',
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'flex-start',
},
}, [
// large identicon and addresses
h('.identicon-wrapper.select-none', [
h(Identicon, {
diameter: 62,
address: selected,
}),
]),
h('flex-column', {
style: {
lineHeight: '10px',
marginLeft: '15px',
width: '100%',
},
}, [
h(EditableLabel, {
textValue: identity ? identity.name : '',
state: {
isEditingLabel: false,
},
saveText: (text) => {
props.dispatch(actions.setAccountLabel(selected, text))
},
}, [
// What is shown when not editing + edit text:
h('label.editing-label', [h('.edit-text', 'edit')]),
h(
'div',
{
style: {
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
},
},
[
h(
'div.font-medium.color-forest',
{
name: 'edit',
style: {
},
},
[
h('h2', {
style: {
maxWidth: '180px',
overflow: 'hidden',
textOverflow: 'ellipsis',
padding: '5px 0px',
lineHeight: '25px',
},
}, [
identity && identity.name,
]),
]
),
h(
AccountDropdowns,
{
style: {
marginRight: '8px',
marginLeft: 'auto',
cursor: 'pointer',
},
selected,
network,
identities: props.identities,
enableAccountOptions: true,
},
),
]
),
]),
h('.flex-row', {
style: {
width: '15em',
justifyContent: 'space-between',
alignItems: 'baseline',
},
}, [
// address
h('div', {
style: {
overflow: 'hidden',
textOverflow: 'ellipsis',
paddingTop: '3px',
width: '5em',
height: '15px',
fontSize: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
marginBottom: '15px',
color: '#AEAEAE',
},
}, checksumAddress),
]),
// account ballence
]),
]),
h('.flex-row', {
style: {
justifyContent: 'space-between',
alignItems: 'flex-start',
},
}, [
h(EthBalance, {
value: account && account.balance,
conversionRate,
currentCurrency,
style: {
lineHeight: '7px',
marginTop: '10px',
},
}),
h('.flex-grow'),
h('button', {
onClick: () => props.dispatch(actions.buyEthView(selected)),
style: { marginRight: '10px' },
}, 'BUY'),
h('button', {
onClick: () => props.dispatch(actions.showSendPage()),
style: {
marginBottom: '20px',
marginRight: '8px',
},
}, 'SEND'),
]),
]),
// subview (tx history, pk export confirm, buy eth warning)
this.subview(),
])
)
}
AccountDetailScreen.prototype.subview = function () {
var subview
try {
subview = this.props.accountDetail.subview
} catch (e) {
subview = null
}
switch (subview) {
case 'transactions':
return this.tabSections()
case 'export':
var state = extend({key: 'export'}, this.props)
return h(ExportAccountView, state)
default:
return this.tabSections()
}
}
AccountDetailScreen.prototype.tabSections = function () {
const { currentAccountTab } = this.props
return h('section.tabSection.full-flex-height.grow-tenx', [
h(TabBar, {
tabs: [
{ content: 'Sent', key: 'history' },
{ content: 'Tokens', key: 'tokens' },
],
defaultTab: currentAccountTab || 'history',
tabSelected: (key) => {
this.props.dispatch(actions.setCurrentAccountTab(key))
},
}),
this.tabSwitchView(),
])
}
AccountDetailScreen.prototype.tabSwitchView = function () {
const props = this.props
const { address, network } = props
const { currentAccountTab, tokens } = this.props
switch (currentAccountTab) {
case 'tokens':
return h(TokenList, {
userAddress: address,
network,
tokens,
addToken: () => this.props.dispatch(actions.showAddTokenPage()),
})
default:
return this.transactionList()
}
}
AccountDetailScreen.prototype.transactionList = function () {
const {transactions, unapprovedMsgs, address,
network, shapeShiftTxList, conversionRate } = this.props
return h(TransactionList, {
transactions: transactions.sort((a, b) => b.time - a.time),
network,
unapprovedMsgs,
conversionRate,
address,
shapeShiftTxList,
viewPendingTx: (txId) => {
this.props.dispatch(actions.viewPendingTx(txId))
},
})
}

@ -1,86 +0,0 @@
const PropTypes = require('prop-types')
const {PureComponent} = require('react')
const h = require('react-hyperscript')
const {qrcode: qrCode} = require('qrcode-npm')
const {connect} = require('react-redux')
const {isHexPrefixed} = require('ethereumjs-util')
const actions = require('../../ui/app/actions')
const CopyButton = require('./components/copyButton')
class AccountQrScreen extends PureComponent {
static defaultProps = {
warning: null,
}
static propTypes = {
dispatch: PropTypes.func.isRequired,
buyView: PropTypes.any.isRequired,
Qr: PropTypes.object.isRequired,
selectedAddress: PropTypes.string.isRequired,
warning: PropTypes.node,
}
render () {
const {dispatch, Qr, selectedAddress, warning} = this.props
const address = `${isHexPrefixed(Qr.data) ? 'ethereum:' : ''}${Qr.data}`
const qrImage = qrCode(4, 'M')
qrImage.addData(address)
qrImage.make()
return h('div.flex-column.full-width', {
style: {
alignItems: 'center',
boxSizing: 'border-box',
padding: '50px',
},
}, [
h('div.flex-row.full-width', {
style: {
alignItems: 'flex-start',
},
}, [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
onClick () {
dispatch(actions.backToAccountDetail(selectedAddress))
},
}),
]),
h('div.qr-header', Qr.message),
warning && h('span.error.flex-center', {
style: {
textAlign: 'center',
width: '229px',
height: '82px',
},
}, [
this.props.warning,
]),
h('div#qr-container.flex-column', {
style: {
marginTop: '25px',
marginBottom: '15px',
},
dangerouslySetInnerHTML: {
__html: qrImage.createTableTag(4),
},
}),
h('div.flex-row.full-width', [
h('h3.ellip-address.grow-tenx', Qr.data),
h(CopyButton, {
value: Qr.data,
}),
]),
])
}
}
function mapStateToProps (state) {
return {
Qr: state.appState.Qr,
buyView: state.appState.buyView,
warning: state.appState.warning,
}
}
module.exports = connect(mapStateToProps)(AccountQrScreen)

@ -1,119 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../../../ui/app/actions')
import Select from 'react-select'
// Subviews
const JsonImportView = require('./json.js')
const PrivateKeyImportView = require('./private-key.js')
const menuItems = [
'Private Key',
'JSON File',
]
module.exports = connect(mapStateToProps)(AccountImportSubview)
function mapStateToProps (state) {
return {
menuItems,
}
}
inherits(AccountImportSubview, Component)
function AccountImportSubview () {
Component.call(this)
}
AccountImportSubview.prototype.render = function () {
const props = this.props
const state = this.state || {}
const { menuItems } = props
const { type } = state
return (
h('div', [
h('.section-title.flex-row.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: (event) => {
props.dispatch(actions.goHome())
},
}),
h('h2.page-subtitle', 'Import Accounts'),
]),
h('.error', {
style: {
display: 'inline-block',
alignItems: 'center',
padding: '5px 15px 0px 15px',
},
}, [
h('span', 'Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts '),
h('span', {
style: {
color: 'rgba(247, 134, 28, 1)',
cursor: 'pointer',
textDecoration: 'underline',
},
onClick: () => {
global.platform.openWindow({
url: 'https://metamask.zendesk.com/hc/en-us/articles/360015289592-What-Are-Loose-Accounts-',
})
},
}, 'here.'),
]),
h('div', {
style: {
padding: '10px',
color: 'rgb(174, 174, 174)',
},
}, [
h('h3', { style: { padding: '3px' } }, 'SELECT TYPE'),
h('style', `
.has-value.Select--single > .Select-control .Select-value .Select-value-label, .Select-value-label {
color: rgb(174,174,174);
}
`),
h(Select, {
name: 'import-type-select',
clearable: false,
value: type || menuItems[0],
options: menuItems.map((type) => {
return {
value: type,
label: type,
}
}),
onChange: (opt) => {
props.dispatch(actions.showImportPage())
this.setState({ type: opt.value })
},
}),
]),
this.renderImportView(),
])
)
}
AccountImportSubview.prototype.renderImportView = function () {
const props = this.props
const state = this.state || {}
const { type } = state
const { menuItems } = props
const current = type || menuItems[0]
switch (current) {
case 'Private Key':
return h(PrivateKeyImportView)
case 'JSON File':
return h(JsonImportView)
default:
return h(JsonImportView)
}
}

@ -1,124 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../../../ui/app/actions')
const FileInput = require('react-simple-file-input').default
const PropTypes = require('prop-types')
const HELP_LINK = 'https://github.com/MetaMask/faq/blob/master/README.md#q-i-cant-use-the-import-feature-for-uploading-a-json-file-the-window-keeps-closing-when-i-try-to-select-a-file'
class JsonImportSubview extends Component {
constructor (props) {
super(props)
this.state = {
file: null,
fileContents: '',
}
}
render () {
const { error } = this.props
return (
h('div', {
style: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '5px 15px 0px 15px',
},
}, [
h('p', 'Used by a variety of different clients'),
h('a.warning', {
href: HELP_LINK,
target: '_blank',
}, 'File import not working? Click here!'),
h(FileInput, {
readAs: 'text',
onLoad: this.onLoad.bind(this),
style: {
margin: '20px 0px 12px 20px',
fontSize: '15px',
},
}),
h('input.large-input.letter-spacey', {
type: 'password',
placeholder: 'Enter password',
id: 'json-password-box',
onKeyPress: this.createKeyringOnEnter.bind(this),
style: {
width: 260,
marginTop: 12,
},
}),
h('button.primary', {
onClick: this.createNewKeychain.bind(this),
style: {
margin: 12,
},
}, 'Import'),
error ? h('span.error', error) : null,
])
)
}
onLoad (event, file) {
this.setState({file: file, fileContents: event.target.result})
}
createKeyringOnEnter (event) {
if (event.key === 'Enter') {
event.preventDefault()
this.createNewKeychain()
}
}
createNewKeychain () {
const { fileContents } = this.state
if (!fileContents) {
const message = 'You must select a file to import.'
return this.props.displayWarning(message)
}
const passwordInput = document.getElementById('json-password-box')
const password = passwordInput.value
if (!password) {
const message = 'You must enter a password for the selected file.'
return this.props.displayWarning(message)
}
this.props.importNewAccount([ fileContents, password ])
// JS runtime requires caught rejections but failures are handled by Redux
.catch()
}
}
JsonImportSubview.propTypes = {
error: PropTypes.string,
displayWarning: PropTypes.func,
importNewAccount: PropTypes.func,
}
const mapStateToProps = state => {
return {
error: state.appState.warning,
}
}
const mapDispatchToProps = dispatch => {
return {
goHome: () => dispatch(actions.goHome()),
displayWarning: warning => dispatch(actions.displayWarning(warning)),
importNewAccount: options => dispatch(actions.importNewAccount('JSON File', options)),
}
}
module.exports = connect(mapStateToProps, mapDispatchToProps)(JsonImportSubview)

@ -1,69 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../../../ui/app/actions')
module.exports = connect(mapStateToProps)(PrivateKeyImportView)
function mapStateToProps (state) {
return {
error: state.appState.warning,
}
}
inherits(PrivateKeyImportView, Component)
function PrivateKeyImportView () {
Component.call(this)
}
PrivateKeyImportView.prototype.render = function () {
const { error } = this.props
return (
h('div', {
style: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '5px 15px 0px 15px',
},
}, [
h('span', 'Paste your private key string here'),
h('input.large-input.letter-spacey', {
type: 'password',
id: 'private-key-box',
onKeyPress: this.createKeyringOnEnter.bind(this),
style: {
width: 260,
marginTop: 12,
},
}),
h('button.primary', {
onClick: this.createNewKeychain.bind(this),
style: {
margin: 12,
},
}, 'Import'),
error ? h('span.error', error) : null,
])
)
}
PrivateKeyImportView.prototype.createKeyringOnEnter = function (event) {
if (event.key === 'Enter') {
event.preventDefault()
this.createNewKeychain()
}
}
PrivateKeyImportView.prototype.createNewKeychain = function () {
const input = document.getElementById('private-key-box')
const privateKey = input.value
this.props.dispatch(actions.importNewAccount('Private Key', [ privateKey ]))
// JS runtime requires caught rejections but failures are handled by Redux
.catch()
}

@ -1,30 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
module.exports = connect(mapStateToProps)(SeedImportSubview)
function mapStateToProps (state) {
return {}
}
inherits(SeedImportSubview, Component)
function SeedImportSubview () {
Component.call(this)
}
SeedImportSubview.prototype.render = function () {
return (
h('div', {
style: {
},
}, [
`Paste your seed phrase here!`,
h('textarea'),
h('br'),
h('button', 'Submit'),
])
)
}

@ -1,202 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
const Tooltip = require('./components/tooltip.js')
const ethUtil = require('ethereumjs-util')
const Copyable = require('./components/copyable')
const addressSummary = require('./util').addressSummary
module.exports = connect(mapStateToProps)(AddSuggestedTokenScreen)
function mapStateToProps (state) {
return {
identities: state.metamask.identities,
suggestedTokens: state.metamask.suggestedTokens,
}
}
inherits(AddSuggestedTokenScreen, Component)
function AddSuggestedTokenScreen () {
this.state = {
warning: null,
}
Component.call(this)
}
AddSuggestedTokenScreen.prototype.render = function () {
const state = this.state
const props = this.props
const { warning } = state
const key = Object.keys(props.suggestedTokens)[0]
const { address, symbol, decimals } = props.suggestedTokens[key]
return (
h('.flex-column.flex-grow', [
// subtitle and nav
h('.section-title.flex-row.flex-center', [
h('h2.page-subtitle', 'Add Suggested Token'),
]),
h('.error', {
style: {
display: warning ? 'block' : 'none',
padding: '0 20px',
textAlign: 'center',
},
}, warning),
// conf view
h('.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-space-around', {
style: {
padding: '20px',
},
}, [
h('div', [
h(Tooltip, {
position: 'top',
title: 'The contract of the actual token contract. Click for more info.',
}, [
h('a', {
style: { fontWeight: 'bold', paddingRight: '10px'},
href: 'https://support.metamask.io/kb/article/24-what-is-a-token-contract-address',
target: '_blank',
}, [
h('span', 'Token Contract Address '),
h('i.fa.fa-question-circle'),
]),
]),
]),
h('div', {
style: { display: 'flex' },
}, [
h(Copyable, {
value: ethUtil.toChecksumAddress(address),
}, [
h('span#token-address', {
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
display: 'flex',
},
}, addressSummary(address, 24, 4, false)),
]),
]),
h('div', [
h('span', {
style: { fontWeight: 'bold', paddingRight: '10px'},
}, 'Token Symbol'),
]),
h('div', { style: {display: 'flex'} }, [
h('p#token_symbol', {
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
}, symbol),
]),
h('div', [
h('span', {
style: { fontWeight: 'bold', paddingRight: '10px'},
}, 'Decimals of Precision'),
]),
h('div', { style: {display: 'flex'} }, [
h('p#token_decimals', {
type: 'number',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
}, decimals),
]),
h('button', {
style: {
alignSelf: 'center',
margin: '8px',
},
onClick: (event) => {
this.props.dispatch(actions.removeSuggestedTokens())
},
}, 'Cancel'),
h('button', {
style: {
alignSelf: 'center',
margin: '8px',
},
onClick: (event) => {
const valid = this.validateInputs({ address, symbol, decimals })
if (!valid) return
this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals))
.then(() => {
this.props.dispatch(actions.removeSuggestedTokens())
})
},
}, 'Add'),
]),
]),
])
)
}
AddSuggestedTokenScreen.prototype.componentWillMount = function () {
if (typeof global.ethereumProvider === 'undefined') return
}
AddSuggestedTokenScreen.prototype.validateInputs = function (opts) {
let msg = ''
const identitiesList = Object.keys(this.props.identities)
const { address, symbol, decimals } = opts
const standardAddress = ethUtil.addHexPrefix(address).toLowerCase()
const validAddress = ethUtil.isValidAddress(address)
if (!validAddress) {
msg += 'Address is invalid.'
}
const validDecimals = decimals >= 0 && decimals <= 36
if (!validDecimals) {
msg += 'Decimals must be at least 0, and not over 36. '
}
const symbolLen = symbol.trim().length
const validSymbol = symbolLen > 0 && symbolLen < 10
if (!validSymbol) {
msg += 'Symbol must be between 0 and 10 characters.'
}
const ownAddress = identitiesList.includes(standardAddress)
if (ownAddress) {
msg = 'Personal address detected. Input the token contract address.'
}
const isValid = validAddress && validDecimals && !ownAddress
if (!isValid) {
this.setState({
warning: msg,
})
} else {
this.setState({ warning: null })
}
return isValid
}

@ -1,241 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
const Tooltip = require('./components/tooltip.js')
const ethUtil = require('ethereumjs-util')
const abi = require('human-standard-token-abi')
const Eth = require('ethjs-query')
const EthContract = require('ethjs-contract')
const emptyAddr = '0x0000000000000000000000000000000000000000'
module.exports = connect(mapStateToProps)(AddTokenScreen)
function mapStateToProps (state) {
return {
identities: state.metamask.identities,
}
}
inherits(AddTokenScreen, Component)
function AddTokenScreen () {
this.state = {
warning: null,
address: '',
symbol: 'TOKEN',
decimals: 18,
}
Component.call(this)
}
AddTokenScreen.prototype.render = function () {
const state = this.state
const props = this.props
const { warning, symbol, decimals } = state
return (
h('.flex-column.flex-grow', [
// subtitle and nav
h('.section-title.flex-row.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: (event) => {
props.dispatch(actions.goHome())
},
}),
h('h2.page-subtitle', 'Add Token'),
]),
h('.error', {
style: {
display: warning ? 'block' : 'none',
padding: '0 20px',
textAlign: 'center',
},
}, warning),
// conf view
h('.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-space-around', {
style: {
padding: '20px',
},
}, [
h('div', [
h(Tooltip, {
position: 'top',
title: 'The contract of the actual token contract. Click for more info.',
}, [
h('a', {
style: { fontWeight: 'bold', paddingRight: '10px'},
href: 'https://support.metamask.io/kb/article/24-what-is-a-token-contract-address',
target: '_blank',
}, [
h('span', 'Token Contract Address '),
h('i.fa.fa-question-circle'),
]),
]),
]),
h('section.flex-row.flex-center', [
h('input#token-address', {
name: 'address',
placeholder: 'Token Contract Address',
onChange: this.tokenAddressDidChange.bind(this),
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
}),
]),
h('div', [
h('span', {
style: { fontWeight: 'bold', paddingRight: '10px'},
}, 'Token Symbol'),
]),
h('div', { style: {display: 'flex'} }, [
h('input#token_symbol', {
placeholder: `Like "ETH"`,
value: symbol,
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onChange: (event) => {
var element = event.target
var symbol = element.value
this.setState({ symbol })
},
}),
]),
h('div', [
h('span', {
style: { fontWeight: 'bold', paddingRight: '10px'},
}, 'Decimals of Precision'),
]),
h('div', { style: {display: 'flex'} }, [
h('input#token_decimals', {
value: decimals,
type: 'number',
min: 0,
max: 36,
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onChange: (event) => {
var element = event.target
var decimals = element.value.trim()
this.setState({ decimals })
},
}),
]),
h('button', {
style: {
alignSelf: 'center',
},
onClick: (event) => {
const valid = this.validateInputs()
if (!valid) return
const { address, symbol, decimals } = this.state
this.props.dispatch(actions.addToken(address.trim(), symbol.trim(), decimals))
.then(() => {
this.props.dispatch(actions.goHome())
})
},
}, 'Add'),
]),
]),
])
)
}
AddTokenScreen.prototype.componentWillMount = function () {
if (typeof global.ethereumProvider === 'undefined') return
this.eth = new Eth(global.ethereumProvider)
this.contract = new EthContract(this.eth)
this.TokenContract = this.contract(abi)
}
AddTokenScreen.prototype.tokenAddressDidChange = function (event) {
const el = event.target
const address = el.value.trim()
if (ethUtil.isValidAddress(address) && address !== emptyAddr) {
this.setState({ address })
this.attemptToAutoFillTokenParams(address)
}
}
AddTokenScreen.prototype.validateInputs = function () {
let msg = ''
const state = this.state
const identitiesList = Object.keys(this.props.identities)
const { address, symbol, decimals } = state
const standardAddress = ethUtil.addHexPrefix(address).toLowerCase()
const validAddress = ethUtil.isValidAddress(address)
if (!validAddress) {
msg += 'Address is invalid.'
}
const validDecimals = decimals >= 0 && decimals <= 36
if (!validDecimals) {
msg += 'Decimals must be at least 0, and not over 36. '
}
const symbolLen = symbol.trim().length
const validSymbol = symbolLen > 0 && symbolLen < 10
if (!validSymbol) {
msg += 'Symbol must be between 0 and 10 characters.'
}
const ownAddress = identitiesList.includes(standardAddress)
if (ownAddress) {
msg = 'Personal address detected. Input the token contract address.'
}
const isValid = validAddress && validDecimals && !ownAddress
if (!isValid) {
this.setState({
warning: msg,
})
} else {
this.setState({ warning: null })
}
return isValid
}
AddTokenScreen.prototype.attemptToAutoFillTokenParams = async function (address) {
const contract = this.TokenContract.at(address)
const results = await Promise.all([
contract.symbol(),
contract.decimals(),
])
const [ symbol, decimals ] = results
if (symbol && decimals) {
console.log('SETTING SYMBOL AND DECIMALS', { symbol, decimals })
this.setState({ symbol: symbol[0], decimals: decimals[0].toString() })
}
}

@ -1,315 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../ui/app/actions')
const log = require('loglevel')
// mascara
const FirstTime = require('../../ui/app/components/pages/first-time-flow').default
// init
const InitializeMenuScreen = require('./first-time/init-menu')
const NewKeyChainScreen = require('./new-keychain')
// unlock
const UnlockScreen = require('./unlock')
// accounts
const AccountDetailScreen = require('./account-detail')
const AccountQrScreen = require('./account-qr')
const SendTransactionScreen = require('./send')
const ConfirmTxScreen = require('./conf-tx')
// notice
const NoticeScreen = require('./components/notice')
const generateLostAccountsNotice = require('../lib/lost-accounts-notice')
// other views
const ConfigScreen = require('./config')
const AddTokenScreen = require('./add-token')
const AddSuggestedTokenScreen = require('./add-suggested-token')
const Import = require('./accounts/import')
const InfoScreen = require('./info')
const NewUiAnnouncement = require('./new-ui-annoucement')
const AppBar = require('./components/app-bar')
const Loading = require('./components/loading')
const BuyView = require('./components/buy-button-subview')
const HDCreateVaultComplete = require('./keychains/hd/create-vault-complete')
const HDRestoreVaultScreen = require('./keychains/hd/restore-vault')
const RevealSeedConfirmation = require('./keychains/hd/recover-seed/confirmation')
const ProviderApproval = require('./provider-approval')
module.exports = connect(mapStateToProps)(App)
inherits(App, Component)
function App () { Component.call(this) }
function mapStateToProps (state) {
const {
identities,
accounts,
address,
keyrings,
isInitialized,
noActiveNotices,
seedWords,
featureFlags,
providerRequests,
} = state.metamask
const selected = address || Object.keys(accounts)[0]
return {
// state from plugin
isLoading: state.appState.isLoading,
loadingMessage: state.appState.loadingMessage,
noActiveNotices: state.metamask.noActiveNotices,
isInitialized: state.metamask.isInitialized,
isUnlocked: state.metamask.isUnlocked,
currentView: state.appState.currentView,
selectedAddress: state.metamask.selectedAddress,
transForward: state.appState.transForward,
isMascara: state.metamask.isMascara,
isOnboarding: Boolean(!noActiveNotices || seedWords || !isInitialized),
seedWords: state.metamask.seedWords,
unapprovedTxs: state.metamask.unapprovedTxs,
unapprovedMsgs: state.metamask.unapprovedMsgs,
menuOpen: state.appState.menuOpen,
network: state.metamask.network,
provider: state.metamask.provider,
forgottenPassword: state.appState.forgottenPassword,
nextUnreadNotice: state.metamask.nextUnreadNotice,
lostAccounts: state.metamask.lostAccounts,
frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
featureFlags,
providerRequests,
suggestedTokens: state.metamask.suggestedTokens,
// state needed to get account dropdown temporarily rendering from app bar
identities,
selected,
keyrings,
}
}
App.prototype.render = function () {
const {
currentView,
dispatch,
isLoading,
loadingMessage,
transForward,
network,
featureFlags,
} = this.props
const isLoadingNetwork = network === 'loading' && currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork
? `Connecting to ${this.getNetworkName()}`
: null
log.debug('Main ui render function')
if (!featureFlags.skipAnnounceBetaUI) {
return (
h(NewUiAnnouncement, {
dispatch,
})
)
}
return (
h('.flex-column.full-height', {
style: {
// Windows was showing a vertical scroll bar:
overflow: 'hidden',
position: 'relative',
alignItems: 'center',
},
}, [
h(AppBar, {
...this.props,
}),
this.renderLoadingIndicator({ isLoading, isLoadingNetwork, loadMessage }),
// panel content
h('.app-primary' + (transForward ? '.from-right' : '.from-left'), {
style: {
width: '100%',
},
}, [
this.renderPrimary(),
]),
])
)
}
App.prototype.renderLoadingIndicator = function ({ isLoading, isLoadingNetwork, loadMessage }) {
const { isMascara } = this.props
return isMascara
? null
: h(Loading, {
isLoading: isLoading || isLoadingNetwork,
loadingMessage: loadMessage,
})
}
App.prototype.renderPrimary = function () {
log.debug('rendering primary')
var props = this.props
const {isMascara, isOnboarding, providerRequests} = props
if (isMascara && isOnboarding) {
return h(FirstTime)
}
// notices
if (!props.noActiveNotices) {
log.debug('rendering notice screen for unread notices.')
return h('div', {
style: { width: '100%' },
}, [
h(NoticeScreen, {
notice: props.nextUnreadNotice,
key: 'NoticeScreen',
onConfirm: () => props.dispatch(actions.markNoticeRead(props.nextUnreadNotice)),
}),
])
} else if (props.lostAccounts && props.lostAccounts.length > 0) {
log.debug('rendering notice screen for lost accounts view.')
return h(NoticeScreen, {
notice: generateLostAccountsNotice(props.lostAccounts),
key: 'LostAccountsNotice',
onConfirm: () => props.dispatch(actions.markAccountsFound()),
})
}
// show initialize screen
if (!props.isInitialized || props.forgottenPassword) {
// show current view
log.debug('rendering an initialize screen')
switch (props.currentView.name) {
case 'restoreVault':
log.debug('rendering restore vault screen')
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
default:
log.debug('rendering menu screen')
return h(InitializeMenuScreen, {key: 'menuScreenInit'})
}
}
// show unlock screen
if (!props.isUnlocked) {
switch (props.currentView.name) {
case 'restoreVault':
log.debug('rendering restore vault screen')
return h(HDRestoreVaultScreen, {key: 'HDRestoreVaultScreen'})
case 'config':
log.debug('rendering config screen from unlock screen.')
return h(ConfigScreen, {key: 'config'})
default:
log.debug('rendering locked screen')
return h(UnlockScreen, {key: 'locked'})
}
}
// show seed words screen
if (props.seedWords) {
log.debug('rendering seed words')
return h(HDCreateVaultComplete, {key: 'HDCreateVaultComplete'})
}
if (providerRequests && providerRequests.length > 0) {
log.debug('rendering provider API approval screen')
return h(ProviderApproval, { origin: providerRequests[0].origin, tabID: providerRequests[0].tabID })
}
// show current view
switch (props.currentView.name) {
case 'accountDetail':
log.debug('rendering account detail screen')
return h(AccountDetailScreen, {key: 'account-detail'})
case 'sendTransaction':
log.debug('rendering send tx screen')
return h(SendTransactionScreen, {key: 'send-transaction'})
case 'newKeychain':
log.debug('rendering new keychain screen')
return h(NewKeyChainScreen, {key: 'new-keychain'})
case 'confTx':
log.debug('rendering confirm tx screen')
return h(ConfirmTxScreen, {key: 'confirm-tx'})
case 'add-token':
log.debug('rendering add-token screen from unlock screen.')
return h(AddTokenScreen, {key: 'add-token'})
case 'add-suggested-token':
log.debug('rendering add-suggested-token screen from unlock screen.')
return h(AddSuggestedTokenScreen, {key: 'add-suggested-token'})
case 'config':
log.debug('rendering config screen')
return h(ConfigScreen, {key: 'config'})
case 'import-menu':
log.debug('rendering import screen')
return h(Import, {key: 'import-menu'})
case 'reveal-seed-conf':
log.debug('rendering reveal seed confirmation screen')
return h(RevealSeedConfirmation, {key: 'reveal-seed-conf'})
case 'info':
log.debug('rendering info screen')
return h(InfoScreen, {key: 'info'})
case 'buyEth':
log.debug('rendering buy ether screen')
return h(BuyView, {key: 'buyEthView'})
case 'qr':
log.debug('rendering show qr screen')
return h(AccountQrScreen, {
key: 'account-qr',
selectedAddress: props.selectedAddress,
})
default:
log.debug('rendering default, account detail screen')
return h(AccountDetailScreen, {key: 'account-detail'})
}
}
App.prototype.toggleMetamaskActive = function () {
if (!this.props.isUnlocked) {
// currently inactive: redirect to password box
var passwordBox = document.querySelector('input[type=password]')
if (!passwordBox) return
passwordBox.focus()
} else {
// currently active: deactivate
this.props.dispatch(actions.lockMetamask(false))
}
}
App.prototype.getNetworkName = function () {
const { provider } = this.props
const providerName = provider.type
let name
if (providerName === 'mainnet') {
name = 'Main Ethereum Network'
} else if (providerName === 'ropsten') {
name = 'Ropsten Test Network'
} else if (providerName === 'kovan') {
name = 'Kovan Test Network'
} else if (providerName === 'rinkeby') {
name = 'Rinkeby Test Network'
} else {
name = 'Unknown Private Network'
}
return name
}

@ -1,321 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const actions = require('../../../ui/app/actions')
const genAccountLink = require('etherscan-link').createAccountLink
const connect = require('react-redux').connect
const Dropdown = require('./dropdown').Dropdown
const DropdownMenuItem = require('./dropdown').DropdownMenuItem
const Identicon = require('./identicon')
const ethUtil = require('ethereumjs-util')
const copyToClipboard = require('copy-to-clipboard')
class AccountDropdowns extends Component {
constructor (props) {
super(props)
this.state = {
accountSelectorActive: false,
optionsMenuActive: false,
}
this.accountSelectorToggleClassName = 'accounts-selector'
this.optionsMenuToggleClassName = 'fa-ellipsis-h'
}
renderAccounts () {
const { identities, selected, keyrings } = this.props
const accountOrder = keyrings.reduce((list, keyring) => list.concat(keyring.accounts), [])
return accountOrder.map((address, index) => {
const identity = identities[address]
const isSelected = identity.address === selected
const simpleAddress = identity.address.substring(2).toLowerCase()
const keyring = keyrings.find((kr) => {
return kr.accounts.includes(simpleAddress) ||
kr.accounts.includes(identity.address)
})
return h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
this.props.actions.showAccountDetail(identity.address)
},
style: {
marginTop: index === 0 ? '5px' : '',
fontSize: '24px',
},
},
[
h(
Identicon,
{
address: identity.address,
diameter: 32,
style: {
marginLeft: '10px',
},
},
),
this.indicateIfLoose(keyring),
h('span', {
style: {
marginLeft: '20px',
fontSize: '24px',
maxWidth: '145px',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
}, identity.name || ''),
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, isSelected ? h('.check', '✓') : null),
]
)
})
}
indicateIfLoose (keyring) {
try { // Sometimes keyrings aren't loaded yet:
const type = keyring.type
const isLoose = type !== 'HD Key Tree'
return isLoose ? h('.keyring-label', 'IMPORTED') : null
} catch (e) { return }
}
renderAccountSelector () {
const { actions } = this.props
const { accountSelectorActive } = this.state
return h(
Dropdown,
{
useCssTransition: true, // Hardcoded because account selector is temporarily in app-header
style: {
marginLeft: '-238px',
marginTop: '38px',
minWidth: '180px',
overflowY: 'auto',
maxHeight: '300px',
width: '300px',
},
innerStyle: {
padding: '8px 25px',
},
isOpen: accountSelectorActive,
onClickOutside: (event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.accountSelectorToggleClassName)
if (accountSelectorActive && isNotToggleElement) {
this.setState({ accountSelectorActive: false })
}
},
},
[
...this.renderAccounts(),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => actions.addNewAccount(),
},
[
h(
Identicon,
{
style: {
marginLeft: '10px',
},
diameter: 32,
},
),
h('span', { style: { marginLeft: '20px', fontSize: '24px' } }, 'Create Account'),
],
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => actions.showImportPage(),
},
[
h(
Identicon,
{
style: {
marginLeft: '10px',
},
diameter: 32,
},
),
h('span', {
style: {
marginLeft: '20px',
fontSize: '24px',
marginBottom: '5px',
},
}, 'Import Account'),
]
),
]
)
}
renderAccountOptions () {
const { actions } = this.props
const { optionsMenuActive } = this.state
return h(
Dropdown,
{
style: {
marginLeft: '-215px',
minWidth: '180px',
},
isOpen: optionsMenuActive,
onClickOutside: (event) => {
const { classList } = event.target
const isNotToggleElement = !classList.contains(this.optionsMenuToggleClassName)
if (optionsMenuActive && isNotToggleElement) {
this.setState({ optionsMenuActive: false })
}
},
},
[
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected, network } = this.props
const url = genAccountLink(selected, network)
global.platform.openWindow({ url })
},
},
'View account on Etherscan',
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected, identities } = this.props
var identity = identities[selected]
actions.showQrView(selected, identity ? identity.name : '')
},
},
'Show QR Code',
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
const { selected } = this.props
const checkSumAddress = selected && ethUtil.toChecksumAddress(selected)
copyToClipboard(checkSumAddress)
},
},
'Copy Address to clipboard',
),
h(
DropdownMenuItem,
{
closeMenu: () => {},
onClick: () => {
actions.requestAccountExport()
},
},
'Export Private Key',
),
]
)
}
render () {
const { style, enableAccountsSelector, enableAccountOptions } = this.props
const { optionsMenuActive, accountSelectorActive } = this.state
return h(
'span',
{
style: style,
},
[
enableAccountsSelector && h(
// 'i.fa.fa-angle-down',
'div.cursor-pointer.color-orange.accounts-selector',
{
style: {
// fontSize: '1.8em',
background: 'url(images/switch_acc.svg) white center center no-repeat',
height: '25px',
width: '25px',
transform: 'scale(0.75)',
marginRight: '3px',
},
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: !accountSelectorActive,
optionsMenuActive: false,
})
},
},
this.renderAccountSelector(),
),
enableAccountOptions && h(
'i.fa.fa-ellipsis-h',
{
style: {
margin: '0.5em',
fontSize: '1.8em',
},
onClick: (event) => {
event.stopPropagation()
this.setState({
accountSelectorActive: false,
optionsMenuActive: !optionsMenuActive,
})
},
},
this.renderAccountOptions()
),
]
)
}
}
AccountDropdowns.defaultProps = {
enableAccountsSelector: false,
enableAccountOptions: false,
}
AccountDropdowns.propTypes = {
identities: PropTypes.objectOf(PropTypes.object),
selected: PropTypes.string,
keyrings: PropTypes.array,
actions: PropTypes.objectOf(PropTypes.func),
network: PropTypes.string,
style: PropTypes.object,
enableAccountOptions: PropTypes.bool,
enableAccountsSelector: PropTypes.bool,
}
const mapDispatchToProps = (dispatch) => {
return {
actions: {
showConfigPage: () => dispatch(actions.showConfigPage()),
requestAccountExport: () => dispatch(actions.requestExportAccount()),
showAccountDetail: (address) => dispatch(actions.showAccountDetail(address)),
addNewAccount: () => dispatch(actions.addNewAccount()),
showImportPage: () => dispatch(actions.showImportPage()),
showQrView: (selected, identity) => dispatch(actions.showQrView(selected, identity)),
},
}
}
module.exports = {
AccountDropdowns: connect(null, mapDispatchToProps)(AccountDropdowns),
}

@ -1,132 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const exportAsFile = require('../util').exportAsFile
const copyToClipboard = require('copy-to-clipboard')
const actions = require('../../../ui/app/actions')
const ethUtil = require('ethereumjs-util')
const connect = require('react-redux').connect
module.exports = connect(mapStateToProps)(ExportAccountView)
inherits(ExportAccountView, Component)
function ExportAccountView () {
Component.call(this)
}
function mapStateToProps (state) {
return {
warning: state.appState.warning,
}
}
ExportAccountView.prototype.render = function () {
const state = this.props
const accountDetail = state.accountDetail
const nickname = state.identities[state.address].name
if (!accountDetail) return h('div')
const accountExport = accountDetail.accountExport
const notExporting = accountExport === 'none'
const exportRequested = accountExport === 'requested'
const accountExported = accountExport === 'completed'
if (notExporting) return h('div')
if (exportRequested) {
const warning = `Export private keys at your own risk.`
return (
h('div', {
style: {
display: 'inline-block',
textAlign: 'center',
},
},
[
h('div', {
key: 'exporting',
style: {
margin: '0 20px',
},
}, [
h('p.error', warning),
h('input#exportAccount.sizing-input', {
type: 'password',
placeholder: 'confirm password',
onKeyPress: this.onExportKeyPress.bind(this),
style: {
position: 'relative',
top: '1.5px',
marginBottom: '7px',
},
}),
]),
h('div', {
key: 'buttons',
style: {
margin: '0 20px',
},
},
[
h('button', {
onClick: () => this.onExportKeyPress({ key: 'Enter', preventDefault: () => {} }),
style: {
marginRight: '10px',
},
}, 'Submit'),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
}, 'Cancel'),
]),
(this.props.warning) && (
h('span.error', {
style: {
margin: '20px',
},
}, this.props.warning.split('-'))
),
])
)
}
if (accountExported) {
const plainKey = ethUtil.stripHexPrefix(accountDetail.privateKey)
return h('div.privateKey', {
style: {
margin: '0 20px',
},
}, [
h('label', 'Your private key (click to copy):'),
h('p.error.cursor-pointer', {
style: {
textOverflow: 'ellipsis',
overflow: 'hidden',
webkitUserSelect: 'text',
maxWidth: '275px',
},
onClick: function (event) {
copyToClipboard(ethUtil.stripHexPrefix(accountDetail.privateKey))
},
}, plainKey),
h('button', {
onClick: () => this.props.dispatch(actions.backToAccountDetail(this.props.address)),
}, 'Done'),
h('button', {
style: {
marginLeft: '10px',
},
onClick: () => exportAsFile(`MetaMask ${nickname} Private Key`, plainKey),
}, 'Save as File'),
])
}
}
ExportAccountView.prototype.onExportKeyPress = function (event) {
if (event.key !== 'Enter') return
event.preventDefault()
const input = document.getElementById('exportAccount').value
this.props.dispatch(actions.exportAccount(input, this.props.address))
}

@ -1,86 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const Identicon = require('./identicon')
const formatBalance = require('../util').formatBalance
const addressSummary = require('../util').addressSummary
module.exports = AccountPanel
inherits(AccountPanel, Component)
function AccountPanel () {
Component.call(this)
}
AccountPanel.prototype.render = function () {
var state = this.props
var identity = state.identity || {}
var account = state.account || {}
var isFauceting = state.isFauceting
var panelState = {
key: `accountPanel${identity.address}`,
identiconKey: identity.address,
identiconLabel: identity.name || '',
attributes: [
{
key: 'ADDRESS',
value: addressSummary(identity.address),
},
balanceOrFaucetingIndication(account, isFauceting),
],
}
return (
h('.identity-panel.flex-row.flex-space-between', {
style: {
flex: '1 0 auto',
cursor: panelState.onClick ? 'pointer' : undefined,
},
onClick: panelState.onClick,
}, [
// account identicon
h('.identicon-wrapper.flex-column.select-none', [
h(Identicon, {
address: panelState.identiconKey,
imageify: state.imageifyIdenticons,
}),
h('span.font-small', panelState.identiconLabel.substring(0, 7) + '...'),
]),
// account address, balance
h('.identity-data.flex-column.flex-justify-center.flex-grow.select-none', [
panelState.attributes.map((attr) => {
return h('.flex-row.flex-space-between', {
key: '' + Math.round(Math.random() * 1000000),
}, [
h('label.font-small.no-select', attr.key),
h('span.font-small', attr.value),
])
}),
]),
])
)
}
function balanceOrFaucetingIndication (account, isFauceting) {
// Temporarily deactivating isFauceting indication
// because it shows fauceting for empty restored accounts.
if (/* isFauceting*/ false) {
return {
key: 'Account is auto-funding.',
value: 'Please wait.',
}
} else {
return {
key: 'BALANCE',
value: formatBalance(account.balance),
}
}
}

@ -1,436 +0,0 @@
const PropTypes = require('prop-types')
const {Component} = require('react')
const h = require('react-hyperscript')
const actions = require('../../../ui/app/actions')
const SandwichExpando = require('sandwich-expando')
const {Dropdown} = require('./dropdown')
const {DropdownMenuItem} = require('./dropdown')
const NetworkIndicator = require('./network')
const {AccountDropdowns} = require('./account-dropdowns')
const LOCALHOST_RPC_URL = 'http://localhost:8545'
module.exports = class AppBar extends Component {
static defaultProps = {
selectedAddress: undefined,
}
static propTypes = {
dispatch: PropTypes.func.isRequired,
frequentRpcListDetail: PropTypes.array.isRequired,
isMascara: PropTypes.bool.isRequired,
isOnboarding: PropTypes.bool.isRequired,
identities: PropTypes.any.isRequired,
selectedAddress: PropTypes.string,
isUnlocked: PropTypes.bool.isRequired,
network: PropTypes.any.isRequired,
keyrings: PropTypes.any.isRequired,
provider: PropTypes.any.isRequired,
}
static renderSpace () {
return (
h('span', {
dangerouslySetInnerHTML: {
__html: '&nbsp;',
},
})
)
}
state = {
isNetworkMenuOpen: false,
}
renderAppBar () {
if (window.METAMASK_UI_TYPE === 'notification') {
return null
}
const props = this.props
const {isMascara, isOnboarding} = props
// Do not render header if user is in mascara onboarding
if (isMascara && isOnboarding) {
return null
}
// Do not render header if user is in mascara buy ether
if (isMascara && props.currentView.name === 'buyEth') {
return null
}
return (
h('div.app-bar', [
this.renderAppBarNewUiNotice(),
this.renderAppBarAppHeader(),
])
)
}
renderAppBarNewUiNotice () {
const {dispatch} = this.props
return (
h('div.app-bar__new-ui-banner', {
style: {
height: '28px',
zIndex: 12,
},
}, [
'Try the New MetaMask',
AppBar.renderSpace(),
h('span.banner__link', {
async onClick () {
await dispatch(actions.setFeatureFlag('betaUI', true))
global.platform.openExtensionInBrowser()
},
}, [
'Now',
]),
AppBar.renderSpace(),
'or',
AppBar.renderSpace(),
h('span.banner__link', {
onClick () {
global.platform.openWindow({
url: 'https://medium.com/metamask/74dba32cc7f7',
})
},
}, [
'Learn More',
]),
])
)
}
renderAppBarAppHeader () {
const {
identities,
selectedAddress,
isUnlocked,
network,
keyrings,
provider,
} = this.props
const {
isNetworkMenuOpen,
isMainMenuOpen,
} = this.state
return (
h('.full-width', {
style: {
display: 'flex',
flexDirection: 'column',
height: '38px',
},
}, [
h('.app-header.flex-row.flex-space-between', {
style: {
alignItems: 'center',
visibility: isUnlocked ? 'visible' : 'none',
background: isUnlocked ? 'white' : 'none',
height: '38px',
position: 'relative',
zIndex: 12,
},
}, [
h('div.left-menu-section', {
style: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
}, [
// mini logo
h('img', {
height: 24,
width: 24,
src: './images/icon-128.png',
}),
h(NetworkIndicator, {
network: network,
provider: provider,
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
this.setState({ isNetworkMenuOpen: !isNetworkMenuOpen })
},
}),
]),
isUnlocked && h('div', {
style: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
},
}, [
h(AccountDropdowns, {
style: {},
enableAccountsSelector: true,
identities: identities,
selected: selectedAddress,
network,
keyrings,
}, []),
h(SandwichExpando, {
className: 'sandwich-expando',
width: 16,
barHeight: 2,
padding: 0,
isOpen: isMainMenuOpen,
color: 'rgb(247,146,30)',
onClick: () => {
this.setState({
isMainMenuOpen: !isMainMenuOpen,
})
},
}),
]),
]),
])
)
}
renderNetworkDropdown () {
const {
dispatch,
frequentRpcListDetail: rpcList,
provider,
} = this.props
const {
type: providerType,
rpcTarget: activeNetwork,
} = provider
const isOpen = this.state.isNetworkMenuOpen
return h(Dropdown, {
useCssTransition: true,
isOpen,
onClickOutside: (event) => {
const { classList } = event.target
const isNotToggleElement = [
classList.contains('menu-icon'),
classList.contains('network-name'),
classList.contains('network-indicator'),
].filter(bool => bool).length === 0
// classes from three constituent nodes of the toggle element
if (isNotToggleElement) {
this.setState({ isNetworkMenuOpen: false })
}
},
zIndex: 11,
style: {
position: 'absolute',
left: '2px',
top: '64px',
},
innerStyle: {
padding: '2px 16px 2px 0px',
},
}, [
h(DropdownMenuItem, {
key: 'main',
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
onClick: () => dispatch(actions.setProviderType('mainnet')),
style: {
fontSize: '18px',
},
}, [
h('.menu-icon.diamond'),
'Main Ethereum Network',
providerType === 'mainnet'
? h('.check', '✓')
: null,
]),
h(DropdownMenuItem, {
key: 'ropsten',
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
onClick: () => dispatch(actions.setProviderType('ropsten')),
style: {
fontSize: '18px',
},
}, [
h('.menu-icon.red-dot'),
'Ropsten Test Network',
providerType === 'ropsten'
? h('.check', '✓')
: null,
]),
h(DropdownMenuItem, {
key: 'kovan',
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
onClick: () => dispatch(actions.setProviderType('kovan')),
style: {
fontSize: '18px',
},
}, [
h('.menu-icon.hollow-diamond'),
'Kovan Test Network',
providerType === 'kovan'
? h('.check', '✓')
: null,
]),
h(DropdownMenuItem, {
key: 'rinkeby',
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
onClick: () => dispatch(actions.setProviderType('rinkeby')),
style: {
fontSize: '18px',
},
}, [
h('.menu-icon.golden-square'),
'Rinkeby Test Network',
providerType === 'rinkeby'
? h('.check', '✓')
: null,
]),
h(DropdownMenuItem, {
key: 'default',
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
onClick: () => dispatch(actions.setProviderType('localhost')),
style: {
fontSize: '18px',
},
}, [
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
'Localhost 8545',
activeNetwork === LOCALHOST_RPC_URL
? h('.check', '✓')
: null,
]),
this.renderCustomOption(provider),
this.renderCommonRpc(rpcList, provider),
h(DropdownMenuItem, {
closeMenu: () => this.setState({ isNetworkMenuOpen: !isOpen }),
onClick: () => dispatch(actions.showConfigPage()),
style: {
fontSize: '18px',
},
}, [
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
'Custom RPC',
activeNetwork === 'custom'
? h('.check', '✓')
: null,
]),
])
}
renderCustomOption ({ rpcTarget, type, ticker }) {
const {dispatch, network} = this.props
if (type !== 'rpc') {
return null
}
// Concatenate long URLs
let label = rpcTarget
if (rpcTarget.length > 31) {
label = label.substr(0, 34) + '...'
}
switch (rpcTarget) {
case LOCALHOST_RPC_URL:
return null
default:
return h(DropdownMenuItem, {
key: rpcTarget,
onClick: () => dispatch(actions.setRpcTarget(rpcTarget, network, ticker)),
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
}, [
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
label,
h('.check', '✓'),
])
}
}
renderCommonRpc (rpcList, provider) {
const {dispatch} = this.props
const reversedRpcList = rpcList.slice().reverse()
return reversedRpcList.map((entry) => {
const rpc = entry.rpcUrl
const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget
if ((rpc === LOCALHOST_RPC_URL) || currentRpcTarget) {
return null
} else {
return h(DropdownMenuItem, {
key: `common${rpc}`,
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
onClick: () => dispatch(actions.setRpcTarget(rpc, entry.chainId, entry.ticker)),
}, [
h('i.fa.fa-question-circle.fa-lg.menu-icon'),
rpc,
currentRpcTarget
? h('.check', '✓')
: null,
])
}
})
}
renderDropdown () {
const {dispatch} = this.props
const isOpen = this.state.isMainMenuOpen
return h(Dropdown, {
useCssTransition: true,
isOpen: isOpen,
zIndex: 11,
onClickOutside: (event) => {
const classList = event.target.classList
const parentClassList = event.target.parentElement.classList
const isToggleElement = classList.contains('sandwich-expando') ||
parentClassList.contains('sandwich-expando')
if (isOpen && !isToggleElement) {
this.setState({ isMainMenuOpen: false })
}
},
style: {
position: 'absolute',
right: '2px',
top: '66px',
},
innerStyle: {},
}, [
h(DropdownMenuItem, {
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
onClick: () => { dispatch(actions.showConfigPage()) },
}, 'Settings'),
h(DropdownMenuItem, {
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
onClick: () => { dispatch(actions.lockMetamask()) },
}, 'Log Out'),
h(DropdownMenuItem, {
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
onClick: () => { dispatch(actions.showInfoPage()) },
}, 'Info/Help'),
h(DropdownMenuItem, {
closeMenu: () => this.setState({ isMainMenuOpen: !isOpen }),
onClick: () => {
dispatch(actions.setFeatureFlag('betaUI', true, 'BETA_UI_NOTIFICATION_MODAL'))
},
}, 'Try New UI!'),
])
}
render () {
return h('div.full-width', [
this.renderAppBar(),
this.renderNetworkDropdown(),
this.renderDropdown(),
])
}
}

@ -1,96 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const inherits = require('util').inherits
const formatBalance = require('../util').formatBalance
const generateBalanceObject = require('../util').generateBalanceObject
const Tooltip = require('./tooltip.js')
const FiatValue = require('./fiat-value.js')
module.exports = connect(mapStateToProps)(EthBalanceComponent)
function mapStateToProps (state) {
return {
ticker: state.metamask.ticker,
}
}
inherits(EthBalanceComponent, Component)
function EthBalanceComponent () {
Component.call(this)
}
EthBalanceComponent.prototype.render = function () {
var props = this.props
let { value } = props
const { ticker } = props
var style = props.style
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
value = value ? formatBalance(value, 6, needsParse, ticker) : '...'
var width = props.width
return (
h('.ether-balance.ether-balance-amount', {
style: style,
}, [
h('div', {
style: {
display: 'inline',
width: width,
},
}, this.renderBalance(value)),
])
)
}
EthBalanceComponent.prototype.renderBalance = function (value) {
var props = this.props
if (value === 'None') return value
if (value === '...') return value
var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3)
var balance
var splitBalance = value.split(' ')
var ethNumber = splitBalance[0]
var ethSuffix = splitBalance[1]
const showFiat = 'showFiat' in props ? props.showFiat : true
if (props.shorten) {
balance = balanceObj.shortBalance
} else {
balance = balanceObj.balance
}
var label = balanceObj.label
return (
h(Tooltip, {
position: 'bottom',
title: `${ethNumber} ${ethSuffix}`,
}, h('div.flex-column', [
h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
},
}, [
h('div', {
style: {
width: '100%',
textAlign: 'right',
},
}, this.props.incoming ? `+${balance}` : balance),
h('div', {
style: {
color: ' #AEAEAE',
fontSize: '12px',
marginLeft: '5px',
},
}, label),
]),
showFiat ? h(FiatValue, { value: props.value }) : null,
]))
)
}

@ -1,46 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const extend = require('xtend')
module.exports = BinaryRenderer
inherits(BinaryRenderer, Component)
function BinaryRenderer () {
Component.call(this)
}
BinaryRenderer.prototype.render = function () {
const props = this.props
const { value, style } = props
const text = this.hexToText(value)
const defaultStyle = extend({
width: '315px',
maxHeight: '210px',
resize: 'none',
border: 'none',
background: 'white',
padding: '3px',
}, style)
return (
h('textarea.font-small', {
readOnly: true,
style: defaultStyle,
defaultValue: text,
})
)
}
BinaryRenderer.prototype.hexToText = function (hex) {
try {
const stripped = ethUtil.stripHexPrefix(hex)
const buff = Buffer.from(stripped, 'hex')
return buff.toString('utf8')
} catch (e) {
return hex
}
}

@ -1,181 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
module.exports = BnAsDecimalInput
inherits(BnAsDecimalInput, Component)
function BnAsDecimalInput () {
this.state = { invalid: null }
Component.call(this)
}
/* Bn as Decimal Input
*
* A component for allowing easy, decimal editing
* of a passed in bn string value.
*
* On change, calls back its `onChange` function parameter
* and passes it an updated bn string.
*/
BnAsDecimalInput.prototype.render = function () {
const props = this.props
const state = this.state
const { value, scale, precision, onChange, min, max } = props
const suffix = props.suffix
const style = props.style
const valueString = value.toString(10)
const newMin = min && this.downsize(min.toString(10), scale)
const newMax = max && this.downsize(max.toString(10), scale)
const newValue = this.downsize(valueString, scale)
return (
h('.flex-column', [
h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
},
}, [
h('input.hex-input', {
type: 'number',
step: 'any',
required: true,
min: newMin,
max: newMax,
style: extend({
display: 'block',
textAlign: 'right',
backgroundColor: 'transparent',
border: '1px solid #bdbdbd',
}, style),
value: newValue,
onBlur: (event) => {
this.updateValidity(event)
},
onChange: (event) => {
this.updateValidity(event)
const value = (event.target.value === '') ? '' : event.target.value
const scaledNumber = this.upsize(value, scale, precision)
const precisionBN = new BN(scaledNumber, 10)
onChange(precisionBN, event.target.checkValidity())
},
onInvalid: (event) => {
const msg = this.constructWarning()
if (msg === state.invalid) {
return
}
this.setState({ invalid: msg })
event.preventDefault()
return false
},
}),
h('div', {
style: {
color: ' #AEAEAE',
fontSize: '12px',
marginLeft: '5px',
marginRight: '6px',
width: '20px',
},
}, suffix),
]),
state.invalid ? h('span.error', {
style: {
position: 'absolute',
right: '0px',
textAlign: 'right',
transform: 'translateY(26px)',
padding: '3px',
background: 'rgba(255,255,255,0.85)',
zIndex: '1',
textTransform: 'capitalize',
border: '2px solid #E20202',
},
}, state.invalid) : null,
])
)
}
BnAsDecimalInput.prototype.setValid = function (message) {
this.setState({ invalid: null })
}
BnAsDecimalInput.prototype.updateValidity = function (event) {
const target = event.target
const value = this.props.value
const newValue = target.value
if (value === newValue) {
return
}
const valid = target.checkValidity()
if (valid) {
this.setState({ invalid: null })
}
}
BnAsDecimalInput.prototype.constructWarning = function () {
const { name, min, max, scale, suffix } = this.props
const newMin = min && this.downsize(min.toString(10), scale)
const newMax = max && this.downsize(max.toString(10), scale)
let message = name ? name + ' ' : ''
if (min && max) {
message += `must be greater than or equal to ${newMin} ${suffix} and less than or equal to ${newMax} ${suffix}.`
} else if (min) {
message += `must be greater than or equal to ${newMin} ${suffix}.`
} else if (max) {
message += `must be less than or equal to ${newMax} ${suffix}.`
} else {
message += 'Invalid input.'
}
return message
}
BnAsDecimalInput.prototype.downsize = function (number, scale) {
// if there is no scaling, simply return the number
if (scale === 0) {
return Number(number)
} else {
// if the scale is the same as the precision, account for this edge case.
var adjustedNumber = number
while (adjustedNumber.length < scale) {
adjustedNumber = '0' + adjustedNumber
}
return Number(adjustedNumber.slice(0, -scale) + '.' + adjustedNumber.slice(-scale))
}
}
BnAsDecimalInput.prototype.upsize = function (number, scale, precision) {
var stringArray = number.toString().split('.')
var decimalLength = stringArray[1] ? stringArray[1].length : 0
var newString = stringArray[0]
// If there is scaling and decimal parts exist, integrate them in.
if ((scale !== 0) && (decimalLength !== 0)) {
newString += stringArray[1].slice(0, precision)
}
// Add 0s to account for the upscaling.
for (var i = decimalLength; i < scale; i++) {
newString += '0'
}
return newString
}

@ -1,261 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../../ui/app/actions')
const CoinbaseForm = require('./coinbase-form')
const ShapeshiftForm = require('./shapeshift-form')
const Loading = require('./loading')
const AccountPanel = require('./account-panel')
const RadioList = require('./custom-radio-list')
const { getNetworkDisplayName } = require('../../../app/scripts/controllers/network/util')
module.exports = connect(mapStateToProps)(BuyButtonSubview)
function mapStateToProps (state) {
return {
identity: state.appState.identity,
account: state.metamask.accounts[state.appState.buyView.buyAddress],
warning: state.appState.warning,
buyView: state.appState.buyView,
network: state.metamask.network,
provider: state.metamask.provider,
context: state.appState.currentView.context,
isSubLoading: state.appState.isSubLoading,
}
}
inherits(BuyButtonSubview, Component)
function BuyButtonSubview () {
Component.call(this)
}
BuyButtonSubview.prototype.render = function () {
return (
h('div', {
style: {
width: '100%',
},
}, [
this.headerSubview(),
this.primarySubview(),
])
)
}
BuyButtonSubview.prototype.headerSubview = function () {
const props = this.props
const isLoading = props.isSubLoading
return (
h('.flex-column', {
style: {
alignItems: 'center',
},
}, [
// header bar (back button, label)
h('.flex-row', {
style: {
alignItems: 'center',
justifyContent: 'center',
},
}, [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
onClick: this.backButtonContext.bind(this),
style: {
position: 'absolute',
left: '10px',
},
}),
h('h2.text-transform-uppercase.flex-center', {
style: {
width: '100vw',
background: 'rgb(235, 235, 235)',
color: 'rgb(174, 174, 174)',
paddingTop: '4px',
paddingBottom: '4px',
},
}, 'Buy Eth'),
]),
// loading indication
h('div', {
style: {
position: 'absolute',
top: '57vh',
left: '49vw',
},
}, [
h(Loading, { isLoading }),
]),
// account panel
h('div', {
style: {
width: '80%',
},
}, [
h(AccountPanel, {
showFullAddress: true,
identity: props.identity,
account: props.account,
}),
]),
h('.flex-row', {
style: {
alignItems: 'center',
justifyContent: 'center',
},
}, [
h('h3.text-transform-uppercase.flex-center', {
style: {
paddingLeft: '15px',
width: '100vw',
background: 'rgb(235, 235, 235)',
color: 'rgb(174, 174, 174)',
paddingTop: '4px',
paddingBottom: '4px',
},
}, 'Select Service'),
]),
])
)
}
BuyButtonSubview.prototype.primarySubview = function () {
const props = this.props
const network = props.network
switch (network) {
case 'loading':
return
case '1':
return this.mainnetSubview()
// Ropsten, Rinkeby, Kovan
case '3':
case '4':
case '42':
const networkName = getNetworkDisplayName(network)
const label = `${networkName} Test Faucet`
return (
h('div.flex-column', {
style: {
alignItems: 'center',
margin: '20px 50px',
},
}, [
h('button.text-transform-uppercase', {
onClick: () => this.props.dispatch(actions.buyEth({ network })),
style: {
marginTop: '15px',
},
}, label),
// Kovan only: Dharma loans beta
network === '42' ? (
h('button.text-transform-uppercase', {
onClick: () => this.navigateTo('https://borrow.dharma.io/'),
style: {
marginTop: '15px',
},
}, 'Borrow With Dharma (Beta)')
) : null,
])
)
default:
return (
h('h2.error', 'Unknown network ID')
)
}
}
BuyButtonSubview.prototype.mainnetSubview = function () {
const props = this.props
return (
h('.flex-column', {
style: {
alignItems: 'center',
},
}, [
h('.flex-row.selected-exchange', {
style: {
position: 'relative',
right: '35px',
marginTop: '20px',
marginBottom: '20px',
},
}, [
h(RadioList, {
defaultFocus: props.buyView.subview,
labels: [
'Coinbase',
'ShapeShift',
],
subtext: {
'Coinbase': 'Crypto/FIAT (USA only)',
'ShapeShift': 'Crypto',
},
onClick: this.radioHandler.bind(this),
}),
]),
h('h3.text-transform-uppercase', {
style: {
paddingLeft: '15px',
fontFamily: 'Montserrat Light',
width: '100vw',
background: 'rgb(235, 235, 235)',
color: 'rgb(174, 174, 174)',
paddingTop: '4px',
paddingBottom: '4px',
},
}, props.buyView.subview),
this.formVersionSubview(),
])
)
}
BuyButtonSubview.prototype.formVersionSubview = function () {
const network = this.props.network
if (network === '1') {
if (this.props.buyView.formView.coinbase) {
return h(CoinbaseForm, this.props)
} else if (this.props.buyView.formView.shapeshift) {
return h(ShapeshiftForm, this.props)
}
}
}
BuyButtonSubview.prototype.navigateTo = function (url) {
global.platform.openWindow({ url })
}
BuyButtonSubview.prototype.backButtonContext = function () {
if (this.props.context === 'confTx') {
this.props.dispatch(actions.showConfTxPage(false))
} else {
this.props.dispatch(actions.goHome())
}
}
BuyButtonSubview.prototype.radioHandler = function (event) {
switch (event.target.title) {
case 'Coinbase':
return this.props.dispatch(actions.coinBaseSubview())
case 'ShapeShift':
return this.props.dispatch(actions.shapeShiftSubview(this.props.provider.type))
}
}

@ -1,63 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../../ui/app/actions')
module.exports = connect(mapStateToProps)(CoinbaseForm)
function mapStateToProps (state) {
return {
warning: state.appState.warning,
}
}
inherits(CoinbaseForm, Component)
function CoinbaseForm () {
Component.call(this)
}
CoinbaseForm.prototype.render = function () {
var props = this.props
return h('.flex-column', {
style: {
marginTop: '35px',
padding: '25px',
width: '100%',
},
}, [
h('.flex-row', {
style: {
justifyContent: 'space-around',
margin: '33px',
marginTop: '0px',
},
}, [
h('button.btn-green', {
onClick: this.toCoinbase.bind(this),
}, 'Continue to Coinbase'),
h('button.btn-red', {
onClick: () => props.dispatch(actions.goHome()),
}, 'Cancel'),
]),
])
}
CoinbaseForm.prototype.toCoinbase = function () {
const props = this.props
const address = props.buyView.buyAddress
props.dispatch(actions.buyEth({ network: '1', address, amount: 0 }))
}
CoinbaseForm.prototype.renderLoading = function () {
return h('img', {
style: {
width: '27px',
marginRight: '-27px',
},
src: 'images/loading.svg',
})
}

@ -1,59 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const copyToClipboard = require('copy-to-clipboard')
const Tooltip = require('./tooltip')
module.exports = CopyButton
inherits(CopyButton, Component)
function CopyButton () {
Component.call(this)
}
// As parameters, accepts:
// "value", which is the value to copy (mandatory)
// "title", which is the text to show on hover (optional, defaults to 'Copy')
CopyButton.prototype.render = function () {
const props = this.props
const state = this.state || {}
const value = props.value
const copied = state.copied
const message = copied ? 'Copied' : props.title || ' Copy '
return h('.copy-button', {
style: {
display: 'flex',
alignItems: 'center',
},
}, [
h(Tooltip, {
title: message,
}, [
h('i.fa.fa-clipboard.cursor-pointer.color-orange', {
style: {
margin: '5px',
},
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
copyToClipboard(value)
this.debounceRestore()
},
}),
]),
])
}
CopyButton.prototype.debounceRestore = function () {
this.setState({ copied: true })
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.setState({ copied: false })
}, 850)
}

@ -1,46 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Tooltip = require('./tooltip')
const copyToClipboard = require('copy-to-clipboard')
module.exports = Copyable
inherits(Copyable, Component)
function Copyable () {
Component.call(this)
this.state = {
copied: false,
}
}
Copyable.prototype.render = function () {
const props = this.props
const state = this.state
const { value, children } = props
const { copied } = state
return h(Tooltip, {
title: copied ? 'Copied!' : 'Copy',
position: 'bottom',
}, h('span', {
style: {
cursor: 'pointer',
},
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
copyToClipboard(value)
this.debounceRestore()
},
}, children))
}
Copyable.prototype.debounceRestore = function () {
this.setState({ copied: true })
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.setState({ copied: false })
}, 850)
}

@ -1,60 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
module.exports = RadioList
inherits(RadioList, Component)
function RadioList () {
Component.call(this)
}
RadioList.prototype.render = function () {
const props = this.props
const activeClass = '.custom-radio-selected'
const inactiveClass = '.custom-radio-inactive'
const {
labels,
defaultFocus,
} = props
return (
h('.flex-row', {
style: {
fontSize: '12px',
},
}, [
h('.flex-column.custom-radios', {
style: {
marginRight: '5px',
},
},
labels.map((lable, i) => {
let isSelcted = (this.state !== null)
isSelcted = isSelcted ? (this.state.selected === lable) : (defaultFocus === lable)
return h(isSelcted ? activeClass : inactiveClass, {
title: lable,
onClick: (event) => {
this.setState({selected: event.target.title})
props.onClick(event)
},
})
})
),
h('.text', {},
labels.map((lable) => {
if (props.subtext) {
return h('.flex-row', {}, [
h('.radio-titles', lable),
h('.radio-titles-subtext', `- ${props.subtext[lable]}`),
])
} else {
return h('.radio-titles', lable)
}
})
),
])
)
}

@ -1,98 +0,0 @@
const Component = require('react').Component
const PropTypes = require('prop-types')
const h = require('react-hyperscript')
const MenuDroppo = require('./menu-droppo')
const extend = require('xtend')
const noop = () => {}
class Dropdown extends Component {
render () {
const { isOpen, onClickOutside, style, innerStyle, children, useCssTransition } = this.props
const innerStyleDefaults = extend({
borderRadius: '4px',
padding: '8px 16px',
background: 'rgba(0, 0, 0, 0.8)',
boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px',
}, innerStyle)
return h(
MenuDroppo,
{
useCssTransition,
isOpen,
zIndex: 11,
onClickOutside,
style,
innerStyle: innerStyleDefaults,
},
[
h(
'style',
`
li.dropdown-menu-item:hover { color:rgb(225, 225, 225); }
li.dropdown-menu-item { color: rgb(185, 185, 185); position: relative }
`
),
...children,
]
)
}
}
Dropdown.defaultProps = {
isOpen: false,
onClick: noop,
useCssTransition: false,
}
Dropdown.propTypes = {
isOpen: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
children: PropTypes.node,
style: PropTypes.object.isRequired,
onClickOutside: PropTypes.func,
innerStyle: PropTypes.object,
useCssTransition: PropTypes.bool,
}
class DropdownMenuItem extends Component {
render () {
const { onClick, closeMenu, children, style } = this.props
return h(
'li.dropdown-menu-item',
{
onClick: () => {
onClick()
closeMenu()
},
style: Object.assign({
listStyle: 'none',
padding: '8px 0px 8px 0px',
fontSize: '18px',
fontStyle: 'normal',
fontFamily: 'Montserrat Regular',
cursor: 'pointer',
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
}, style),
},
children
)
}
}
DropdownMenuItem.propTypes = {
closeMenu: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
children: PropTypes.node,
style: PropTypes.object,
}
module.exports = {
Dropdown,
DropdownMenuItem,
}

@ -1,57 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const findDOMNode = require('react-dom').findDOMNode
module.exports = EditableLabel
inherits(EditableLabel, Component)
function EditableLabel () {
Component.call(this)
}
EditableLabel.prototype.render = function () {
const props = this.props
const state = this.state
if (state && state.isEditingLabel) {
return h('div.editable-label', [
h('input.sizing-input', {
defaultValue: props.textValue,
maxLength: '20',
onKeyPress: (event) => {
this.saveIfEnter(event)
},
}),
h('button.editable-button', {
onClick: () => this.saveText(),
}, 'Save'),
])
} else {
return h('div.name-label', {
onClick: (event) => {
const nameAttribute = event.target.getAttribute('name')
// checks for class to handle smaller CTA above the account name
const classAttribute = event.target.getAttribute('class')
if (nameAttribute === 'edit' || classAttribute === 'edit-text') {
this.setState({ isEditingLabel: true })
}
},
}, this.props.children)
}
}
EditableLabel.prototype.saveIfEnter = function (event) {
if (event.key === 'Enter') {
this.saveText()
}
}
EditableLabel.prototype.saveText = function () {
// eslint-disable-next-line react/no-find-dom-node
var container = findDOMNode(this)
var text = container.querySelector('.editable-label input').value
var truncatedText = text.substring(0, 20)
this.props.saveText(truncatedText)
this.setState({ isEditingLabel: false, textLabel: truncatedText })
}

@ -1,176 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const debounce = require('debounce')
const copyToClipboard = require('copy-to-clipboard')
const ENS = require('ethjs-ens')
const networkMap = require('ethjs-ens/lib/network-map.json')
const ensRE = /.+\..+$/
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
const log = require('loglevel')
module.exports = EnsInput
inherits(EnsInput, Component)
function EnsInput () {
Component.call(this)
}
EnsInput.prototype.render = function () {
const props = this.props
function onInputChange () {
const network = this.props.network
const networkHasEnsSupport = getNetworkEnsSupport(network)
if (!networkHasEnsSupport) return
const recipient = document.querySelector('input[name="address"]').value
if (recipient.match(ensRE) === null) {
return this.setState({
loadingEns: false,
ensResolution: null,
ensFailure: null,
})
}
this.setState({
loadingEns: true,
})
this.checkName()
}
return (
h('div', {
style: { width: '100%' },
}, [
h('input.large-input', {
name: props.name,
placeholder: props.placeholder,
list: 'addresses',
onChange: onInputChange.bind(this),
}),
// The address book functionality.
h('datalist#addresses',
[
// Corresponds to the addresses owned.
Object.keys(props.identities).map((key) => {
const identity = props.identities[key]
return h('option', {
value: identity.address,
label: identity.name,
key: identity.address,
})
}),
// Corresponds to previously sent-to addresses.
props.addressBook.map((identity) => {
return h('option', {
value: identity.address,
label: identity.name,
key: identity.address,
})
}),
]),
this.ensIcon(),
])
)
}
EnsInput.prototype.componentDidMount = function () {
const network = this.props.network
const networkHasEnsSupport = getNetworkEnsSupport(network)
this.setState({ ensResolution: ZERO_ADDRESS })
if (networkHasEnsSupport) {
const provider = global.ethereumProvider
this.ens = new ENS({ provider, network })
this.checkName = debounce(this.lookupEnsName.bind(this), 200)
}
}
EnsInput.prototype.lookupEnsName = function () {
const recipient = document.querySelector('input[name="address"]').value
const { ensResolution } = this.state
log.info(`ENS attempting to resolve name: ${recipient}`)
this.ens.lookup(recipient.trim())
.then((address) => {
if (address === ZERO_ADDRESS) throw new Error('No address has been set for this name.')
if (address !== ensResolution) {
this.setState({
loadingEns: false,
ensResolution: address,
nickname: recipient.trim(),
hoverText: address + '\nClick to Copy',
ensFailure: false,
})
}
})
.catch((reason) => {
log.error(reason)
return this.setState({
loadingEns: false,
ensResolution: ZERO_ADDRESS,
ensFailure: true,
hoverText: reason.message,
})
})
}
EnsInput.prototype.componentDidUpdate = function (prevProps, prevState) {
const state = this.state || {}
const ensResolution = state.ensResolution
// If an address is sent without a nickname, meaning not from ENS or from
// the user's own accounts, a default of a one-space string is used.
const nickname = state.nickname || ' '
if (prevState && ensResolution && this.props.onChange &&
ensResolution !== prevState.ensResolution) {
this.props.onChange(ensResolution, nickname)
}
}
EnsInput.prototype.ensIcon = function (recipient) {
const { hoverText } = this.state || {}
return h('span', {
title: hoverText,
style: {
position: 'absolute',
padding: '9px',
transform: 'translatex(-40px)',
},
}, this.ensIconContents(recipient))
}
EnsInput.prototype.ensIconContents = function (recipient) {
const { loadingEns, ensFailure, ensResolution } = this.state || { ensResolution: ZERO_ADDRESS}
if (loadingEns) {
return h('img', {
src: 'images/loading.svg',
style: {
width: '30px',
height: '30px',
transform: 'translateY(-6px)',
},
})
}
if (ensFailure) {
return h('i.fa.fa-warning.fa-lg.warning')
}
if (ensResolution && (ensResolution !== ZERO_ADDRESS)) {
return h('i.fa.fa-check-circle.fa-lg.cursor-pointer', {
style: { color: 'green' },
onClick: (event) => {
event.preventDefault()
event.stopPropagation()
copyToClipboard(ensResolution)
},
})
}
}
function getNetworkEnsSupport (network) {
return Boolean(networkMap[network])
}

@ -1,95 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const inherits = require('util').inherits
const formatBalance = require('../util').formatBalance
const generateBalanceObject = require('../util').generateBalanceObject
const Tooltip = require('./tooltip.js')
const FiatValue = require('./fiat-value.js')
module.exports = connect(mapStateToProps)(EthBalanceComponent)
function mapStateToProps (state) {
return {
ticker: state.metamask.ticker,
}
}
inherits(EthBalanceComponent, Component)
function EthBalanceComponent () {
Component.call(this)
}
EthBalanceComponent.prototype.render = function () {
var props = this.props
let { value } = props
const { ticker, style, width } = props
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
value = value ? formatBalance(value, 6, needsParse, ticker) : '...'
return (
h('.ether-balance.ether-balance-amount', {
style,
}, [
h('div', {
style: {
display: 'inline',
width,
},
}, this.renderBalance(value)),
])
)
}
EthBalanceComponent.prototype.renderBalance = function (value) {
var props = this.props
const { conversionRate, shorten, incoming, currentCurrency } = props
if (value === 'None') return value
if (value === '...') return value
var balanceObj = generateBalanceObject(value, shorten ? 1 : 3)
var balance
var splitBalance = value.split(' ')
var ethNumber = splitBalance[0]
var ethSuffix = splitBalance[1]
const showFiat = 'showFiat' in props ? props.showFiat : true
if (shorten) {
balance = balanceObj.shortBalance
} else {
balance = balanceObj.balance
}
var label = balanceObj.label
return (
h(Tooltip, {
position: 'bottom',
title: `${ethNumber} ${ethSuffix}`,
}, h('div.flex-column', [
h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
},
}, [
h('div', {
style: {
width: '100%',
textAlign: 'right',
},
}, incoming ? `+${balance}` : balance),
h('div', {
style: {
color: ' #AEAEAE',
fontSize: '12px',
marginLeft: '5px',
},
}, label),
]),
showFiat ? h(FiatValue, { value: props.value, conversionRate, currentCurrency }) : null,
]))
)
}

@ -1,64 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const formatBalance = require('../util').formatBalance
module.exports = FiatValue
inherits(FiatValue, Component)
function FiatValue () {
Component.call(this)
}
FiatValue.prototype.render = function () {
const props = this.props
const { conversionRate, currentCurrency } = props
const renderedCurrency = currentCurrency || ''
const value = formatBalance(props.value, 6)
if (value === 'None') return value
var fiatDisplayNumber, fiatTooltipNumber
var splitBalance = value.split(' ')
if (conversionRate !== 0) {
fiatTooltipNumber = Number(splitBalance[0]) * conversionRate
fiatDisplayNumber = fiatTooltipNumber.toFixed(2)
} else {
fiatDisplayNumber = 'N/A'
fiatTooltipNumber = 'Unknown'
}
return fiatDisplay(fiatDisplayNumber, renderedCurrency.toUpperCase())
}
function fiatDisplay (fiatDisplayNumber, fiatSuffix) {
if (fiatDisplayNumber !== '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',
},
}, fiatDisplayNumber),
h('div', {
style: {
color: '#AEAEAE',
marginLeft: '5px',
fontSize: '12px',
},
}, fiatSuffix),
])
} else {
return h('div')
}
}

@ -1,154 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
module.exports = HexAsDecimalInput
inherits(HexAsDecimalInput, Component)
function HexAsDecimalInput () {
this.state = { invalid: null }
Component.call(this)
}
/* Hex as Decimal Input
*
* A component for allowing easy, decimal editing
* of a passed in hex string value.
*
* On change, calls back its `onChange` function parameter
* and passes it an updated hex string.
*/
HexAsDecimalInput.prototype.render = function () {
const props = this.props
const state = this.state
const { value, onChange, min, max } = props
const toEth = props.toEth
const suffix = props.suffix
const decimalValue = decimalize(value, toEth)
const style = props.style
return (
h('.flex-column', [
h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
},
}, [
h('input.hex-input', {
type: 'number',
required: true,
min: min,
max: max,
style: extend({
display: 'block',
textAlign: 'right',
backgroundColor: 'transparent',
border: '1px solid #bdbdbd',
}, style),
value: parseInt(decimalValue),
onBlur: (event) => {
this.updateValidity(event)
},
onChange: (event) => {
this.updateValidity(event)
const hexString = (event.target.value === '') ? '' : hexify(event.target.value)
onChange(hexString)
},
onInvalid: (event) => {
const msg = this.constructWarning()
if (msg === state.invalid) {
return
}
this.setState({ invalid: msg })
event.preventDefault()
return false
},
}),
h('div', {
style: {
color: ' #AEAEAE',
fontSize: '12px',
marginLeft: '5px',
marginRight: '6px',
width: '20px',
},
}, suffix),
]),
state.invalid ? h('span.error', {
style: {
position: 'absolute',
right: '0px',
textAlign: 'right',
transform: 'translateY(26px)',
padding: '3px',
background: 'rgba(255,255,255,0.85)',
zIndex: '1',
textTransform: 'capitalize',
border: '2px solid #E20202',
},
}, state.invalid) : null,
])
)
}
HexAsDecimalInput.prototype.setValid = function (message) {
this.setState({ invalid: null })
}
HexAsDecimalInput.prototype.updateValidity = function (event) {
const target = event.target
const value = this.props.value
const newValue = target.value
if (value === newValue) {
return
}
const valid = target.checkValidity()
if (valid) {
this.setState({ invalid: null })
}
}
HexAsDecimalInput.prototype.constructWarning = function () {
const { name, min, max } = this.props
let message = name ? name + ' ' : ''
if (min && max) {
message += `must be greater than or equal to ${min} and less than or equal to ${max}.`
} else if (min) {
message += `must be greater than or equal to ${min}.`
} else if (max) {
message += `must be less than or equal to ${max}.`
} else {
message += 'Invalid input.'
}
return message
}
function hexify (decimalString) {
const hexBN = new BN(parseInt(decimalString), 10)
return '0x' + hexBN.toString('hex')
}
function decimalize (input, toEth) {
if (input === '') {
return ''
} else {
const strippedInput = ethUtil.stripHexPrefix(input)
const inputBN = new BN(strippedInput, 'hex')
return inputBN.toString(10)
}
}

@ -1,74 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const isNode = require('detect-node')
const findDOMNode = require('react-dom').findDOMNode
const jazzicon = require('jazzicon')
const iconFactoryGen = require('../../lib/icon-factory')
const iconFactory = iconFactoryGen(jazzicon)
module.exports = IdenticonComponent
inherits(IdenticonComponent, Component)
function IdenticonComponent () {
Component.call(this)
this.defaultDiameter = 46
}
IdenticonComponent.prototype.render = function () {
var props = this.props
var diameter = props.diameter || this.defaultDiameter
return (
h('div', {
key: 'identicon-' + this.props.address,
style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: diameter,
width: diameter,
borderRadius: diameter / 2,
overflow: 'hidden',
},
})
)
}
IdenticonComponent.prototype.componentDidMount = function () {
var props = this.props
const { address } = props
if (!address) return
// eslint-disable-next-line react/no-find-dom-node
var container = findDOMNode(this)
var diameter = props.diameter || this.defaultDiameter
if (!isNode) {
var img = iconFactory.iconForAddress(address, diameter)
container.appendChild(img)
}
}
IdenticonComponent.prototype.componentDidUpdate = function () {
var props = this.props
const { address } = props
if (!address) return
// eslint-disable-next-line react/no-find-dom-node
var container = findDOMNode(this)
var children = container.children
for (var i = 0; i < children.length; i++) {
container.removeChild(children[i])
}
var diameter = props.diameter || this.defaultDiameter
if (!isNode) {
var img = iconFactory.iconForAddress(address, diameter)
container.appendChild(img)
}
}

@ -1,55 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
inherits(LoadingIndicator, Component)
module.exports = LoadingIndicator
function LoadingIndicator () {
Component.call(this)
}
LoadingIndicator.prototype.render = function () {
const { isLoading, loadingMessage, canBypass, bypass } = this.props
return (
isLoading ? h('.full-flex-height', {
style: {
left: '0px',
zIndex: 10,
position: 'absolute',
flexDirection: 'column',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
width: '100%',
background: 'rgba(255, 255, 255, 0.8)',
},
}, [
canBypass ? h('i.fa.fa-close.cursor-pointer.close-loading', {
style: {
position: 'absolute',
top: '1px',
right: '15px',
color: '#AEAEAE',
},
onClick: bypass,
}) : null,
h('img', {
src: 'images/loading.svg',
}),
h('br'),
showMessageIfAny(loadingMessage),
]) : null
)
}
function showMessageIfAny (loadingMessage) {
if (!loadingMessage) return null
return h('span', loadingMessage)
}

@ -1,59 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const metamaskLogo = require('metamask-logo')
const debounce = require('debounce')
module.exports = Mascot
inherits(Mascot, Component)
function Mascot () {
Component.call(this)
this.logo = metamaskLogo({
followMouse: true,
pxNotRatio: true,
width: 200,
height: 200,
})
this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000)
this.unfollowMouse = this.logo.setFollowMouse.bind(this.logo, false)
}
Mascot.prototype.render = function () {
// this is a bit hacky
// the event emitter is on `this.props`
// and we dont get that until render
this.handleAnimationEvents()
return h('#metamask-mascot-container', {
style: { zIndex: 0 },
})
}
Mascot.prototype.componentDidMount = function () {
var targetDivId = 'metamask-mascot-container'
var container = document.getElementById(targetDivId)
container.appendChild(this.logo.container)
}
Mascot.prototype.componentWillUnmount = function () {
this.animations = this.props.animationEventEmitter
this.animations.removeAllListeners()
this.logo.container.remove()
this.logo.stopAnimation()
}
Mascot.prototype.handleAnimationEvents = function () {
// only setup listeners once
if (this.animations) return
this.animations = this.props.animationEventEmitter
this.animations.on('point', this.lookAt.bind(this))
this.animations.on('setFollowMouse', this.logo.setFollowMouse.bind(this.logo))
}
Mascot.prototype.lookAt = function (target) {
this.unfollowMouse()
this.logo.lookAt(target)
this.refollowMouse()
}

@ -1,132 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const findDOMNode = require('react-dom').findDOMNode
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
module.exports = MenuDroppoComponent
inherits(MenuDroppoComponent, Component)
function MenuDroppoComponent () {
Component.call(this)
}
MenuDroppoComponent.prototype.render = function () {
const speed = this.props.speed || '300ms'
const useCssTransition = this.props.useCssTransition
const zIndex = ('zIndex' in this.props) ? this.props.zIndex : 0
this.manageListeners()
const style = this.props.style || {}
if (!('position' in style)) {
style.position = 'fixed'
}
style.zIndex = zIndex
return (
h('.menu-droppo-container', {
style,
}, [
h('style', `
.menu-droppo-enter {
transition: transform ${speed} ease-in-out;
transform: translateY(-200%);
}
.menu-droppo-enter.menu-droppo-enter-active {
transition: transform ${speed} ease-in-out;
transform: translateY(0%);
}
.menu-droppo-leave {
transition: transform ${speed} ease-in-out;
transform: translateY(0%);
}
.menu-droppo-leave.menu-droppo-leave-active {
transition: transform ${speed} ease-in-out;
transform: translateY(-200%);
}
`),
useCssTransition
? h(ReactCSSTransitionGroup, {
className: 'css-transition-group',
transitionName: 'menu-droppo',
transitionEnterTimeout: parseInt(speed),
transitionLeaveTimeout: parseInt(speed),
}, this.renderPrimary())
: this.renderPrimary(),
])
)
}
MenuDroppoComponent.prototype.renderPrimary = function () {
const isOpen = this.props.isOpen
if (!isOpen) {
return null
}
const innerStyle = this.props.innerStyle || {}
return (
h('.menu-droppo', {
key: 'menu-droppo-drawer',
style: innerStyle,
},
[ this.props.children ])
)
}
MenuDroppoComponent.prototype.manageListeners = function () {
const isOpen = this.props.isOpen
const onClickOutside = this.props.onClickOutside
if (isOpen) {
this.outsideClickHandler = onClickOutside
} else if (isOpen) {
this.outsideClickHandler = null
}
}
MenuDroppoComponent.prototype.componentDidMount = function () {
if (this && document.body) {
this.globalClickHandler = this.globalClickOccurred.bind(this)
document.body.addEventListener('click', this.globalClickHandler)
// eslint-disable-next-line react/no-find-dom-node
var container = findDOMNode(this)
this.container = container
}
}
MenuDroppoComponent.prototype.componentWillUnmount = function () {
if (this && document.body) {
document.body.removeEventListener('click', this.globalClickHandler)
}
}
MenuDroppoComponent.prototype.globalClickOccurred = function (event) {
const target = event.target
// eslint-disable-next-line react/no-find-dom-node
const container = findDOMNode(this)
if (target !== container &&
!isDescendant(this.container, event.target) &&
this.outsideClickHandler) {
this.outsideClickHandler(event)
}
}
function isDescendant (parent, child) {
var node = child.parentNode
while (node !== null) {
if (node === parent) {
return true
}
node = node.parentNode
}
return false
}

@ -1,74 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const Identicon = require('./identicon')
module.exports = AccountPanel
inherits(AccountPanel, Component)
function AccountPanel () {
Component.call(this)
}
AccountPanel.prototype.render = function () {
var props = this.props
var picOrder = props.picOrder || 'left'
const { imageSeed } = props
return (
h('.identity-panel.flex-row.flex-left', {
style: {
cursor: props.onClick ? 'pointer' : undefined,
},
onClick: props.onClick,
}, [
this.genIcon(imageSeed, picOrder),
h('div.flex-column.flex-justify-center', {
style: {
lineHeight: '15px',
order: 2,
display: 'flex',
alignItems: picOrder === 'left' ? 'flex-begin' : 'flex-end',
},
}, this.props.children),
])
)
}
AccountPanel.prototype.genIcon = function (seed, picOrder) {
const props = this.props
// When there is no seed value, this is a contract creation.
// We then show the contract icon.
if (!seed) {
return h('.identicon-wrapper.flex-column.select-none', {
style: {
order: picOrder === 'left' ? 1 : 3,
},
}, [
h('i.fa.fa-file-text-o.fa-lg', {
style: {
fontSize: '42px',
transform: 'translate(0px, -16px)',
},
}),
])
}
// If there was a seed, we return an identicon for that address.
return h('.identicon-wrapper.flex-column.select-none', {
style: {
order: picOrder === 'left' ? 1 : 3,
},
}, [
h(Identicon, {
address: seed,
imageify: props.imageifyIdenticons,
}),
])
}

@ -1,131 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
module.exports = Network
inherits(Network, Component)
function Network () {
Component.call(this)
}
Network.prototype.render = function () {
const props = this.props
const networkNumber = props.network
let providerName
try {
providerName = props.provider.type
} catch (e) {
providerName = null
}
let iconName, hoverText
if (networkNumber === 'loading') {
return h('span.pointer', {
className: props.onClick && 'pointer',
style: {
display: 'flex',
alignItems: 'center',
flexDirection: 'row',
},
onClick: (event) => props.onClick && props.onClick(event),
}, [
props.onClick && h('img', {
title: 'Attempting to connect to blockchain.',
style: {
width: '27px',
},
src: 'images/loading.svg',
}),
h('i.fa.fa-caret-down'),
])
} else if (providerName === 'mainnet') {
hoverText = 'Main Ethereum Network'
iconName = 'ethereum-network'
} else if (providerName === 'ropsten') {
hoverText = 'Ropsten Test Network'
iconName = 'ropsten-test-network'
} else if (parseInt(networkNumber) === 3) {
hoverText = 'Ropsten Test Network'
iconName = 'ropsten-test-network'
} else if (providerName === 'kovan') {
hoverText = 'Kovan Test Network'
iconName = 'kovan-test-network'
} else if (providerName === 'rinkeby') {
hoverText = 'Rinkeby Test Network'
iconName = 'rinkeby-test-network'
} else {
hoverText = 'Unknown Private Network'
iconName = 'unknown-private-network'
}
return (
h('#network_component', {
className: props.onClick && 'pointer',
title: hoverText,
onClick: (event) => props.onClick && props.onClick(event),
}, [
(function () {
switch (iconName) {
case 'ethereum-network':
return h('.network-indicator', [
h('.menu-icon.diamond'),
h('.network-name', {
style: {
color: '#039396',
}},
'Main Network'),
props.onClick && h('i.fa.fa-caret-down.fa-lg'),
])
case 'ropsten-test-network':
return h('.network-indicator', [
h('.menu-icon.red-dot'),
h('.network-name', {
style: {
color: '#ff6666',
}},
'Ropsten Test Net'),
props.onClick && h('i.fa.fa-caret-down.fa-lg'),
])
case 'kovan-test-network':
return h('.network-indicator', [
h('.menu-icon.hollow-diamond'),
h('.network-name', {
style: {
color: '#690496',
}},
'Kovan Test Net'),
props.onClick && h('i.fa.fa-caret-down.fa-lg'),
])
case 'rinkeby-test-network':
return h('.network-indicator', [
h('.menu-icon.golden-square'),
h('.network-name', {
style: {
color: '#e7a218',
}},
'Rinkeby Test Net'),
props.onClick && h('i.fa.fa-caret-down.fa-lg'),
])
default:
return h('.network-indicator', [
h('i.fa.fa-question-circle.fa-lg', {
style: {
margin: '10px',
color: 'rgb(125, 128, 130)',
},
}),
h('.network-name', {
style: {
color: '#AEAEAE',
}},
'Private Network'),
props.onClick && h('i.fa.fa-caret-down.fa-lg'),
])
}
})(),
])
)
}

@ -1,145 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const ReactMarkdown = require('react-markdown')
const linker = require('extension-link-enabler')
const findDOMNode = require('react-dom').findDOMNode
module.exports = Notice
inherits(Notice, Component)
function Notice () {
Component.call(this)
}
Notice.prototype.render = function () {
const { notice, onConfirm } = this.props
const { title, date, body } = notice
const state = this.state || { disclaimerDisabled: true }
const disabled = state.disclaimerDisabled
return (
h('.flex-column.flex-center.flex-grow', {
style: {
width: '100%',
},
}, [
h('h3.flex-center.text-transform-uppercase.terms-header', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
width: '100%',
fontSize: '20px',
textAlign: 'center',
padding: 6,
},
}, [
title,
]),
h('h5.flex-center.text-transform-uppercase.terms-header', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
marginBottom: 24,
width: '100%',
fontSize: '20px',
textAlign: 'center',
padding: 6,
},
}, [
date,
]),
h('style', `
.markdown {
overflow-x: hidden;
}
.markdown h1, .markdown h2, .markdown h3 {
margin: 10px 0;
font-weight: bold;
}
.markdown strong {
font-weight: bold;
}
.markdown em {
font-style: italic;
}
.markdown p {
margin: 10px 0;
}
.markdown a {
color: #df6b0e;
}
`),
h('div.markdown', {
onScroll: (e) => {
var object = e.currentTarget
if (object.offsetHeight + object.scrollTop + 100 >= object.scrollHeight) {
this.setState({disclaimerDisabled: false})
}
},
style: {
background: 'rgb(235, 235, 235)',
height: '310px',
padding: '6px',
width: '90%',
overflowY: 'scroll',
scroll: 'auto',
},
}, [
h(ReactMarkdown, {
className: 'notice-box',
source: body,
skipHtml: true,
}),
]),
h('button', {
disabled,
onClick: () => {
this.setState({disclaimerDisabled: true})
onConfirm()
},
style: {
marginTop: '18px',
},
}, 'Accept'),
])
)
}
Notice.prototype.setInitialDisclaimerState = function () {
if (document.getElementsByClassName('notice-box')[0].clientHeight < 310) {
this.setState({disclaimerDisabled: false})
}
}
Notice.prototype.componentDidMount = function () {
// eslint-disable-next-line react/no-find-dom-node
var node = findDOMNode(this)
linker.setupListener(node)
this.setInitialDisclaimerState()
}
Notice.prototype.componentDidUpdate = function (prevProps) {
const { notice: { id } = {} } = this.props
const { notice: { id: prevNoticeId } = {} } = prevProps
if (id !== prevNoticeId) {
this.setInitialDisclaimerState()
}
}
Notice.prototype.componentWillUnmount = function () {
// eslint-disable-next-line react/no-find-dom-node
var node = findDOMNode(this)
linker.teardownListener(node)
}

@ -1,50 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountPanel = require('./account-panel')
module.exports = PendingMsgDetails
inherits(PendingMsgDetails, Component)
function PendingMsgDetails () {
Component.call(this)
}
PendingMsgDetails.prototype.render = function () {
var state = this.props
var msgData = state.txData
var msgParams = msgData.msgParams || {}
var address = msgParams.from || state.selectedAddress
var identity = state.identities[address] || { address: address }
var account = state.accounts[address] || { address: address }
return (
h('div', {
key: msgData.id,
style: {
margin: '10px 20px',
},
}, [
// account that will sign
h(AccountPanel, {
showFullAddress: true,
identity: identity,
account: account,
imageifyIdenticons: state.imageifyIdenticons,
}),
// message data
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-column.flex-space-between', [
h('label.font-small', 'MESSAGE'),
h('span.font-small', msgParams.data),
]),
]),
])
)
}

@ -1,70 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const PendingTxDetails = require('./pending-msg-details')
module.exports = PendingMsg
inherits(PendingMsg, Component)
function PendingMsg () {
Component.call(this)
}
PendingMsg.prototype.render = function () {
var state = this.props
var msgData = state.txData
return (
h('div', {
key: msgData.id,
style: {
maxWidth: '350px',
},
}, [
// header
h('h3', {
style: {
fontWeight: 'bold',
textAlign: 'center',
},
}, 'Sign Message'),
h('.error', {
style: {
margin: '10px',
},
}, [
`Signing this message can have
dangerous side effects. Only sign messages from
sites you fully trust with your entire account.
This dangerous method will be removed in a future version. `,
h('a', {
href: 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527',
style: { color: 'rgb(247, 134, 28)' },
onClick: (event) => {
event.preventDefault()
const url = 'https://medium.com/metamask/the-new-secure-way-to-sign-data-in-your-browser-6af9dd2a1527'
global.platform.openWindow({ url })
},
}, 'Read more here.'),
]),
// message details
h(PendingTxDetails, state),
// sign + cancel
h('.flex-row.flex-space-around', [
h('button', {
onClick: state.cancelMessage,
}, 'Cancel'),
h('button', {
onClick: state.signMessage,
}, 'Sign'),
]),
])
)
}

@ -1,60 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountPanel = require('./account-panel')
const BinaryRenderer = require('./binary-renderer')
module.exports = PendingMsgDetails
inherits(PendingMsgDetails, Component)
function PendingMsgDetails () {
Component.call(this)
}
PendingMsgDetails.prototype.render = function () {
var state = this.props
var msgData = state.txData
var msgParams = msgData.msgParams || {}
var address = msgParams.from || state.selectedAddress
var identity = state.identities[address] || { address: address }
var account = state.accounts[address] || { address: address }
var { data } = msgParams
return (
h('div', {
key: msgData.id,
style: {
margin: '10px 20px',
},
}, [
// account that will sign
h(AccountPanel, {
showFullAddress: true,
identity: identity,
account: account,
imageifyIdenticons: state.imageifyIdenticons,
}),
// message data
h('div', {
style: {
height: '260px',
},
}, [
h('label.font-small', { style: { display: 'block' } }, 'MESSAGE'),
h(BinaryRenderer, {
value: data,
style: {
height: '215px',
},
}),
]),
])
)
}

@ -1,47 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const PendingTxDetails = require('./pending-personal-msg-details')
module.exports = PendingMsg
inherits(PendingMsg, Component)
function PendingMsg () {
Component.call(this)
}
PendingMsg.prototype.render = function () {
var state = this.props
var msgData = state.txData
return (
h('div', {
key: msgData.id,
}, [
// header
h('h3', {
style: {
fontWeight: 'bold',
textAlign: 'center',
},
}, 'Sign Message'),
// message details
h(PendingTxDetails, state),
// sign + cancel
h('.flex-row.flex-space-around', [
h('button', {
onClick: state.cancelPersonalMessage,
}, 'Cancel'),
h('button', {
onClick: state.signPersonalMessage,
}, 'Sign'),
]),
])
)
}

@ -1,519 +0,0 @@
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const inherits = require('util').inherits
const actions = require('../../../ui/app/actions')
const clone = require('clone')
const log = require('loglevel')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const hexToBn = require('../../../app/scripts/lib/hex-to-bn')
const util = require('../util')
const MiniAccountPanel = require('./mini-account-panel')
const Copyable = require('./copyable')
const EthBalance = require('./eth-balance')
const addressSummary = util.addressSummary
const nameForAddress = require('../../lib/contract-namer')
const BNInput = require('./bn-as-decimal-input')
const MIN_GAS_PRICE_BN = new BN('0')
const MIN_GAS_LIMIT_BN = new BN('21000')
module.exports = connect()(PendingTx)
inherits(PendingTx, Component)
function PendingTx () {
Component.call(this)
this.state = {
valid: true,
txData: null,
submitting: false,
}
}
PendingTx.prototype.render = function () {
const props = this.props
const { currentCurrency, blockGasLimit } = props
const conversionRate = props.conversionRate
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
// Allow retry txs
const { lastGasPrice } = txMeta
let forceGasMin
if (lastGasPrice) {
const stripped = ethUtil.stripHexPrefix(lastGasPrice)
const lastGas = new BN(stripped, 16)
const priceBump = lastGas.divn('10')
forceGasMin = lastGas.add(priceBump)
}
// Account Details
const address = txParams.from || props.selectedAddress
const identity = props.identities[address] || { address: address }
const account = props.accounts[address]
const balance = account ? account.balance : '0x0'
// recipient check
const isValidAddress = !txParams.to || util.isValidAddress(txParams.to)
// Gas
const gas = txParams.gas
const gasBn = hexToBn(gas)
// default to 8MM gas limit
const gasLimit = new BN(parseInt(blockGasLimit) || '8000000')
const safeGasLimitBN = this.bnMultiplyByFraction(gasLimit, 99, 100)
const saferGasLimitBN = this.bnMultiplyByFraction(gasLimit, 98, 100)
const safeGasLimit = safeGasLimitBN.toString(10)
// Gas Price
const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16)
const gasPriceBn = hexToBn(gasPrice)
const txFeeBn = gasBn.mul(gasPriceBn)
const valueBn = hexToBn(txParams.value)
const maxCost = txFeeBn.add(valueBn)
const dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0
const balanceBn = hexToBn(balance)
const insufficientBalance = balanceBn.lt(maxCost)
const dangerousGasLimit = gasBn.gte(saferGasLimitBN)
const gasLimitSpecified = txMeta.gasLimitSpecified
const buyDisabled = insufficientBalance || !this.state.valid || !isValidAddress || this.state.submitting
const showRejectAll = props.unconfTxListLength > 1
this.inputs = []
return (
h('div', {
key: txMeta.id,
}, [
h('form#pending-tx-form', {
onSubmit: this.onSubmit.bind(this),
}, [
// tx info
h('div', [
h('.flex-row.flex-center', {
style: {
maxWidth: '100%',
},
}, [
h(MiniAccountPanel, {
imageSeed: address,
picOrder: 'right',
}, [
h('span.font-small', {
style: {
fontFamily: 'Montserrat Bold, Montserrat, sans-serif',
},
}, identity.name),
h(Copyable, {
value: ethUtil.toChecksumAddress(address),
}, [
h('span.font-small', {
style: {
fontFamily: 'Montserrat Light, Montserrat, sans-serif',
},
}, addressSummary(address, 6, 4, false)),
]),
h('span.font-small', {
style: {
fontFamily: 'Montserrat Light, Montserrat, sans-serif',
},
}, [
h(EthBalance, {
value: balance,
conversionRate,
currentCurrency,
inline: true,
labelColor: '#F7861C',
}),
]),
]),
forwardCarrat(),
this.miniAccountPanelForRecipient(),
]),
h('style', `
.table-box {
margin: 7px 0px 0px 0px;
width: 100%;
}
.table-box .row {
margin: 0px;
background: rgb(236,236,236);
display: flex;
justify-content: space-between;
font-family: Montserrat Light, sans-serif;
font-size: 13px;
padding: 5px 25px;
}
.table-box .row .value {
font-family: Montserrat Regular;
}
`),
h('.table-box', [
// Ether Value
// Currently not customizable, but easily modified
// in the way that gas and gasLimit currently are.
h('.row', [
h('.cell.label', 'Amount'),
h(EthBalance, { value: txParams.value, currentCurrency, conversionRate }),
]),
// Gas Limit (customizable)
h('.cell.row', [
h('.cell.label', 'Gas Limit'),
h('.cell.value', {
}, [
h(BNInput, {
name: 'Gas Limit',
value: gasBn,
precision: 0,
scale: 0,
// The hard lower limit for gas.
min: MIN_GAS_LIMIT_BN,
max: safeGasLimit,
suffix: 'UNITS',
style: {
position: 'relative',
top: '5px',
},
onChange: this.gasLimitChanged.bind(this),
ref: (hexInput) => { this.inputs.push(hexInput) },
}),
]),
]),
// Gas Price (customizable)
h('.cell.row', [
h('.cell.label', 'Gas Price'),
h('.cell.value', {
}, [
h(BNInput, {
name: 'Gas Price',
value: gasPriceBn,
precision: 9,
scale: 9,
suffix: 'GWEI',
min: forceGasMin || MIN_GAS_PRICE_BN,
style: {
position: 'relative',
top: '5px',
},
onChange: this.gasPriceChanged.bind(this),
ref: (hexInput) => { this.inputs.push(hexInput) },
}),
]),
]),
// Max Transaction Fee (calculated)
h('.cell.row', [
h('.cell.label', 'Max Transaction Fee'),
h(EthBalance, { value: txFeeBn.toString(16), currentCurrency, conversionRate }),
]),
h('.cell.row', {
style: {
fontFamily: 'Montserrat Regular',
background: 'white',
padding: '10px 25px',
},
}, [
h('.cell.label', 'Max Total'),
h('.cell.value', {
style: {
display: 'flex',
alignItems: 'center',
},
}, [
h(EthBalance, {
value: maxCost.toString(16),
currentCurrency,
conversionRate,
inline: true,
labelColor: 'black',
fontSize: '16px',
}),
]),
]),
// Data size row:
h('.cell.row', {
style: {
background: '#f7f7f7',
paddingBottom: '0px',
},
}, [
h('.cell.label'),
h('.cell.value', {
style: {
fontFamily: 'Montserrat Light',
fontSize: '11px',
},
}, `Data included: ${dataLength} bytes`),
]),
]), // End of Table
]),
h('style', `
.conf-buttons button {
margin-left: 10px;
text-transform: uppercase;
}
`),
h('.cell.row', {
style: {
textAlign: 'center',
},
}, [
txMeta.simulationFails ?
h('.error', {
style: {
fontSize: '0.9em',
},
}, 'Transaction Error. Exception thrown in contract code.')
: null,
!isValidAddress ?
h('.error', {
style: {
fontSize: '0.9em',
},
}, 'Recipient address is invalid. Sending this transaction will result in a loss of ETH.')
: null,
insufficientBalance ?
h('span.error', {
style: {
fontSize: '0.9em',
},
}, 'Insufficient balance for transaction')
: null,
(dangerousGasLimit && !gasLimitSpecified) ?
h('span.error', {
style: {
fontSize: '0.9em',
},
}, 'Gas limit set dangerously high. Approving this transaction is liable to fail.')
: null,
]),
// send + cancel
h('.flex-row.flex-space-around.conf-buttons', {
style: {
display: 'flex',
justifyContent: 'flex-end',
margin: '14px 25px',
},
}, [
h('button', {
onClick: (event) => {
this.resetGasFields()
event.preventDefault()
},
}, 'Reset'),
// Accept Button or Buy Button
insufficientBalance ? h('button.btn-green', { onClick: props.buyEth }, 'Buy Ether') :
h('input.confirm.btn-green', {
type: 'submit',
value: 'SUBMIT',
style: { marginLeft: '10px' },
disabled: buyDisabled,
}),
h('button.cancel.btn-red', {
onClick: props.cancelTransaction,
}, 'Reject'),
]),
showRejectAll ? h('.flex-row.flex-space-around.conf-buttons', {
style: {
display: 'flex',
justifyContent: 'flex-end',
margin: '14px 25px',
},
}, [
h('button.cancel.btn-red', {
onClick: props.cancelAllTransactions,
}, 'Reject All'),
]) : null,
]),
])
)
}
PendingTx.prototype.miniAccountPanelForRecipient = function () {
const props = this.props
const txData = props.txData
const txParams = txData.txParams || {}
const isContractDeploy = !('to' in txParams)
// If it's not a contract deploy, send to the account
if (!isContractDeploy) {
return h(MiniAccountPanel, {
imageSeed: txParams.to,
picOrder: 'left',
}, [
h('span.font-small', {
style: {
fontFamily: 'Montserrat Bold, Montserrat, sans-serif',
},
}, nameForAddress(txParams.to, props.identities)),
h(Copyable, {
value: ethUtil.toChecksumAddress(txParams.to),
}, [
h('span.font-small', {
style: {
fontFamily: 'Montserrat Light, Montserrat, sans-serif',
},
}, addressSummary(txParams.to, 6, 4, false)),
]),
])
} else {
return h(MiniAccountPanel, {
picOrder: 'left',
}, [
h('span.font-small', {
style: {
fontFamily: 'Montserrat Bold, Montserrat, sans-serif',
},
}, 'New Contract'),
])
}
}
PendingTx.prototype.gasPriceChanged = function (newBN, valid) {
log.info(`Gas price changed to: ${newBN.toString(10)}`)
const txMeta = this.gatherTxMeta()
txMeta.txParams.gasPrice = '0x' + newBN.toString('hex')
this.setState({
txData: clone(txMeta),
valid,
})
}
PendingTx.prototype.gasLimitChanged = function (newBN, valid) {
log.info(`Gas limit changed to ${newBN.toString(10)}`)
const txMeta = this.gatherTxMeta()
txMeta.txParams.gas = '0x' + newBN.toString('hex')
this.setState({
txData: clone(txMeta),
valid,
})
}
PendingTx.prototype.resetGasFields = function () {
log.debug(`pending-tx resetGasFields`)
this.inputs.forEach((hexInput) => {
if (hexInput) {
hexInput.setValid()
}
})
this.setState({
txData: null,
valid: true,
})
}
PendingTx.prototype.onSubmit = function (event) {
event.preventDefault()
const txMeta = this.gatherTxMeta()
const valid = this.checkValidity()
this.setState({ valid, submitting: true })
const validGasParams = this.verifyGasParams()
if (valid && validGasParams) {
this.props.sendTransaction(txMeta, event)
} else {
this.props.dispatch(actions.displayWarning('Invalid Gas Parameters'))
this.setState({ submitting: false })
}
}
PendingTx.prototype.checkValidity = function () {
const form = this.getFormEl()
const valid = form.checkValidity()
return valid
}
PendingTx.prototype.getFormEl = function () {
const form = document.querySelector('form#pending-tx-form')
// Stub out form for unit tests:
if (!form) {
return { checkValidity () { return true } }
}
return form
}
// After a customizable state value has been updated,
PendingTx.prototype.gatherTxMeta = function () {
log.debug(`pending-tx gatherTxMeta`)
const props = this.props
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
PendingTx.prototype.verifyGasParams = function () {
// We call this in case the gas has not been modified at all
if (!this.state) { return true }
return (
this._notZeroOrEmptyString(this.state.gas) &&
this._notZeroOrEmptyString(this.state.gasPrice)
)
}
PendingTx.prototype._notZeroOrEmptyString = function (value) {
// allow undefined values
if (value === undefined) return true
// Geth will return '0x', and ganache-core v2.2.1 will return '0x0'
const valueIsEmpty = !value || value === '0x' || value === '0x0'
return !valueIsEmpty
}
PendingTx.prototype.bnMultiplyByFraction = function (targetBN, numerator, denominator) {
const numBN = new BN(numerator)
const denomBN = new BN(denominator)
return targetBN.mul(numBN).div(denomBN)
}
function forwardCarrat () {
return (
h('img', {
src: 'images/forward-carrat.svg',
style: {
padding: '5px 6px 0px 10px',
height: '37px',
},
})
)
}

@ -1,60 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const AccountPanel = require('./account-panel')
const TypedMessageRenderer = require('./typed-message-renderer')
module.exports = PendingMsgDetails
inherits(PendingMsgDetails, Component)
function PendingMsgDetails () {
Component.call(this)
}
PendingMsgDetails.prototype.render = function () {
var state = this.props
var msgData = state.txData
var msgParams = msgData.msgParams || {}
var address = msgParams.from || state.selectedAddress
var identity = state.identities[address] || { address: address }
var account = state.accounts[address] || { address: address }
var { data, version } = msgParams
return (
h('div', {
key: msgData.id,
style: {
margin: '10px 20px',
},
}, [
// account that will sign
h(AccountPanel, {
showFullAddress: true,
identity: identity,
account: account,
imageifyIdenticons: state.imageifyIdenticons,
}),
// message data
h('div', {
style: {
height: '260px',
},
}, [
h('label.font-small', { style: { display: 'block' } }, 'YOU ARE SIGNING'),
h(TypedMessageRenderer, {
value: data,
version,
style: {
height: '215px',
},
}),
]),
])
)
}

@ -1,46 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const PendingTxDetails = require('./pending-typed-msg-details')
module.exports = PendingMsg
inherits(PendingMsg, Component)
function PendingMsg () {
Component.call(this)
}
PendingMsg.prototype.render = function () {
var state = this.props
var msgData = state.txData
return (
h('div', {
key: msgData.id,
}, [
// header
h('h3', {
style: {
fontWeight: 'bold',
textAlign: 'center',
},
}, 'Sign Message'),
// message details
h(PendingTxDetails, state),
// sign + cancel
h('.flex-row.flex-space-around', [
h('button', {
onClick: state.cancelTypedMessage,
}, 'Cancel'),
h('button', {
onClick: state.signTypedMessage,
}, 'Sign'),
]),
])
)
}

@ -1,58 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
module.exports = RangeSlider
inherits(RangeSlider, Component)
function RangeSlider () {
Component.call(this)
}
RangeSlider.prototype.render = function () {
const state = this.state || {}
const props = this.props
const onInput = props.onInput || function () {}
const name = props.name
const {
min = 0,
max = 100,
increment = 1,
defaultValue = 50,
mirrorInput = false,
} = this.props.options
const {container, input, range} = props.style
return (
h('.flex-row', {
style: container,
}, [
h('input', {
type: 'range',
name: name,
min: min,
max: max,
step: increment,
style: range,
value: state.value || defaultValue,
onChange: mirrorInput ? this.mirrorInputs.bind(this) : onInput,
}),
// Mirrored input for range
mirrorInput ? h('input.large-input', {
type: 'number',
name: `${name}Mirror`,
min: min,
max: max,
value: state.value || defaultValue,
step: increment,
style: input,
onChange: this.mirrorInputs.bind(this),
}) : null,
])
)
}
RangeSlider.prototype.mirrorInputs = function (event) {
this.setState({value: event.target.value})
}

@ -1,306 +0,0 @@
const PersistentForm = require('../../lib/persistent-form')
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const actions = require('../../../ui/app/actions')
const isValidAddress = require('../util').isValidAddress
module.exports = connect(mapStateToProps)(ShapeshiftForm)
function mapStateToProps (state) {
return {
warning: state.appState.warning,
isSubLoading: state.appState.isSubLoading,
}
}
inherits(ShapeshiftForm, PersistentForm)
function ShapeshiftForm () {
PersistentForm.call(this)
this.persistentFormParentId = 'shapeshift-buy-form'
}
ShapeshiftForm.prototype.render = function () {
return this.renderMain()
}
ShapeshiftForm.prototype.renderMain = function () {
const marketinfo = this.props.buyView.formView.marketinfo
const coinOptions = this.props.buyView.formView.coinOptions
var coin = marketinfo.pair.split('_')[0].toUpperCase()
return h('.flex-column', {
style: {
position: 'relative',
padding: '25px',
paddingTop: '5px',
width: '90%',
minHeight: '215px',
alignItems: 'center',
overflowY: 'auto',
},
}, [
h('.flex-row', {
style: {
justifyContent: 'center',
alignItems: 'baseline',
height: '42px',
},
}, [
h('img', {
src: coinOptions[coin].image,
width: '25px',
height: '25px',
style: {
marginRight: '5px',
},
}),
h('.input-container', {
position: 'relative',
}, [
h('input#fromCoin.buy-inputs.ex-coins', {
type: 'text',
list: 'coinList',
autoFocus: true,
dataset: {
persistentFormId: 'input-coin',
},
style: {
boxSizing: 'border-box',
},
onChange: this.handleLiveInput.bind(this),
defaultValue: 'BTC',
}),
this.renderCoinList(),
h('i.fa.fa-pencil-square-o.edit-text', {
style: {
fontSize: '12px',
color: '#F7861C',
position: 'absolute',
},
}),
]),
h('.icon-control', {
style: {
position: 'relative',
},
}, [
// Not visible on the screen, can't see it on master.
// h('i.fa.fa-refresh.fa-4.orange', {
// style: {
// bottom: '5px',
// left: '5px',
// color: '#F7861C',
// },
// onClick: this.updateCoin.bind(this),
// }),
h('i.fa.fa-chevron-right.fa-4.orange', {
style: {
position: 'absolute',
bottom: '35%',
left: '0%',
color: '#F7861C',
},
onClick: this.updateCoin.bind(this),
}),
]),
h('#toCoin.ex-coins', marketinfo.pair.split('_')[1].toUpperCase()),
h('img', {
src: coinOptions[marketinfo.pair.split('_')[1].toUpperCase()].image,
width: '25px',
height: '25px',
style: {
marginLeft: '5px',
},
}),
]),
h('.flex-column', {
style: {
marginTop: '1%',
alignItems: 'flex-start',
},
}, [
this.props.warning ?
this.props.warning &&
h('span.error.flex-center', {
style: {
textAlign: 'center',
width: '229px',
height: '82px',
},
}, this.props.warning + '')
: this.renderInfo(),
this.renderRefundAddressForCoin(coin),
]),
])
}
ShapeshiftForm.prototype.renderRefundAddressForCoin = function (coin) {
return h(this.activeToggle('.input-container'), {
style: {
marginTop: '1%',
},
}, [
h('div', `${coin} Address:`),
h('input#fromCoinAddress.buy-inputs', {
type: 'text',
placeholder: `Your ${coin} Refund Address`,
dataset: {
persistentFormId: 'refund-address',
},
style: {
boxSizing: 'border-box',
width: '227px',
height: '30px',
padding: ' 5px ',
},
}),
h('i.fa.fa-pencil-square-o.edit-text', {
style: {
fontSize: '12px',
color: '#F7861C',
position: 'absolute',
},
}),
h('div.flex-row', {
style: {
justifyContent: 'flex-start',
},
}, [
h('button', {
onClick: this.shift.bind(this),
style: {
marginTop: '1%',
},
},
'Submit'),
]),
])
}
ShapeshiftForm.prototype.shift = function () {
var props = this.props
var withdrawal = this.props.buyView.buyAddress
var returnAddress = document.getElementById('fromCoinAddress').value
var pair = this.props.buyView.formView.marketinfo.pair
var data = {
'withdrawal': withdrawal,
'pair': pair,
'returnAddress': returnAddress,
// Public api key
'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6',
}
var message = [
`Deposit Limit: ${props.buyView.formView.marketinfo.limit}`,
`Deposit Minimum:${props.buyView.formView.marketinfo.minimum}`,
]
if (isValidAddress(withdrawal)) {
this.props.dispatch(actions.coinShiftRquest(data, message))
}
}
ShapeshiftForm.prototype.renderCoinList = function () {
var list = Object.keys(this.props.buyView.formView.coinOptions).map((item) => {
return h('option', {
value: item,
}, item)
})
return h('datalist#coinList', {
onClick: (event) => {
event.preventDefault()
},
}, list)
}
ShapeshiftForm.prototype.updateCoin = function (event) {
event.preventDefault()
const props = this.props
var coinOptions = this.props.buyView.formView.coinOptions
var coin = document.getElementById('fromCoin').value
if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') {
var message = 'Not a valid coin'
return props.dispatch(actions.displayWarning(message))
} else {
return props.dispatch(actions.pairUpdate(coin))
}
}
ShapeshiftForm.prototype.handleLiveInput = function () {
const props = this.props
var coinOptions = this.props.buyView.formView.coinOptions
var coin = document.getElementById('fromCoin').value
if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') {
return null
} else {
return props.dispatch(actions.pairUpdate(coin))
}
}
ShapeshiftForm.prototype.renderInfo = function () {
const marketinfo = this.props.buyView.formView.marketinfo
const coinOptions = this.props.buyView.formView.coinOptions
var coin = marketinfo.pair.split('_')[0].toUpperCase()
return h('span', {
style: {
},
}, [
h('h3.flex-row.text-transform-uppercase', {
style: {
color: '#868686',
paddingTop: '4px',
justifyContent: 'space-around',
textAlign: 'center',
fontSize: '17px',
},
}, `Market Info for ${marketinfo.pair.replace('_', ' to ').toUpperCase()}:`),
h('.marketinfo', ['Status : ', `${coinOptions[coin].status}`]),
h('.marketinfo', ['Exchange Rate: ', `${marketinfo.rate}`]),
h('.marketinfo', ['Limit: ', `${marketinfo.limit}`]),
h('.marketinfo', ['Minimum : ', `${marketinfo.minimum}`]),
])
}
ShapeshiftForm.prototype.activeToggle = function (elementType) {
if (!this.props.buyView.formView.response || this.props.warning) return elementType
return `${elementType}.inactive`
}
ShapeshiftForm.prototype.renderLoading = function () {
return h('span', {
style: {
position: 'absolute',
left: '70px',
bottom: '194px',
background: 'transparent',
width: '229px',
height: '82px',
display: 'flex',
justifyContent: 'center',
},
}, [
h('img', {
style: {
width: '60px',
},
src: 'images/loading.svg',
}),
])
}

@ -1,204 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const { DateTime } = require('luxon')
const explorerLink = require('etherscan-link').createExplorerLink
const actions = require('../../../ui/app/actions')
const addressSummary = require('../util').addressSummary
const CopyButton = require('./copyButton')
const EthBalance = require('./eth-balance')
const Tooltip = require('./tooltip')
module.exports = connect(mapStateToProps)(ShiftListItem)
function mapStateToProps (state) {
return {
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
}
}
inherits(ShiftListItem, Component)
function ShiftListItem () {
Component.call(this)
}
ShiftListItem.prototype.render = function () {
return (
h('.transaction-list-item.flex-row', {
style: {
paddingTop: '20px',
paddingBottom: '20px',
justifyContent: 'space-around',
alignItems: 'center',
},
}, [
h('div', {
style: {
width: '0px',
position: 'relative',
bottom: '19px',
},
}, [
h('img', {
src: 'https://info.shapeshift.io/sites/default/files/logo.png',
style: {
height: '35px',
width: '132px',
position: 'absolute',
clip: 'rect(0px,23px,34px,0px)',
},
}),
]),
this.renderInfo(),
this.renderUtilComponents(),
])
)
}
function formatDate (date) {
return DateTime.fromMillis(date).toFormat('MMMM d y T')
}
ShiftListItem.prototype.renderUtilComponents = function () {
var props = this.props
const { conversionRate, currentCurrency } = props
switch (props.response.status) {
case 'no_deposits':
return h('.flex-row', [
h(CopyButton, {
value: this.props.depositAddress,
}),
h(Tooltip, {
title: 'QR Code',
}, [
h('i.fa.fa-qrcode.pointer.pop-hover', {
onClick: () => props.dispatch(actions.reshowQrCode(props.depositAddress, props.depositType)),
style: {
margin: '5px',
marginLeft: '23px',
marginRight: '12px',
fontSize: '20px',
color: '#F7861C',
},
}),
]),
])
case 'received':
return h('.flex-row')
case 'complete':
return h('.flex-row', [
h(CopyButton, {
value: this.props.response.transaction,
}),
h(EthBalance, {
value: `${props.response.outgoingCoin}`,
conversionRate,
currentCurrency,
width: '55px',
shorten: true,
needsParse: false,
incoming: true,
style: {
fontSize: '15px',
color: '#01888C',
},
}),
])
case 'failed':
return ''
default:
return ''
}
}
ShiftListItem.prototype.renderInfo = function () {
var props = this.props
switch (props.response.status) {
case 'no_deposits':
return h('.flex-column', {
style: {
width: '200px',
overflow: 'hidden',
},
}, [
h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
width: '100%',
},
}, `${props.depositType} to ETH via ShapeShift`),
h('div', 'No deposits received'),
h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
width: '100%',
},
}, formatDate(props.time)),
])
case 'received':
return h('.flex-column', {
style: {
width: '200px',
overflow: 'hidden',
},
}, [
h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
width: '100%',
},
}, `${props.depositType} to ETH via ShapeShift`),
h('div', 'Conversion in progress'),
h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
width: '100%',
},
}, formatDate(props.time)),
])
case 'complete':
var url = explorerLink(props.response.transaction, parseInt('1'))
return h('.flex-column.pointer', {
style: {
width: '200px',
overflow: 'hidden',
},
onClick: () => global.platform.openWindow({ url }),
}, [
h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
width: '100%',
},
}, 'From ShapeShift'),
h('div', formatDate(props.time)),
h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
width: '100%',
},
}, addressSummary(props.response.transaction)),
])
case 'failed':
return h('span.error', '(Failed)')
default:
return ''
}
}

@ -1,37 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
module.exports = TabBar
inherits(TabBar, Component)
function TabBar () {
Component.call(this)
}
TabBar.prototype.render = function () {
const props = this.props
const state = this.state || {}
const { tabs = [], defaultTab, tabSelected } = props
const { subview = defaultTab } = state
return (
h('.flex-row.space-around.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
paddingTop: '4px',
minHeight: '30px',
},
}, tabs.map((tab) => {
const { key, content } = tab
return h(subview === key ? '.activeForm' : '.inactiveForm.pointer', {
onClick: () => {
this.setState({ subview: key })
tabSelected(key)
},
}, content)
}))
)
}

@ -1,18 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
module.exports = NewComponent
inherits(NewComponent, Component)
function NewComponent () {
Component.call(this)
}
NewComponent.prototype.render = function () {
const props = this.props
return (
h('span', props.message)
)
}

@ -1,72 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Identicon = require('./identicon')
const prefixForNetwork = require('../../lib/etherscan-prefix-for-network')
module.exports = TokenCell
inherits(TokenCell, Component)
function TokenCell () {
Component.call(this)
}
TokenCell.prototype.render = function () {
const props = this.props
const { address, symbol, string, network, userAddress } = props
return (
h('li.token-cell', {
style: { cursor: network === '1' ? 'pointer' : 'default' },
onClick: this.view.bind(this, address, userAddress, network),
}, [
h(Identicon, {
diameter: 50,
address,
network,
}),
h('h3', `${string || 0} ${symbol}`),
h('span', { style: { flex: '1 0 auto' } }),
/*
h('button', {
onClick: this.send.bind(this, address),
}, 'SEND'),
*/
])
)
}
TokenCell.prototype.send = function (address, event) {
event.preventDefault()
event.stopPropagation()
const url = tokenFactoryFor(address)
if (url) {
navigateTo(url)
}
}
TokenCell.prototype.view = function (address, userAddress, network, event) {
const url = etherscanLinkFor(address, userAddress, network)
if (url) {
navigateTo(url)
}
}
function navigateTo (url) {
global.platform.openWindow({ url })
}
function etherscanLinkFor (tokenAddress, address, network) {
const prefix = prefixForNetwork(network)
return `https://${prefix}etherscan.io/token/${tokenAddress}?a=${address}`
}
function tokenFactoryFor (tokenAddress) {
return `https://tokenfactory.surge.sh/#/token/${tokenAddress}`
}

@ -1,205 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const TokenTracker = require('eth-token-tracker')
const TokenCell = require('./token-cell.js')
const log = require('loglevel')
module.exports = TokenList
inherits(TokenList, Component)
function TokenList () {
this.state = {
tokens: [],
isLoading: true,
network: null,
}
Component.call(this)
}
TokenList.prototype.render = function () {
const state = this.state
const { tokens, isLoading, error } = state
const { userAddress, network } = this.props
if (isLoading) {
return this.message('Loading')
}
if (error) {
log.error(error)
return h('.hotFix', {
style: {
padding: '80px',
},
}, [
'We had trouble loading your token balances. You can view them ',
h('span.hotFix', {
style: {
color: 'rgba(247, 134, 28, 1)',
cursor: 'pointer',
},
onClick: () => {
global.platform.openWindow({
url: `https://ethplorer.io/address/${userAddress}`,
})
},
}, 'here'),
])
}
const tokenViews = tokens.map((tokenData) => {
tokenData.network = network
tokenData.userAddress = userAddress
return h(TokenCell, tokenData)
})
return h('.full-flex-height', [
this.renderTokenStatusBar(),
h('ol.full-flex-height.flex-column', {
style: {
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
},
}, [
h('style', `
li.token-cell {
display: flex;
flex-direction: row;
align-items: center;
padding: 10px;
min-height: 50px;
}
li.token-cell > h3 {
margin-left: 12px;
}
li.token-cell:hover {
background: white;
cursor: pointer;
}
`),
...tokenViews,
h('.flex-grow'),
]),
])
}
TokenList.prototype.renderTokenStatusBar = function () {
const { tokens } = this.state
let msg
if (tokens.length === 1) {
msg = `You own 1 token`
} else if (tokens.length > 1) {
msg = `You own ${tokens.length} tokens`
} else {
msg = `No tokens found`
}
return h('div', {
style: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
minHeight: '70px',
padding: '10px',
},
}, [
h('span', msg),
h('button', {
key: 'reveal-account-bar',
onClick: (event) => {
event.preventDefault()
this.props.addToken()
},
style: {
display: 'flex',
height: '40px',
padding: '10px',
justifyContent: 'center',
alignItems: 'center',
},
}, [
'ADD TOKEN',
]),
])
}
TokenList.prototype.message = function (body) {
return h('div', {
style: {
display: 'flex',
height: '250px',
alignItems: 'center',
justifyContent: 'center',
padding: '30px',
},
}, body)
}
TokenList.prototype.componentDidMount = function () {
this.createFreshTokenTracker()
}
TokenList.prototype.createFreshTokenTracker = function () {
if (this.tracker) {
// Clean up old trackers when refreshing:
this.tracker.stop()
this.tracker.removeListener('update', this.balanceUpdater)
this.tracker.removeListener('error', this.showError)
}
if (!global.ethereumProvider) return
const { userAddress } = this.props
this.tracker = new TokenTracker({
userAddress,
provider: global.ethereumProvider,
tokens: this.props.tokens,
pollingInterval: 8000,
})
// Set up listener instances for cleaning up
this.balanceUpdater = this.updateBalances.bind(this)
this.showError = (error) => {
this.setState({ error, isLoading: false })
}
this.tracker.on('update', this.balanceUpdater)
this.tracker.on('error', this.showError)
this.tracker.updateBalances()
.then(() => {
this.updateBalances(this.tracker.serialize())
})
.catch((reason) => {
log.error(`Problem updating balances`, reason)
this.setState({ isLoading: false })
})
}
TokenList.prototype.componentWillUpdate = function (nextProps) {
if (nextProps.network === 'loading') return
const oldNet = this.props.network
const newNet = nextProps.network
if (oldNet && newNet && newNet !== oldNet) {
this.setState({ isLoading: true })
this.createFreshTokenTracker()
}
}
TokenList.prototype.updateBalances = function (tokens) {
this.setState({ tokens, isLoading: false })
}
TokenList.prototype.componentWillUnmount = function () {
if (!this.tracker) return
this.tracker.stop()
}

@ -1,22 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const ReactTooltip = require('react-tooltip-component')
module.exports = Tooltip
inherits(Tooltip, Component)
function Tooltip () {
Component.call(this)
}
Tooltip.prototype.render = function () {
const props = this.props
const { position, title, children } = props
return h(ReactTooltip, {
position: position || 'left',
title,
fixed: true,
}, children)
}

@ -1,68 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const Tooltip = require('./tooltip')
const Identicon = require('./identicon')
module.exports = TransactionIcon
inherits(TransactionIcon, Component)
function TransactionIcon () {
Component.call(this)
}
TransactionIcon.prototype.render = function () {
const { transaction, txParams, isMsg } = this.props
switch (transaction.status) {
case 'unapproved':
return h(!isMsg ? '.unapproved-tx-icon' : 'i.fa.fa-certificate.fa-lg')
case 'rejected':
return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
style: {
width: '24px',
},
})
case 'failed':
return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
style: {
width: '24px',
},
})
case 'submitted':
return h(Tooltip, {
title: 'Pending',
position: 'right',
}, [
h('i.fa.fa-ellipsis-h', {
style: {
fontSize: '27px',
},
}),
])
}
if (isMsg) {
return h('i.fa.fa-certificate.fa-lg', {
style: {
width: '24px',
},
})
}
if (txParams.to) {
return h(Identicon, {
diameter: 24,
address: txParams.to || transaction.hash,
})
} else {
return h('i.fa.fa-file-text-o.fa-lg', {
style: {
width: '24px',
},
})
}
}

@ -1,258 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const connect = require('react-redux').connect
const EthBalance = require('./eth-balance')
const addressSummary = require('../util').addressSummary
const explorerLink = require('etherscan-link').createExplorerLink
const CopyButton = require('./copyButton')
const { DateTime } = require('luxon')
const Tooltip = require('./tooltip')
const numberToBN = require('number-to-bn')
const actions = require('../../../ui/app/actions')
const TransactionIcon = require('./transaction-list-item-icon')
const ShiftListItem = require('./shift-list-item')
const mapDispatchToProps = dispatch => {
return {
retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
}
}
module.exports = connect(null, mapDispatchToProps)(TransactionListItem)
inherits(TransactionListItem, Component)
function TransactionListItem () {
Component.call(this)
}
TransactionListItem.prototype.showRetryButton = function () {
const { transaction = {}, transactions } = this.props
const { submittedTime, txParams } = transaction
if (!txParams) {
return false
}
let currentTxSharesEarliestNonce = false
const currentNonce = txParams.nonce
const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
const currentSubmittedTxs = transactions.filter(tx => tx.status === 'submitted')
const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce &&
lastSubmittedTxWithCurrentNonce.id === transaction.id
if (currentSubmittedTxs.length > 0) {
const earliestSubmitted = currentSubmittedTxs.reduce((tx1, tx2) => {
if (tx1.submittedTime < tx2.submittedTime) return tx1
return tx2
})
currentTxSharesEarliestNonce = currentNonce === earliestSubmitted.txParams.nonce
}
return currentTxSharesEarliestNonce && currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
}
TransactionListItem.prototype.render = function () {
const { transaction, network, conversionRate, currentCurrency } = this.props
const { status } = transaction
if (transaction.key === 'shapeshift') {
if (network === '1') return h(ShiftListItem, transaction)
}
var date = formatDate(transaction.time)
let isLinkable = false
const numericNet = parseInt(network)
isLinkable = numericNet === 1 || numericNet === 3 || numericNet === 4 || numericNet === 42
var isMsg = ('msgParams' in transaction)
var isTx = ('txParams' in transaction)
var isPending = status === 'unapproved'
let txParams
if (isTx) {
txParams = transaction.txParams
} else if (isMsg) {
txParams = transaction.msgParams
}
const nonce = txParams.nonce ? numberToBN(txParams.nonce).toString(10) : ''
const isClickable = ('hash' in transaction && isLinkable) || isPending
return (
h('.transaction-list-item.flex-column', {
onClick: (event) => {
if (isPending) {
this.props.showTx(transaction.id)
}
event.stopPropagation()
if (!transaction.hash || !isLinkable) return
var url = explorerLink(transaction.hash, parseInt(network))
global.platform.openWindow({ url })
},
style: {
padding: '20px 0',
alignItems: 'center',
},
}, [
h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
style: {
width: '100%',
},
}, [
h('.identicon-wrapper.flex-column.flex-center.select-none', [
h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
]),
h(Tooltip, {
title: 'Transaction Number',
position: 'right',
}, [
h('span', {
style: {
display: 'flex',
cursor: 'normal',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '10px',
},
}, nonce),
]),
h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
domainField(txParams),
h('div', date),
recipientField(txParams, transaction, isTx, isMsg),
]),
// Places a copy button if tx is successful, else places a placeholder empty div.
transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
isTx ? h(EthBalance, {
value: txParams.value,
conversionRate,
currentCurrency,
width: '55px',
shorten: true,
showFiat: false,
style: {fontSize: '15px'},
}) : h('.flex-column'),
]),
this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', {
onClick: event => {
event.stopPropagation()
this.resubmit()
},
style: {
height: '22px',
borderRadius: '22px',
color: '#F9881B',
padding: '0 20px',
backgroundColor: '#FFE3C9',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '8px',
cursor: 'pointer',
},
}, [
h('div', {
style: {
paddingRight: '2px',
},
}, 'Taking too long?'),
h('div', {
style: {
textDecoration: 'underline',
},
}, 'Retry with a higher gas price here'),
]),
])
)
}
TransactionListItem.prototype.resubmit = function () {
const { transaction } = this.props
this.props.retryTransaction(transaction.id)
}
function domainField (txParams) {
return h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
overflow: 'hidden',
textOverflow: 'ellipsis',
width: '100%',
},
}, [
txParams.origin,
])
}
function recipientField (txParams, transaction, isTx, isMsg) {
let message
if (isMsg) {
message = 'Signature Requested'
} else if (txParams.to) {
message = addressSummary(txParams.to)
} else {
message = 'Contract Deployment'
}
return h('div', {
style: {
fontSize: 'x-small',
color: '#ABA9AA',
},
}, [
message,
renderErrorOrWarning(transaction),
])
}
function formatDate (date) {
return DateTime.fromMillis(date).toFormat('MMMM d y T')
}
function renderErrorOrWarning (transaction) {
const { status, err, warning } = transaction
// show dropped
if (status === 'dropped') {
return h('span.dropped', ' (Dropped)')
}
// show rejected
if (status === 'rejected') {
return h('span.error', ' (Rejected)')
}
// show error
if (err) {
const message = err.message || ''
return (
h(Tooltip, {
title: message,
position: 'bottom',
}, [
h(`span.error`, ` (Failed)`),
])
)
}
// show warning
if (warning) {
const message = warning.message
return h(Tooltip, {
title: message,
position: 'bottom',
}, [
h(`span.warning`, ` (Warning)`),
])
}
}

@ -1,87 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const TransactionListItem = require('./transaction-list-item')
module.exports = TransactionList
inherits(TransactionList, Component)
function TransactionList () {
Component.call(this)
}
TransactionList.prototype.render = function () {
const { transactions, network, unapprovedMsgs, conversionRate } = this.props
var shapeShiftTxList
if (network === '1') {
shapeShiftTxList = this.props.shapeShiftTxList
}
const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
.sort((a, b) => b.time - a.time)
return (
h('section.transaction-list.full-flex-height', {
style: {
justifyContent: 'center',
},
}, [
h('style', `
.transaction-list .transaction-list-item:not(:last-of-type) {
border-bottom: 1px solid #D4D4D4;
}
.transaction-list .transaction-list-item .ether-balance-label {
display: block !important;
font-size: small;
}
`),
h('.tx-list', {
style: {
overflowY: 'auto',
height: '100%',
padding: '0 25px 0 15px',
textAlign: 'center',
},
}, [
txsToRender.length
? txsToRender.map((transaction, i) => {
let key
switch (transaction.key) {
case 'shapeshift':
const { depositAddress, time } = transaction
key = `shift-tx-${depositAddress}-${time}-${i}`
break
default:
key = `tx-${transaction.id}-${i}`
}
return h(TransactionListItem, {
transaction, i, network, key,
conversionRate, transactions,
showTx: (txId) => {
this.props.viewPendingTx(txId)
},
})
})
: h('.flex-center.full-flex-height', {
style: {
flexDirection: 'column',
justifyContent: 'center',
},
}, [
h('p', {
style: {
marginTop: '50px',
},
}, 'No transaction history.'),
]),
]),
])
)
}

@ -1,69 +0,0 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const extend = require('xtend')
const { ObjectInspector } = require('react-inspector')
module.exports = TypedMessageRenderer
inherits(TypedMessageRenderer, Component)
function TypedMessageRenderer () {
Component.call(this)
}
TypedMessageRenderer.prototype.render = function () {
const props = this.props
const { value, version, style } = props
let text
switch (version) {
case 'V1':
text = renderTypedData(value)
break
case 'V3':
text = renderTypedDataV3(value)
break
}
const defaultStyle = extend({
width: '315px',
maxHeight: '210px',
resize: 'none',
border: 'none',
background: 'white',
padding: '3px',
overflow: 'scroll',
}, style)
return (
h('div.font-small', {
style: defaultStyle,
}, text)
)
}
function renderTypedData (values) {
return values.map(function (value) {
let v = value.value
if (typeof v === 'boolean') {
v = v.toString()
}
return h('div', {}, [
h('strong', {style: {display: 'block', fontWeight: 'bold'}}, String(value.name) + ':'),
h('div', {}, v),
])
})
}
function renderTypedDataV3 (values) {
const { domain, message } = JSON.parse(values)
return [
domain ? h('div', [
h('h1', 'Domain'),
h(ObjectInspector, { data: domain, expandLevel: 1, name: 'domain' }),
]) : '',
message ? h('div', [
h('h1', 'Message'),
h(ObjectInspector, { data: message, expandLevel: 1, name: 'message' }),
]) : '',
]
}

@ -1,247 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
const NetworkIndicator = require('./components/network')
const LoadingIndicator = require('./components/loading')
const txHelper = require('../lib/tx-helper')
const log = require('loglevel')
const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../app/scripts/lib/enums')
const { getEnvironmentType } = require('../../app/scripts/lib/util')
const PendingTx = require('./components/pending-tx')
const PendingMsg = require('./components/pending-msg')
const PendingPersonalMsg = require('./components/pending-personal-msg')
const PendingTypedMsg = require('./components/pending-typed-msg')
const Loading = require('./components/loading')
module.exports = connect(mapStateToProps)(ConfirmTxScreen)
function mapStateToProps (state) {
return {
identities: state.metamask.identities,
accounts: state.metamask.accounts,
selectedAddress: state.metamask.selectedAddress,
unapprovedTxs: state.metamask.unapprovedTxs,
unapprovedMsgs: state.metamask.unapprovedMsgs,
unapprovedPersonalMsgs: state.metamask.unapprovedPersonalMsgs,
unapprovedTypedMessages: state.metamask.unapprovedTypedMessages,
index: state.appState.currentView.context,
warning: state.appState.warning,
network: state.metamask.network,
provider: state.metamask.provider,
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
blockGasLimit: state.metamask.currentBlockGasLimit,
computedBalances: state.metamask.computedBalances,
}
}
inherits(ConfirmTxScreen, Component)
function ConfirmTxScreen () {
Component.call(this)
}
ConfirmTxScreen.prototype.render = function () {
const props = this.props
const { network, provider, unapprovedTxs, currentCurrency, computedBalances,
unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, conversionRate, blockGasLimit } = props
var unconfTxList = txHelper(unapprovedTxs, unapprovedMsgs, unapprovedPersonalMsgs, unapprovedTypedMessages, network)
var txData = unconfTxList[props.index] || {}
var txParams = txData.params || {}
var isNotification = getEnvironmentType(window.location.href) === ENVIRONMENT_TYPE_NOTIFICATION
log.info(`rendering a combined ${unconfTxList.length} unconf msg & txs`)
if (unconfTxList.length === 0) return h(Loading, { isLoading: true })
const unconfTxListLength = unconfTxList.length
return (
h('.flex-column.flex-grow', [
h(LoadingIndicator, {
isLoading: this.state ? !this.state.bypassLoadingScreen : txData.loadingDefaults,
loadingMessage: 'Estimating transaction cost…',
canBypass: true,
bypass: () => {
this.setState({bypassLoadingScreen: true})
},
}),
// subtitle and nav
h('.section-title.flex-row.flex-center', [
!isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: this.goHome.bind(this),
}) : null,
h('h2.page-subtitle', 'Confirm Transaction'),
isNotification ? h(NetworkIndicator, {
network: network,
provider: provider,
}) : null,
]),
h('h3', {
style: {
alignSelf: 'center',
display: unconfTxList.length > 1 ? 'block' : 'none',
},
}, [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
style: {
display: props.index === 0 ? 'none' : 'inline-block',
},
onClick: () => props.dispatch(actions.previousTx()),
}),
` ${props.index + 1} of ${unconfTxList.length} `,
h('i.fa.fa-arrow-right.fa-lg.cursor-pointer', {
style: {
display: props.index + 1 === unconfTxList.length ? 'none' : 'inline-block',
},
onClick: () => props.dispatch(actions.nextTx()),
}),
]),
warningIfExists(props.warning),
currentTxView({
// Properties
txData: txData,
key: txData.id,
selectedAddress: props.selectedAddress,
accounts: props.accounts,
identities: props.identities,
conversionRate,
currentCurrency,
blockGasLimit,
unconfTxListLength,
computedBalances,
// Actions
buyEth: this.buyEth.bind(this, txParams.from || props.selectedAddress),
sendTransaction: this.sendTransaction.bind(this),
cancelTransaction: this.cancelTransaction.bind(this, txData),
cancelAllTransactions: this.cancelAllTransactions.bind(this, unconfTxList),
signMessage: this.signMessage.bind(this, txData),
signPersonalMessage: this.signPersonalMessage.bind(this, txData),
signTypedMessage: this.signTypedMessage.bind(this, txData),
cancelMessage: this.cancelMessage.bind(this, txData),
cancelPersonalMessage: this.cancelPersonalMessage.bind(this, txData),
cancelTypedMessage: this.cancelTypedMessage.bind(this, txData),
}),
])
)
}
function currentTxView (opts) {
log.info('rendering current tx view')
const { txData } = opts
const { txParams, msgParams, type } = txData
if (txParams) {
log.debug('txParams detected, rendering pending tx')
return h(PendingTx, opts)
} else if (msgParams) {
log.debug('msgParams detected, rendering pending msg')
if (type === 'eth_sign') {
log.debug('rendering eth_sign message')
return h(PendingMsg, opts)
} else if (type === 'personal_sign') {
log.debug('rendering personal_sign message')
return h(PendingPersonalMsg, opts)
} else if (type === 'eth_signTypedData') {
log.debug('rendering eth_signTypedData message')
return h(PendingTypedMsg, opts)
}
}
}
ConfirmTxScreen.prototype.buyEth = function (address, event) {
event.preventDefault()
this.props.dispatch(actions.buyEthView(address))
}
ConfirmTxScreen.prototype.sendTransaction = function (txData, event) {
this.stopPropagation(event)
this.props.dispatch(actions.updateAndApproveTx(txData))
}
ConfirmTxScreen.prototype.cancelTransaction = function (txData, event) {
this.stopPropagation(event)
event.preventDefault()
this.props.dispatch(actions.cancelTx(txData))
}
ConfirmTxScreen.prototype.cancelAllTransactions = function (unconfTxList, event) {
this.stopPropagation(event)
event.preventDefault()
this.props.dispatch(actions.cancelAllTx(unconfTxList))
}
ConfirmTxScreen.prototype.signMessage = function (msgData, event) {
log.info('conf-tx.js: signing message')
var params = msgData.msgParams
params.metamaskId = msgData.id
this.stopPropagation(event)
this.props.dispatch(actions.signMsg(params))
}
ConfirmTxScreen.prototype.stopPropagation = function (event) {
if (event.stopPropagation) {
event.stopPropagation()
}
}
ConfirmTxScreen.prototype.signPersonalMessage = function (msgData, event) {
log.info('conf-tx.js: signing personal message')
var params = msgData.msgParams
params.metamaskId = msgData.id
this.stopPropagation(event)
this.props.dispatch(actions.signPersonalMsg(params))
}
ConfirmTxScreen.prototype.signTypedMessage = function (msgData, event) {
log.info('conf-tx.js: signing typed message')
var params = msgData.msgParams
params.metamaskId = msgData.id
this.stopPropagation(event)
this.props.dispatch(actions.signTypedMsg(params))
}
ConfirmTxScreen.prototype.cancelMessage = function (msgData, event) {
log.info('canceling message')
this.stopPropagation(event)
this.props.dispatch(actions.cancelMsg(msgData))
}
ConfirmTxScreen.prototype.cancelPersonalMessage = function (msgData, event) {
log.info('canceling personal message')
this.stopPropagation(event)
this.props.dispatch(actions.cancelPersonalMsg(msgData))
}
ConfirmTxScreen.prototype.cancelTypedMessage = function (msgData, event) {
log.info('canceling typed message')
this.stopPropagation(event)
this.props.dispatch(actions.cancelTypedMsg(msgData))
}
ConfirmTxScreen.prototype.goHome = function (event) {
this.stopPropagation(event)
this.props.dispatch(actions.goHome())
}
function warningIfExists (warning) {
if (warning &&
// Do not display user rejections on this screen:
warning.indexOf('User denied transaction signature') === -1) {
return h('.error', {
style: {
margin: 'auto',
},
}, warning)
}
}

@ -1,383 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
const infuraCurrencies = require('./infura-conversion.json').objects.sort((a, b) => {
return a.quote.name.toLocaleLowerCase().localeCompare(b.quote.name.toLocaleLowerCase())
})
const validUrl = require('valid-url')
const exportAsFile = require('./util').exportAsFile
const Modal = require('../../ui/app/components/modals/index').Modal
module.exports = connect(mapStateToProps)(ConfigScreen)
function mapStateToProps (state) {
return {
metamask: state.metamask,
warning: state.appState.warning,
}
}
inherits(ConfigScreen, Component)
function ConfigScreen () {
Component.call(this)
}
ConfigScreen.prototype.render = function () {
var state = this.props
var metamaskState = state.metamask
var warning = state.warning
return (
h('.flex-column.flex-grow', {
style: {
maxHeight: '585px',
overflowY: 'auto',
},
}, [
h(Modal, {}, []),
// subtitle and nav
h('.section-title.flex-row.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: () => {
state.dispatch(actions.goHome())
},
}),
h('h2.page-subtitle', 'Settings'),
]),
h('.error', {
style: {
display: warning ? 'block' : 'none',
padding: '0 20px',
textAlign: 'center',
},
}, warning),
// conf view
h('.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-space-around', {
style: {
padding: '20px',
overflow: 'auto',
},
}, [
currentProviderDisplay(metamaskState),
h('div', { style: {display: 'block'} }, [
h('input#new_rpc', {
placeholder: 'New RPC URL',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onKeyPress (event) {
if (event.key === 'Enter') {
var element = event.target
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}
},
}),
h('br'),
h('input#chainid', {
placeholder: 'ChainId (optional)',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onKeyPress (event) {
if (event.key === 'Enter') {
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}
},
}),
h('br'),
h('input#ticker', {
placeholder: 'Symbol (optional)',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onKeyPress (event) {
if (event.key === 'Enter') {
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}
},
}),
h('br'),
h('input#nickname', {
placeholder: 'Nickname (optional)',
style: {
width: 'inherit',
flex: '1 0 auto',
height: '30px',
margin: '8px',
},
onKeyPress (event) {
if (event.key === 'Enter') {
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
}
},
}),
h('button', {
style: {
alignSelf: 'center',
},
onClick (event) {
event.preventDefault()
var element = document.querySelector('input#new_rpc')
var newRpc = element.value
var chainid = document.querySelector('input#chainid')
var ticker = document.querySelector('input#ticker')
var nickname = document.querySelector('input#nickname')
rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state)
},
}, 'Save'),
]),
h('hr.horizontal-line'),
currentConversionInformation(metamaskState, state),
h('hr.horizontal-line'),
h('div', {
style: {
marginTop: '20px',
},
}, [
h('p', {
style: {
fontFamily: 'Montserrat Light',
fontSize: '13px',
},
}, `State logs contain your public account addresses and sent transactions.`),
h('br'),
h('button', {
style: {
alignSelf: 'center',
},
onClick (event) {
window.logStateString((err, result) => {
if (err) {
state.dispatch(actions.displayWarning('Error in retrieving state logs.'))
} else {
exportAsFile('MetaMask State Logs.json', result)
}
})
},
}, 'Download State Logs'),
]),
h('hr.horizontal-line'),
h('div', {
style: {
marginTop: '20px',
},
}, [
h('p', {
style: {
fontFamily: 'Montserrat Light',
fontSize: '13px',
},
}, 'Clear privacy data so all websites must request access to view account information again.'),
h('br'),
h('button', {
style: {
alignSelf: 'center',
},
onClick (event) {
event.preventDefault()
state.dispatch(actions.clearApprovedOrigins())
},
}, 'Clear privacy data'),
]),
h('hr.horizontal-line'),
h('div', {
style: {
marginTop: '20px',
},
}, [
h('p', {
style: {
fontFamily: 'Montserrat Light',
fontSize: '13px',
},
}, metamaskState.featureFlags.privacyMode ?
'Websites will be able to view your account information.' :
'Websites must request access to view your account information.'
),
h('br'),
h('button', {
style: {
alignSelf: 'center',
},
onClick (event) {
event.preventDefault()
state.dispatch(actions.setFeatureFlag('privacyMode', !metamaskState.featureFlags.privacyMode))
},
}, metamaskState.featureFlags.privacyMode ?
'Disable privacy mode' :
'Enable privacy mode'
),
]),
h('hr.horizontal-line'),
h('div', {
style: {
marginTop: '20px',
},
}, [
h('button', {
style: {
alignSelf: 'center',
},
onClick (event) {
event.preventDefault()
state.dispatch(actions.revealSeedConfirmation())
},
}, 'Reveal Seed Words'),
]),
h('hr.horizontal-line'),
h('div', {
style: {
marginTop: '20px',
},
}, [
h('p', {
style: {
fontFamily: 'Montserrat Light',
fontSize: '13px',
},
}, [
'Resetting is for developer use only. ',
h('a', {
href: 'https://metamask.zendesk.com/hc/en-us/articles/360015489231-Resetting-an-Account-Old-UI-',
target: '_blank',
}, 'Read more.'),
]),
h('br'),
h('button', {
style: {
alignSelf: 'center',
},
onClick (event) {
event.preventDefault()
state.dispatch(actions.resetAccount())
},
}, 'Reset Account'),
]),
]),
]),
])
)
}
function rpcValidation (newRpc, chainid, ticker = 'ETH', nickname = '', state) {
if (validUrl.isWebUri(newRpc)) {
state.dispatch(actions.setRpcTarget(newRpc, chainid, ticker, nickname))
} else {
var appendedRpc = `http://${newRpc}`
if (validUrl.isWebUri(appendedRpc)) {
state.dispatch(actions.displayWarning('URIs require the appropriate HTTP/HTTPS prefix.'))
} else {
state.dispatch(actions.displayWarning('Invalid RPC URI'))
}
}
}
function currentConversionInformation (metamaskState, state) {
var currentCurrency = metamaskState.currentCurrency
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#currentCurrency', {
onChange (event) {
event.preventDefault()
var element = document.getElementById('currentCurrency')
var newCurrency = element.value
state.dispatch(actions.setCurrentCurrency(newCurrency))
},
defaultValue: currentCurrency,
}, infuraCurrencies.map((currency) => {
return h('option', {key: currency.quote.code, value: currency.quote.code}, `${currency.quote.code.toUpperCase()} - ${currency.quote.name}`)
})
),
])
}
function currentProviderDisplay (metamaskState) {
var provider = metamaskState.provider
var title, value
switch (provider.type) {
case 'mainnet':
title = 'Current Network'
value = 'Main Ethereum Network'
break
case 'ropsten':
title = 'Current Network'
value = 'Ropsten Test Network'
break
case 'kovan':
title = 'Current Network'
value = 'Kovan Test Network'
break
case 'rinkeby':
title = 'Current Network'
value = 'Rinkeby Test Network'
break
default:
title = 'Current RPC'
value = metamaskState.provider.rpcTarget
}
return h('div', [
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, title),
h('span', value),
])
}

@ -1,21 +0,0 @@
/*
debug / dev
*/
#app-content {
border: 2px solid green;
}
#design-container {
position: absolute;
left: 360px;
top: -42px;
width: calc(100vw - 360px);
height: 100vh;
overflow: scroll;
}
#design-container img {
width: 2000px;
margin-right: 600px;
}

@ -1,372 +0,0 @@
/* cyrillic-ext */
@import url('/fonts/Font_Awesome/font-awesome.min.css');
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 100;
src: local('Roboto Thin'), local('Roboto-Thin'), url('fonts/Roboto/Roboto-Thin.ttf') format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local('Roboto Light'), local('Roboto-Light'), url('fonts/Roboto/Roboto-Light.ttf') format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local('Roboto'), local('Roboto-Regular'), url('fonts/Roboto/Roboto-Regular.ttf') format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local('Roboto Medium'), local('Roboto-Medium'), url('fonts/Roboto/Roboto-Medium.ttf') format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url('fonts/Roboto/Roboto-Bold.ttf') format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local('Roboto Black'), local('Roboto-Black'), url('fonts/Roboto/Roboto-Black.ttf') format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Montserrat Regular';
src: url('/fonts/Montserrat/Montserrat-Regular.woff') format('woff');
src: url('/fonts/Montserrat/Montserrat-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-size: 'small';
}
@font-face {
font-family: 'Montserrat Bold';
src: url('/fonts/Montserrat/Montserrat-Bold.woff') format('woff');
src: url('/fonts/Montserrat/Montserrat-Bold.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Montserrat Light';
src: url('/fonts/Montserrat/Montserrat-Light.woff') format('woff');
src: url('/fonts/Montserrat/Montserrat-Light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Montserrat UltraLight';
src: url('/fonts/Montserrat/Montserrat-UltraLight.woff') format('woff');
src: url('/fonts/Montserrat/Montserrat-UltraLight.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}

@ -1,939 +0,0 @@
/*
faint orange (textfield shades) #FAF6F0
light orange (button shades): #F5C26D
dark orange (text): #F5A623
borders/font/any gray: #4A4A4A
*/
/*
application specific styles
*/
* {
box-sizing: border-box;
}
html, body {
font-family: 'Montserrat Regular', Arial;
color: #4D4D4D;
font-weight: 300;
line-height: 1.4em;
background: #F7F7F7;
margin: 0;
padding: 0;
height: 100%;
}
html {
min-height: 500px;
}
.app-root {
overflow: hidden;
position: relative
}
.app-primary {
display: flex;
}
input:focus, textarea:focus {
outline: none;
}
.full-size {
height: 100%;
width: 100%;
}
.full-width {
width: 100%;
}
.full-height {
height: 100%;
}
.full-flex-height {
display: flex;
flex: 1 1 auto;
flex-direction: column;
}
#app-content {
overflow-x: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
button, input[type="submit"] {
font-family: 'Montserrat Bold';
outline: none;
cursor: pointer;
padding: 8px 12px;
border: none;
color: white;
transform-origin: center center;
transition: transform 50ms ease-in;
/* default orange */
background: rgba(247, 134, 28, 1);
box-shadow: 0px 3px 6px rgba(247, 134, 28, 0.36);
}
.btn-green, input[type="submit"].btn-green {
background: rgba(106, 195, 96, 1);
box-shadow: 0px 3px 6px rgba(106, 195, 96, 0.36);
}
.btn-red {
background: rgba(254, 35, 17, 1);
box-shadow: 0px 3px 6px rgba(254, 35, 17, 0.36);
}
button[disabled], input[type="submit"][disabled] {
cursor: not-allowed;
background: rgba(197, 197, 197, 1);
box-shadow: 0px 3px 6px rgba(197, 197, 197, 0.36);
}
button.spaced {
margin: 2px;
}
button:not([disabled]):hover, input[type="submit"]:not([disabled]):hover {
transform: scale(1.1);
}
button:not([disabled]):active, input[type="submit"]:not([disabled]):active {
transform: scale(0.95);
}
.grow-on-hover:hover {
transform: scale(1.05);
}
a {
text-decoration: none;
color: inherit;
}
a:hover{
color: #df6b0e;
}
/*
app
*/
.active {
color: #909090;
}
button.primary {
padding: 8px 12px;
background: #F7861C;
box-shadow: 0px 3px 6px rgba(247, 134, 28, 0.36);
color: white;
font-size: 1.1em;
font-family: 'Montserrat Regular';
text-transform: uppercase;
}
button.btn-thin {
border: 1px solid;
border-color: #4D4D4D;
color: #4D4D4D;
background: rgb(255, 174, 41);
border-radius: 4px;
min-width: 200px;
margin: 12px 0;
padding: 6px;
font-size: 13px;
}
.app-header {
padding: 6px 8px;
}
.app-header h1 {
font-family: 'Montserrat Regular';
text-transform: uppercase;
color: #AEAEAE;
}
h2.page-subtitle {
font-family: 'Montserrat Regular';
text-transform: uppercase;
color: #AEAEAE;
font-size: 1em;
margin: 12px;
}
.app-footer {
padding-bottom: 10px;
align-items: center;
}
.identicon {
height: 46px;
width: 46px;
background-size: cover;
border-radius: 100%;
border: 3px solid gray;
}
textarea.twelve-word-phrase {
padding: 12px;
width: 300px;
height: 140px;
font-size: 16px;
background: white;
resize: none;
}
.network-indicator {
display: flex;
align-items: center;
font-size: 0.6em;
}
.network-name {
width: 5.2em;
line-height: 9px;
text-rendering: geometricPrecision;
}
.check {
margin-left: 12px;
color: #F7861C;
flex: 1 0 auto;
display: flex;
justify-content: flex-end;
}
/*
app sections
*/
/* initialize */
.initialize-screen hr {
width: 60px;
margin: 12px;
border-color: #F7861C;
border-style: solid;
}
.initialize-screen label {
margin-top: 20px;
}
.initialize-screen button.create-vault {
margin-top: 40px;
}
.initialize-screen .warning {
font-size: 14px;
margin: 0 16px;
}
/* unlock */
.error {
color: #f7861c;
margin-bottom: 9px;
}
.warning {
color: #FFAE00;
}
.dropped {
color: #6195ED;
}
.lock {
width: 50px;
height: 50px;
}
.lock.locked {
transform: scale(1.5);
opacity: 0.0;
transition: opacity 400ms ease-in, transform 400ms ease-in;
}
.lock.unlocked {
transform: scale(1);
opacity: 1;
transition: opacity 500ms ease-out, transform 500ms ease-out, background 200ms ease-in;
}
.lock.locked .lock-top {
transform: scaleX(1) translateX(0);
transition: transform 250ms ease-in;
}
.lock.unlocked .lock-top {
transform: scaleX(-1) translateX(-12px);
transition: transform 250ms ease-in;
}
.lock.unlocked:hover {
border-radius: 4px;
background: #e5e5e5;
border: 1px solid #b1b1b1;
}
.lock.unlocked:active {
background: #c3c3c3;
}
.section-title .fa-arrow-left {
margin: -2px 8px 0px -8px;
}
.unlock-screen #metamask-mascot-container {
margin-top: 80px;
}
.unlock-screen h1 {
margin-top: -28px;
margin-bottom: 42px;
}
.unlock-screen input[type=password] {
width: 260px;
/*height: 36px;
margin-bottom: 24px;
padding: 8px;*/
}
.sizing-input{
font-size: 14px;
height: 30px;
padding-left: 5px;
}
.editable-label{
display: flex;
}
/* Webkit */
.unlock-screen input::-webkit-input-placeholder {
text-align: center;
font-size: 1.2em;
}
/* Firefox 18- */
.unlock-screen input:-moz-placeholder {
text-align: center;
font-size: 1.2em;
}
/* Firefox 19+ */
.unlock-screen input::-moz-placeholder {
text-align: center;
font-size: 1.2em;
}
/* IE */
.unlock-screen input:-ms-input-placeholder {
text-align: center;
font-size: 1.2em;
}
input.large-input, textarea.large-input {
/*margin-bottom: 24px;*/
padding: 8px;
}
input.large-input {
height: 36px;
}
.letter-spacey {
letter-spacing: 0.1em;
}
/* accounts */
.accounts-section {
margin: 0 0px;
}
.accounts-section .horizontal-line {
margin: 0px 18px;
}
.accounts-list-option {
height: 120px;
}
.accounts-list-option .identicon-wrapper {
width: 100px;
}
.unconftx-link {
margin-top: 24px;
cursor: pointer;
}
.unconftx-link .fa-arrow-right {
margin: 0px -8px 0px 8px;
}
/* identity panel */
.identity-panel {
font-weight: 500;
}
.identity-panel .identicon-wrapper {
margin: 4px;
margin-top: 8px;
display: flex;
align-items: center;
}
.identity-panel .identicon-wrapper span {
margin: 0 auto;
}
.identity-panel .identity-data {
margin: 8px 8px 8px 18px;
}
.identity-panel i {
margin-top: 32px;
margin-right: 6px;
color: #B9B9B9;
}
.identity-panel .arrow-right {
padding-left: 18px;
width: 42px;
min-width: 18px;
height: 100%;
}
.identity-copy.flex-column {
flex: 0.25 0 auto;
justify-content: center;
}
/* accounts screen */
.identity-section {
}
.identity-section .identity-panel {
background: #E9E9E9;
border-bottom: 1px solid #B1B1B1;
cursor: pointer;
}
.identity-section .identity-panel.selected {
background: white;
color: #F3C83E;
}
.identity-section .identity-panel.selected .identicon {
border-color: orange;
}
.identity-section .accounts-list-option:hover,
.identity-section .accounts-list-option.selected {
background:white;
}
/* account detail screen */
.account-detail-section {
display: flex;
flex-wrap: wrap;
overflow-x: hidden;
overflow-y: auto;
max-height: 585px;
flex-direction: inherit;
}
.account-detail-section .name-label {
margin-left: 15px;
}
.grow-tenx {
flex-grow: 10;
}
.name-label{
}
.unapproved-tx-icon {
height: 16px;
width: 16px;
background: rgb(47, 174, 244);
border-color: #AEAEAE;
border-radius: 13px;
}
.edit-text {
height: 100%;
visibility: hidden;
}
.editing-label {
display: flex;
justify-content: flex-start;
margin-left: 50px;
margin-bottom: 2px;
font-size: 11px;
text-rendering: geometricPrecision;
color: #F7861C;
}
.name-label:hover .edit-text {
visibility: visible;
}
/* tx confirm */
.unconftx-section input[type=password] {
height: 22px;
padding: 2px;
margin: 12px;
margin-bottom: 24px;
border-radius: 4px;
border: 2px solid #F3C83E;
background: #FAF6F0;
}
/* Send Screen */
.send-screen {
}
.send-screen section {
margin: 8px 16px;
}
.send-screen input {
width: 100%;
font-size: 12px;
}
/* Ether Balance Widget */
.ether-balance-amount {
color: #F7861C;
}
.ether-balance-label {
color: #ABA9AA;
}
/* Info screen */
.info-gray{
font-family: 'Montserrat Regular';
text-transform: uppercase;
color: #AEAEAE;
}
.icon-size{
width: 20px;
}
.info{
font-family: 'Montserrat Regular', Arial;
padding-bottom: 10px;
display: inline-block;
padding-left: 5px;
}
/* buy eth warning screen */
.custom-radios {
justify-content: space-around;
align-items: center;
}
.custom-radio-selected {
width: 17px;
height: 17px;
border: solid;
border-style: double;
border-radius: 15px;
border-width: 5px;
background: rgba(247, 134, 28, 1);
border-color: #F7F7F7;
}
.custom-radio-inactive {
width: 14px;
height: 14px;
border: solid;
border-width: 1px;
border-radius: 24px;
border-color: #AEAEAE;
}
.radio-titles {
color: rgba(247, 134, 28, 1);
}
.radio-titles-subtext {
}
.selected-exchange {
}
.buy-radio {
}
.eth-warning{
transition: opacity 400ms ease-in, transform 400ms ease-in;
}
.buy-subview{
transition: opacity 400ms ease-in, transform 400ms ease-in;
}
.input-container:hover .edit-text{
visibility: visible;
}
.buy-inputs{
font-family: 'Montserrat Light';
font-size: 13px;
height: 20px;
background: transparent;
box-sizing: border-box;
border: solid;
border-color: transparent;
border-width: 0.5px;
border-radius: 2px;
}
.input-container:hover .buy-inputs{
box-sizing: inherit;
border: solid;
border-color: #F7861C;
border-width: 0.5px;
border-radius: 2px;
}
.buy-inputs:focus{
border: solid;
border-color: #F7861C;
border-width: 0.5px;
border-radius: 2px;
}
.activeForm {
background: #F7F7F7;
border: none;
border-radius: 8px 8px 0px 0px;
width: 50%;
text-align: center;
padding-bottom: 4px;
}
.inactiveForm {
border: none;
border-radius: 8px 8px 0px 0px;
width: 50%;
text-align: center;
padding-bottom: 4px;
}
.ex-coins {
font-family: 'Montserrat Regular';
text-transform: uppercase;
text-align: center;
font-size: 33px;
width: 118px;
height: 42px;
padding: 1px;
color: #4D4D4D;
}
.marketinfo{
font-family: 'Montserrat light';
color: #AEAEAE;
font-size: 15px;
line-height: 17px;
}
#fromCoin::-webkit-calendar-picker-indicator {
display: none;
}
#coinList {
width: 400px;
height: 500px;
overflow: scroll;
}
.icon-control .fa-refresh{
visibility: hidden;
}
.icon-control:hover .fa-refresh{
visibility: visible;
}
.icon-control:hover .fa-chevron-right{
visibility: hidden;
}
.inactive {
color: #AEAEAE;
}
.inactive button{
background: #AEAEAE;
color: white;
}
.ellip-address {
overflow: hidden;
text-overflow: ellipsis;
width: 5em;
font-size: 14px;
font-family: "Montserrat Light";
margin-left: 5px;
}
.qr-header {
font-size: 25px;
margin-top: 40px;
}
.qr-message {
font-size: 12px;
color: #F7861C;
}
div.message-container > div:first-child {
margin-top: 18px;
font-size: 15px;
color: #4D4D4D;
}
.pop-hover:hover {
transform: scale(1.1);
}
.new-ui-announcement {
display: flex;
flex-direction: column;
height: 100%;
background: white;
color: #4D4D4D;
font-family: Roboto, Arial, sans-serif;
padding: 1.5rem;
}
.new-ui-announcement__announcement-header {
display: flex;
flex-direction: row;
justify-content: space-between;
padding-bottom: 1rem;
}
.new-ui-announcement__announcement-header a.close {
cursor: pointer;
font-size: 32px;
line-height: 17px;
}
.new-ui-announcement__announcement-header a.close:hover {
color: inherit;
}
.new-ui-announcement__announcement-header h1 {
color: #33A4E7;
text-transform: uppercase;
font-size: 18px;
font-weight: 400;
}
.new-ui-announcement__body {
display: flex;
flex: 1;
flex-direction: column;
font-size: 10.5pt;
font-weight: 300;
}
.new-ui-announcement__body h1 {
font-size: 22px;
font-weight: 600;
padding-bottom: 1rem;
}
.new-ui-announcement__body a {
color: #33A4E7;
}
.new-ui-announcement__body .updates-list {
padding: .5rem 1rem;
}
.new-ui-announcement__body .updates-list h2 {
font-weight: 600;
}
.new-ui-announcement__body .updates-list ul {
list-style: disc inside;
}
.new-ui-announcement__footer {
display: flex;
flex-direction: column;
align-items: center;
}
.new-ui-announcement__footer h1 {
font-family: inherit;
font-weight: 600;
}
.new-ui-announcement__footer button:hover {
transform: none;
}
.new-ui-announcement__footer button.positive {
padding: 1rem;
margin: 1rem;
background: #33A4E7;
color: white;
text-transform: uppercase;
box-shadow: none;
border-radius: 5px;
font-family: inherit;
font-size: 13px;
font-weight: 400;
width: 90%;
}
.new-ui-announcement__footer button.negative {
margin: 0;
padding: 0;
background: white;
color: #33A4E7;
font-family: inherit;
font-size: 13px;
font-weight: 400;
box-shadow: none;
}
.app-bar {
width: 100%;
display: flex;
flex-direction: column;
}
.app-bar__new-ui-banner {
background: #33A4E7;
color: white;
font-size: 12px;
line-height: 12px;
padding: 8px;
font-family: Roboto, Arial, sans-serif;
font-weight: 400;
width: 100%;
}
.banner__link {
cursor: pointer;
text-decoration: underline;
}
.notification-modal-wrapper {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
position: relative;
border: 1px solid #dedede;
box-shadow: 0 0 2px 2px #dedede;
font-family: Roboto;
}
.notification-modal-header {
background: #f6f6f6;
width: 100%;
display: flex;
justify-content: center;
padding: 30px;
font-size: 22px;
color: #1b344d;
height: 79px;
}
.notification-modal-message {
padding: 20px;
}
.notification-modal-message {
width: 100%;
display: flex;
justify-content: center;
font-size: 17px;
color: #1b344d;
}
.modal-close-x::after {
content: '\00D7';
font-size: 2em;
color: #9b9b9b;
position: absolute;
top: 25px;
right: 17.5px;
font-family: sans-serif;
cursor: pointer;
}
.notification-modal__wrapper {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
position: relative;
border: 1px solid #dedede;
box-shadow: 0 0 2px 2px #dedede;
font-family: Roboto;
}
.notification-modal__header {
background: #f6f6f6;
width: 100%;
display: flex;
justify-content: center;
padding: 30px;
font-size: 22px;
color: #1b344d;
height: 79px;
}
.notification-modal__message {
padding: 20px;
width: 100%;
display: flex;
justify-content: center;
font-size: 17px;
color: #1b344d;
}
.notification-modal__buttons {
display: flex;
justify-content: space-evenly;
width: 100%;
margin-bottom: 24px;
padding: 0px 42px;
}
.notification-modal__buttons__btn {
cursor: pointer;
}
.notification-modal__link {
color: #2f9ae0;
}

@ -1,306 +0,0 @@
/* color */
.color-orange {
color: #F7861C;
}
.color-forest {
color: #0A5448;
}
/* lib */
.full-width {
width: 100%;
}
.full-height {
height: 100%;
}
.flex-column {
display: flex;
flex-direction: column;
}
.space-between {
justify-content: space-between;
}
.space-around {
justify-content: space-around;
}
.flex-column-bottom {
display: flex;
flex-direction: column-reverse;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-space-between {
justify-content: space-between;
}
.flex-space-around {
justify-content: space-around;
}
.flex-right {
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.flex-left {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.flex-fixed {
flex: none;
}
.flex-basis-auto {
flex-basis: auto;
}
.flex-grow {
flex: 1 1 auto;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.flex-justify-center {
justify-content: center;
}
.flex-align-center {
align-items: center;
}
.flex-self-end {
align-self: flex-end;
}
.flex-self-stretch {
align-self: stretch;
}
.flex-vertical {
flex-direction: column;
}
.z-bump {
z-index: 1;
}
.select-none {
cursor: inherit;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.pointer {
cursor: pointer;
}
.cursor-pointer {
cursor: pointer;
transform-origin: center center;
transition: transform 50ms ease-in-out;
}
.cursor-pointer:hover {
transform: scale(1.1);
}
.cursor-pointer:active {
transform: scale(0.95);
}
.cursor-disabled {
cursor: not-allowed;
}
.margin-bottom-sml {
margin-bottom: 20px;
}
.margin-bottom-med {
margin-bottom: 40px;
}
.margin-right-left {
margin: 0 20px;
}
.bold {
font-weight: bold;
}
.text-transform-uppercase {
text-transform: uppercase;
}
.font-small {
font-size: 12px;
}
.font-medium {
font-size: 1.2em;
}
hr.horizontal-line {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #ccc;
margin: 1em 0;
padding: 0;
}
.hover-white:hover {
background: white;
}
.red-dot {
background: #E91550;
color: white;
border-radius: 10px;
}
.diamond {
transform: rotate(45deg);
background: #038789;
}
.hollow-diamond {
transform: rotate(45deg);
border: 3px solid #690496;
}
.golden-square {
background: #EBB33F;
}
.pending-dot {
background: red;
left: 14px;
top: 14px;
color: white;
border-radius: 10px;
height: 20px;
min-width: 20px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
z-index: 1;
}
.keyring-label {
z-index: 1;
font-size: 11px;
background: rgba(255,0,0,0.8);
color: white;
bottom: 0px;
left: -18px;
border-radius: 10px;
height: 20px;
min-width: 20px;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
}
.ether-balance {
display: flex;
align-items: center;
}
.tabSection {
min-width: 350px;
}
.menu-icon {
display: inline-block;
height: 12px;
min-width: 12px;
margin: 13px;
}
i.fa.fa-question-circle.fa-lg.menu-icon {
font-size: 18px;
}
.ether-icon {
background: rgb(0, 163, 68);
border-radius: 20px;
}
.testnet-icon {
background: #2465E1;
}
.drop-menu-item {
display: flex;
align-items: center;
}
.invisible {
visibility: hidden;
}
.one-line-concat {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.critical-error {
text-align: center;
margin-top: 20px;
color: red;
}
/*
Hacky breakpoint fix for account + tab sections
Resolves issue from @frankiebee in
https://github.com/MetaMask/metamask-extension/pull/1835
Please remove this when integrating new designs
*/
@media screen and (min-width: 575px) and (max-width: 800px) {
.account-data-subsection {
flex: 0 0 auto !important; // reset flex
margin-left: 10px !important; // create additional horizontal space
margin-right: 10px !important;
width: 40%;
}
.tabSection {
flex: 0 0 auto !important;
margin-left: 10px !important;
margin-right: 10px !important;
min-width: 285px;
width: 49%;
}
.name-label {
width: 80%;
}
}

File diff suppressed because one or more lines are too long

@ -1,48 +0,0 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

@ -1,42 +0,0 @@
/* universal */
.app-primary .main-enter {
position: absolute;
width: 100%;
}
/* center position */
.app-primary.from-right .main-enter-active,
.app-primary.from-left .main-enter-active {
overflow-x: hidden;
transform: translateX(0px);
transition: transform 300ms ease-in;
}
/* exited positions */
.app-primary.from-left .main-leave-active {
transform: translateX(360px);
transition: transform 300ms ease-in;
}
.app-primary.from-right .main-leave-active {
transform: translateX(-360px);
transition: transform 300ms ease-in;
}
/* loader transitions */
.loader-enter, .loader-leave-active {
opacity: 0.0;
transition: opacity 150 ease-in;
}
.loader-enter-active, .loader-leave {
opacity: 1.0;
transition: opacity 150 ease-in;
}
/* entering positions */
.app-primary.from-right .main-enter:not(.main-enter-active) {
transform: translateX(360px);
}
.app-primary.from-left .main-enter:not(.main-enter-active) {
transform: translateX(-360px);
}

@ -1,179 +0,0 @@
const inherits = require('util').inherits
const EventEmitter = require('events').EventEmitter
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const Mascot = require('../components/mascot')
const actions = require('../../../ui/app/actions')
const Tooltip = require('../components/tooltip')
const getCaretCoordinates = require('textarea-caret')
module.exports = connect(mapStateToProps)(InitializeMenuScreen)
inherits(InitializeMenuScreen, Component)
function InitializeMenuScreen () {
Component.call(this)
this.animationEventEmitter = new EventEmitter()
}
function mapStateToProps (state) {
return {
// state from plugin
currentView: state.appState.currentView,
warning: state.appState.warning,
}
}
InitializeMenuScreen.prototype.render = function () {
var state = this.props
switch (state.currentView.name) {
default:
return this.renderMenu(state)
}
}
// InitializeMenuScreen.prototype.componentDidMount = function(){
// document.getElementById('password-box').focus()
// }
InitializeMenuScreen.prototype.renderMenu = function (state) {
return (
h('.initialize-screen.flex-column.flex-center.flex-grow', [
h(Mascot, {
animationEventEmitter: this.animationEventEmitter,
}),
h('h1', {
style: {
fontSize: '1.3em',
textTransform: 'uppercase',
color: '#7F8082',
marginBottom: 10,
},
}, 'MetaMask'),
h('div', [
h('h3', {
style: {
fontSize: '0.8em',
color: '#7F8082',
display: 'inline',
},
}, 'Encrypt your new DEN'),
h(Tooltip, {
title: 'Your DEN is your password-encrypted storage within MetaMask.',
}, [
h('i.fa.fa-question-circle.pointer', {
style: {
fontSize: '18px',
position: 'relative',
color: 'rgb(247, 134, 28)',
top: '2px',
marginLeft: '4px',
},
}),
]),
]),
h('span.in-progress-notification', state.warning),
// password
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
placeholder: 'New Password (min 8 chars)',
onInput: this.inputChanged.bind(this),
style: {
width: 260,
marginTop: 12,
},
}),
// confirm password
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
placeholder: 'Confirm Password',
onKeyPress: this.createVaultOnEnter.bind(this),
onInput: this.inputChanged.bind(this),
style: {
width: 260,
marginTop: 16,
},
}),
h('button.primary', {
onClick: this.createNewVaultAndKeychain.bind(this),
style: {
margin: 12,
},
}, 'Create'),
h('.flex-row.flex-center.flex-grow', [
h('p.pointer', {
onClick: this.showRestoreVault.bind(this),
style: {
fontSize: '0.8em',
color: 'rgb(247, 134, 28)',
textDecoration: 'underline',
},
}, 'Import Existing DEN'),
]),
])
)
}
InitializeMenuScreen.prototype.createVaultOnEnter = function (event) {
if (event.key === 'Enter') {
event.preventDefault()
this.createNewVaultAndKeychain()
}
}
InitializeMenuScreen.prototype.componentDidMount = function () {
document.getElementById('password-box').focus()
}
InitializeMenuScreen.prototype.showRestoreVault = function () {
this.props.dispatch(actions.showRestoreVault())
}
InitializeMenuScreen.prototype.createNewVaultAndKeychain = function () {
var passwordBox = document.getElementById('password-box')
var password = passwordBox.value
var passwordConfirmBox = document.getElementById('password-box-confirm')
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) {
this.warning = 'password not long enough'
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (password !== passwordConfirm) {
this.warning = 'passwords don\'t match'
this.props.dispatch(actions.displayWarning(this.warning))
return
}
this.props.dispatch(actions.createNewVaultAndKeychain(password))
}
InitializeMenuScreen.prototype.inputChanged = function (event) {
// tell mascot to look at page action
var element = event.target
var boundingRect = element.getBoundingClientRect()
var coordinates = getCaretCoordinates(element, element.selectionEnd)
this.animationEventEmitter.emit('point', {
x: boundingRect.left + coordinates.left - element.scrollLeft,
y: boundingRect.top + coordinates.top - element.scrollTop,
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 KiB

@ -1,154 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
module.exports = connect(mapStateToProps)(InfoScreen)
function mapStateToProps (state) {
return {}
}
inherits(InfoScreen, Component)
function InfoScreen () {
Component.call(this)
}
InfoScreen.prototype.render = function () {
const state = this.props
const version = global.platform.getVersion()
return (
h('.flex-column.flex-grow', {
style: {
maxWidth: '400px',
},
}, [
// subtitle and nav
h('.section-title.flex-row.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: (event) => {
state.dispatch(actions.goHome())
},
}),
h('h2.page-subtitle', 'Info'),
]),
// main view
h('.flex-column.flex-justify-center.flex-grow.select-none', [
h('.flex-space-around', {
style: {
padding: '20px',
},
}, [
// current version number
h('.info.info-gray', [
h('div', 'Metamask'),
h('div', {
style: {
marginBottom: '10px',
},
}, `Version: ${version}`),
]),
h('div', {
style: {
marginBottom: '5px',
}},
[
h('div', [
h('a', {
href: 'https://metamask.io/privacy.html',
target: '_blank',
onClick: (event) => { this.navigateTo(event.target.href) },
}, [
h('div.info', 'Privacy Policy'),
]),
]),
h('div', [
h('a', {
href: 'https://metamask.io/terms.html',
target: '_blank',
onClick: (event) => { this.navigateTo(event.target.href) },
}, [
h('div.info', 'Terms of Use'),
]),
]),
h('div', [
h('a', {
href: 'https://metamask.io/attributions.html',
target: '_blank',
onClick: (event) => { this.navigateTo(event.target.href) },
}, [
h('div.info', 'Attributions'),
]),
]),
]
),
h('hr', {
style: {
margin: '10px 0 ',
width: '7em',
},
}),
h('div', {
style: {
paddingLeft: '30px',
}},
[
h('div.fa.fa-support', [
h('a.info', {
href: 'https://support.metamask.io',
target: '_blank',
}, 'Visit our Support Center'),
]),
h('div', [
h('a', {
href: 'https://metamask.io/',
target: '_blank',
}, [
h('img.icon-size', {
src: 'images/icon-128.png',
style: {
// IE6-9
filter: 'grayscale(100%)',
// Microsoft Edge and Firefox 35+
WebkitFilter: 'grayscale(100%)',
},
}),
h('div.info', 'Visit our web site'),
]),
]),
h('div', [
h('.fa.fa-twitter', [
h('a.info', {
href: 'https://twitter.com/metamask_io',
target: '_blank',
}, 'Follow us on Twitter'),
]),
]),
h('div.fa.fa-envelope', [
h('a.info', {
target: '_blank',
href: 'mailto:help@metamask.io?subject=Feedback',
}, 'Email us!'),
]),
]),
]),
]),
])
)
}
InfoScreen.prototype.navigateTo = function (url) {
global.platform.openWindow({ url })
}

@ -1,653 +0,0 @@
{
"objects": [
{
"symbol": "ethaud",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "aud",
"name": "Australian Dollar"
}
},
{
"symbol": "ethhkd",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "hkd",
"name": "Hong Kong Dollar"
}
},
{
"symbol": "ethsgd",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "sgd",
"name": "Singapore Dollar"
}
},
{
"symbol": "ethidr",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "idr",
"name": "Indonesian Rupiah"
}
},
{
"symbol": "ethphp",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "php",
"name": "Philippine Peso"
}
},
{
"symbol": "eth1st",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "1st",
"name": "FirstBlood"
}
},
{
"symbol": "ethadt",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "adt",
"name": "adToken"
}
},
{
"symbol": "ethadx",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "adx",
"name": "AdEx"
}
},
{
"symbol": "ethant",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "ant",
"name": "Aragon"
}
},
{
"symbol": "ethbat",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "bat",
"name": "Basic Attention Token"
}
},
{
"symbol": "ethbnt",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "bnt",
"name": "Bancor"
}
},
{
"symbol": "ethbtc",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "btc",
"name": "Bitcoin"
}
},
{
"symbol": "ethcad",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "cad",
"name": "Canadian Dollar"
}
},
{
"symbol": "ethcfi",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "cfi",
"name": "Cofound.it"
}
},
{
"symbol": "ethcrb",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "crb",
"name": "CreditBit"
}
},
{
"symbol": "ethcvc",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "cvc",
"name": "Civic"
}
},
{
"symbol": "ethdash",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "dash",
"name": "Dash"
}
},
{
"symbol": "ethdgd",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "dgd",
"name": "DigixDAO"
}
},
{
"symbol": "ethetc",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "etc",
"name": "Ethereum Classic"
}
},
{
"symbol": "etheur",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "eur",
"name": "Euro"
}
},
{
"symbol": "ethfun",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "fun",
"name": "FunFair"
}
},
{
"symbol": "ethgbp",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "gbp",
"name": "Pound Sterling"
}
},
{
"symbol": "ethgno",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "gno",
"name": "Gnosis"
}
},
{
"symbol": "ethgnt",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "gnt",
"name": "Golem"
}
},
{
"symbol": "ethgup",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "gup",
"name": "Matchpool"
}
},
{
"symbol": "ethhmq",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "hmq",
"name": "Humaniq"
}
},
{
"symbol": "ethjpy",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "jpy",
"name": "Japanese Yen"
}
},
{
"symbol": "ethlgd",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "lgd",
"name": "Legends Room"
}
},
{
"symbol": "ethlsk",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "lsk",
"name": "Lisk"
}
},
{
"symbol": "ethltc",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "ltc",
"name": "Litecoin"
}
},
{
"symbol": "ethlun",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "lun",
"name": "Lunyr"
}
},
{
"symbol": "ethmco",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "mco",
"name": "Monaco"
}
},
{
"symbol": "ethmtl",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "mtl",
"name": "Metal"
}
},
{
"symbol": "ethmyst",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "myst",
"name": "Mysterium"
}
},
{
"symbol": "ethnmr",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "nmr",
"name": "Numeraire"
}
},
{
"symbol": "ethomg",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "omg",
"name": "OmiseGO"
}
},
{
"symbol": "ethpay",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "pay",
"name": "TenX"
}
},
{
"symbol": "ethptoy",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "ptoy",
"name": "Patientory"
}
},
{
"symbol": "ethqrl",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "qrl",
"name": "Quantum-Resistant Ledger"
}
},
{
"symbol": "ethqtum",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "qtum",
"name": "Qtum"
}
},
{
"symbol": "ethrep",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "rep",
"name": "Augur"
}
},
{
"symbol": "ethrlc",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "rlc",
"name": "iEx.ec"
}
},
{
"symbol": "ethrub",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "rub",
"name": "Russian Ruble"
}
},
{
"symbol": "ethsc",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "sc",
"name": "Siacoin"
}
},
{
"symbol": "ethsngls",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "sngls",
"name": "SingularDTV"
}
},
{
"symbol": "ethsnt",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "snt",
"name": "Status"
}
},
{
"symbol": "ethsteem",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "steem",
"name": "Steem"
}
},
{
"symbol": "ethstorj",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "storj",
"name": "Storj"
}
},
{
"symbol": "ethtime",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "time",
"name": "ChronoBank"
}
},
{
"symbol": "ethtkn",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "tkn",
"name": "TokenCard"
}
},
{
"symbol": "ethtrst",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "trst",
"name": "WeTrust"
}
},
{
"symbol": "ethuah",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "uah",
"name": "Ukrainian Hryvnia"
}
},
{
"symbol": "ethusd",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "usd",
"name": "United States Dollar"
}
},
{
"symbol": "ethwings",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "wings",
"name": "Wings"
}
},
{
"symbol": "ethxem",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "xem",
"name": "NEM"
}
},
{
"symbol": "ethxlm",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "xlm",
"name": "Stellar Lumen"
}
},
{
"symbol": "ethxmr",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "xmr",
"name": "Monero"
}
},
{
"symbol": "ethxrp",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "xrp",
"name": "Ripple"
}
},
{
"symbol": "ethzec",
"base": {
"code": "eth",
"name": "Ethereum"
},
"quote": {
"code": "zec",
"name": "Zcash"
}
}
]
}

@ -1,91 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../../../ui/app/actions')
const exportAsFile = require('../../util').exportAsFile
module.exports = connect(mapStateToProps)(CreateVaultCompleteScreen)
inherits(CreateVaultCompleteScreen, Component)
function CreateVaultCompleteScreen () {
Component.call(this)
}
function mapStateToProps (state) {
return {
seed: state.appState.currentView.seedWords,
cachedSeed: state.metamask.seedWords,
}
}
CreateVaultCompleteScreen.prototype.render = function () {
var state = this.props
var seed = state.seed || state.cachedSeed || ''
return (
h('.initialize-screen.flex-column.flex-center.flex-grow', [
// // subtitle and nav
// h('.section-title.flex-row.flex-center', [
// h('h2.page-subtitle', 'Vault Created'),
// ]),
h('h3.flex-center.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
marginTop: 36,
marginBottom: 8,
width: '100%',
fontSize: '20px',
padding: 6,
},
}, [
'Vault Created',
]),
h('div', {
style: {
fontSize: '1em',
marginTop: '10px',
textAlign: 'center',
},
}, [
h('span.error', 'These 12 words are the only way to restore your MetaMask accounts.\nSave them somewhere safe and secret.'),
]),
h('textarea.twelve-word-phrase', {
readOnly: true,
value: seed,
}),
h('button.primary', {
onClick: () => this.confirmSeedWords()
.then(account => this.showAccountDetail(account)),
style: {
margin: '24px',
fontSize: '0.9em',
marginBottom: '10px',
},
}, 'I\'ve copied it somewhere safe'),
h('button.primary', {
onClick: () => exportAsFile(`MetaMask Seed Words`, seed),
style: {
margin: '10px',
fontSize: '0.9em',
},
}, 'Save Seed Words As File'),
])
)
}
CreateVaultCompleteScreen.prototype.confirmSeedWords = function () {
return this.props.dispatch(actions.confirmSeedWords())
}
CreateVaultCompleteScreen.prototype.showAccountDetail = function (account) {
return this.props.dispatch(actions.showAccountDetail(account))
}

@ -1,121 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../../../../ui/app/actions')
module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
inherits(RevealSeedConfirmation, Component)
function RevealSeedConfirmation () {
Component.call(this)
}
function mapStateToProps (state) {
return {
warning: state.appState.warning,
}
}
RevealSeedConfirmation.prototype.render = function () {
const props = this.props
return (
h('.initialize-screen.flex-column.flex-center.flex-grow', {
style: { maxWidth: '420px' },
}, [
h('h3.flex-center.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
marginBottom: 24,
width: '100%',
fontSize: '20px',
padding: 6,
},
}, [
'Reveal Seed Words',
]),
h('.div', {
style: {
display: 'flex',
flexDirection: 'column',
padding: '20px',
justifyContent: 'center',
},
}, [
h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'),
// confirmation
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
placeholder: 'Enter your password to confirm',
onKeyPress: this.checkConfirmation.bind(this),
style: {
width: 260,
marginTop: '12px',
},
}),
h('.flex-row.flex-start', {
style: {
marginTop: 30,
width: '50%',
},
}, [
// cancel
h('button.primary', {
onClick: this.goHome.bind(this),
}, 'CANCEL'),
// submit
h('button.primary', {
style: { marginLeft: '10px' },
onClick: this.revealSeedWords.bind(this),
}, 'OK'),
]),
(props.warning) && (
h('span.error', {
style: {
margin: '20px',
},
}, props.warning.split('-'))
),
props.inProgress && (
h('span.in-progress-notification', 'Generating Seed...')
),
]),
])
)
}
RevealSeedConfirmation.prototype.componentDidMount = function () {
document.getElementById('password-box').focus()
}
RevealSeedConfirmation.prototype.goHome = function () {
this.props.dispatch(actions.showConfigPage(false))
}
// create vault
RevealSeedConfirmation.prototype.checkConfirmation = function (event) {
if (event.key === 'Enter') {
event.preventDefault()
this.revealSeedWords()
}
}
RevealSeedConfirmation.prototype.revealSeedWords = function () {
var password = document.getElementById('password-box').value
this.props.dispatch(actions.requestRevealSeed(password))
}

@ -1,162 +0,0 @@
const inherits = require('util').inherits
const PersistentForm = require('../../../lib/persistent-form')
const connect = require('react-redux').connect
const h = require('react-hyperscript')
const actions = require('../../../../ui/app/actions')
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
inherits(RestoreVaultScreen, PersistentForm)
function RestoreVaultScreen () {
PersistentForm.call(this)
}
function mapStateToProps (state) {
return {
warning: state.appState.warning,
forgottenPassword: state.appState.forgottenPassword,
}
}
RestoreVaultScreen.prototype.render = function () {
var state = this.props
this.persistentFormParentId = 'restore-vault-form'
return (
h('.initialize-screen.flex-column.flex-center.flex-grow', [
h('h3.flex-center.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
marginBottom: 24,
width: '100%',
fontSize: '20px',
padding: 6,
},
}, [
'Restore Vault',
]),
// wallet seed entry
h('h3', 'Wallet Seed'),
h('textarea.twelve-word-phrase.letter-spacey', {
placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
}),
// password
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box',
placeholder: 'New Password (min 8 chars)',
dataset: {
persistentFormId: 'password',
},
style: {
width: 260,
marginTop: 12,
},
}),
// confirm password
h('input.large-input.letter-spacey', {
type: 'password',
id: 'password-box-confirm',
placeholder: 'Confirm Password',
onKeyPress: this.createOnEnter.bind(this),
dataset: {
persistentFormId: 'password-confirmation',
},
style: {
width: 260,
marginTop: 16,
},
}),
(state.warning) && (
h('span.error.in-progress-notification', state.warning)
),
// submit
h('.flex-row.flex-space-between', {
style: {
marginTop: 30,
width: '50%',
},
}, [
// cancel
h('button.primary', {
onClick: this.showInitializeMenu.bind(this),
}, 'CANCEL'),
// submit
h('button.primary', {
onClick: this.createNewVaultAndRestore.bind(this),
}, 'OK'),
]),
])
)
}
RestoreVaultScreen.prototype.showInitializeMenu = function () {
if (this.props.forgottenPassword) {
this.props.dispatch(actions.backToUnlockView())
} else {
this.props.dispatch(actions.showInitializeMenu())
}
}
RestoreVaultScreen.prototype.createOnEnter = function (event) {
if (event.key === 'Enter') {
this.createNewVaultAndRestore()
}
}
RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
// check password
var passwordBox = document.getElementById('password-box')
var password = passwordBox.value
var passwordConfirmBox = document.getElementById('password-box-confirm')
var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) {
this.warning = 'Password not long enough'
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (password !== passwordConfirm) {
this.warning = 'Passwords don\'t match'
this.props.dispatch(actions.displayWarning(this.warning))
return
}
// check seed
var seedBox = document.querySelector('textarea.twelve-word-phrase')
var seed = seedBox.value.trim()
// true if the string has more than a space between words.
if (seed.split(' ').length > 1) {
this.warning = 'there can only be a space between words'
this.props.dispatch(actions.displayWarning(this.warning))
return
}
// true if seed contains a character that is not between a-z or a space
if (!seed.match(/^[a-z ]+$/)) {
this.warning = 'seed words only have lowercase characters'
this.props.dispatch(actions.displayWarning(this.warning))
return
}
if (seed.split(' ').length !== 12) {
this.warning = 'seed phrases are 12 words long'
this.props.dispatch(actions.displayWarning(this.warning))
return
}
// submit
this.warning = null
this.props.dispatch(actions.displayWarning(this.warning))
this.props.dispatch(actions.createNewVaultAndRestore(password, seed))
}

@ -1,29 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
module.exports = connect(mapStateToProps)(NewKeychain)
function mapStateToProps (state) {
return {}
}
inherits(NewKeychain, Component)
function NewKeychain () {
Component.call(this)
}
NewKeychain.prototype.render = function () {
// const props = this.props
return (
h('div', {
style: {
background: 'blue',
},
}, [
h('h1', `Here's a list!!!!`),
])
)
}

@ -1,85 +0,0 @@
const PropTypes = require('prop-types')
const {PureComponent} = require('react')
const h = require('react-hyperscript')
const actions = require('../../ui/app/actions')
module.exports = class NewUiAnnouncement extends PureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
};
close = async () => {
await this.props.dispatch(actions.setFeatureFlag('skipAnnounceBetaUI', true))
}
switchToNewUi = async () => {
const flag = 'betaUI'
const enabled = true
await this.props.dispatch(actions.setFeatureFlag(
flag,
enabled,
))
await this.close()
global.platform.openExtensionInBrowser()
}
render () {
return (
h('div.new-ui-announcement', [
h('section.new-ui-announcement__announcement-header', [
h('h1', 'Announcement'),
h('a.close', {
onClick: this.close,
}, '×'),
]),
h('section.new-ui-announcement__body', [
h('h1', 'A New Version of MetaMask'),
h('p', [
"We're excited to announce a brand-new version of MetaMask with enhanced features and functionality.",
]),
h('div.updates-list', [
h('h2', 'Updates include'),
h('ul', [
h('li', 'New user interface'),
h('li', 'Full-screen mode'),
h('li', 'Better token support'),
h('li', 'Better gas controls'),
h('li', 'Advanced features for developers'),
h('li', 'New confirmation screens'),
h('li', 'And more!'),
]),
]),
h('p', [
'You can still use the current version of MetaMask. The new version is still in beta, ' +
'however we encourage you to try it out as we transition into this exciting new update.',
h('span', {
dangerouslySetInnerHTML: {
__html: '&nbsp;',
},
}),
h('a', {
href: 'https://medium.com/metamask/74dba32cc7f7',
onClick ({target}) {
const url = target.href
global.platform.openWindow({
url,
})
},
}, [
'Learn more.',
]),
]),
]),
h('section.new-ui-announcement__footer', [
h('h1', 'Ready to try the new MetaMask?'),
h('button.positive', {
onClick: this.switchToNewUi,
}, 'Try it now'),
h('button.negative', {
onClick: this.close,
}, 'No thanks, maybe later'),
]),
])
)
}
}

@ -1,65 +0,0 @@
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { approveProviderRequest, rejectProviderRequest } from '../../ui/app/actions'
import { connect } from 'react-redux'
class ProviderApproval extends Component {
render () {
const { approveProviderRequest, origin, tabID, rejectProviderRequest } = this.props
return (
<div className="flex-column flex-grow">
<style dangerouslySetInnerHTML={{__html: `
.provider_approval_actions {
display: flex;
justify-content: flex-end;
margin: 14px 25px;
}
.provider_approval_actions button {
margin-left: 10px;
text-transform: uppercase;
}
.provider_approval_content {
padding: 0 25px;
}
.provider_approval_origin {
font-weight: bold;
margin: 14px 0;
}
`}} />
<div className="section-title flex-row flex-center">
<i
className="fa fa-arrow-left fa-lg cursor-pointer"
onClick={() => { rejectProviderRequest(tabID) }} />
<h2 className="page-subtitle">Web3 API Request</h2>
</div>
<div className="provider_approval_content">
{"The domain listed below is requesting access to the Ethereum blockchain and to view your current account. Always double check that you're on the correct site before approving access."}
<div className="provider_approval_origin">{origin}</div>
</div>
<div className="provider_approval_actions">
<button
className="btn-green"
onClick={() => { approveProviderRequest(tabID) }}>APPROVE</button>
<button
className="cancel btn-red"
onClick={() => { rejectProviderRequest(tabID) }}>REJECT</button>
</div>
</div>
)
}
}
ProviderApproval.propTypes = {
approveProviderRequest: PropTypes.func,
origin: PropTypes.string,
tabID: PropTypes.string,
rejectProviderRequest: PropTypes.func,
}
function mapDispatchToProps (dispatch) {
return {
approveProviderRequest: tabID => dispatch(approveProviderRequest(tabID)),
rejectProviderRequest: tabID => dispatch(rejectProviderRequest(tabID)),
}
}
module.exports = connect(null, mapDispatchToProps)(ProviderApproval)

@ -1,309 +0,0 @@
const inherits = require('util').inherits
const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const Identicon = require('./components/identicon')
const actions = require('../../ui/app/actions')
const util = require('./util')
const numericBalance = require('./util').numericBalance
const addressSummary = require('./util').addressSummary
const isHex = require('./util').isHex
const EthBalance = require('./components/eth-balance')
const EnsInput = require('./components/ens-input')
const ethUtil = require('ethereumjs-util')
module.exports = connect(mapStateToProps)(SendTransactionScreen)
function mapStateToProps (state) {
var result = {
address: state.metamask.selectedAddress,
accounts: state.metamask.accounts,
identities: state.metamask.identities,
warning: state.appState.warning,
network: state.metamask.network,
addressBook: state.metamask.addressBook,
conversionRate: state.metamask.conversionRate,
currentCurrency: state.metamask.currentCurrency,
}
result.error = result.warning && result.warning.split('.')[0]
result.account = result.accounts[result.address]
result.identity = result.identities[result.address]
result.balance = result.account ? numericBalance(result.account.balance) : null
return result
}
inherits(SendTransactionScreen, PersistentForm)
function SendTransactionScreen () {
PersistentForm.call(this)
}
SendTransactionScreen.prototype.render = function () {
this.persistentFormParentId = 'send-tx-form'
const props = this.props
const {
address,
account,
identity,
network,
identities,
addressBook,
conversionRate,
currentCurrency,
} = props
return (
h('.send-screen.flex-column.flex-grow', [
//
// Sender Profile
//
h('.account-data-subsection.flex-row.flex-grow', {
style: {
margin: '0 20px',
},
}, [
// header - identicon + nav
h('.flex-row.flex-space-between', {
style: {
marginTop: '15px',
},
}, [
// back button
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
onClick: this.back.bind(this),
}),
// large identicon
h('.identicon-wrapper.flex-column.flex-center.select-none', [
h(Identicon, {
diameter: 62,
address: address,
}),
]),
// invisible place holder
h('i.fa.fa-users.fa-lg.invisible', {
style: {
marginTop: '28px',
},
}),
]),
// account label
h('.flex-column', {
style: {
marginTop: '10px',
alignItems: 'flex-start',
},
}, [
h('h2.font-medium.color-forest.flex-center', {
style: {
paddingTop: '8px',
marginBottom: '8px',
},
}, identity && identity.name),
// address and getter actions
h('.flex-row.flex-center', {
style: {
marginBottom: '8px',
},
}, [
h('div', {
style: {
lineHeight: '16px',
},
}, addressSummary(address)),
]),
// balance
h('.flex-row.flex-center', [
h(EthBalance, {
value: account && account.balance,
conversionRate,
currentCurrency,
}),
]),
]),
]),
//
// Required Fields
//
h('h3.flex-center.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
marginTop: '15px',
marginBottom: '16px',
},
}, [
'Send Transaction',
]),
// error message
props.error && h('span.error.flex-center', props.error),
// 'to' field
h('section.flex-row.flex-center', [
h(EnsInput, {
name: 'address',
placeholder: 'Recipient Address',
onChange: this.recipientDidChange.bind(this),
network,
identities,
addressBook,
}),
]),
// 'amount' and send button
h('section.flex-row.flex-center', [
h('input.large-input', {
name: 'amount',
placeholder: 'Amount',
type: 'number',
style: {
marginRight: '6px',
},
dataset: {
persistentFormId: 'tx-amount',
},
}),
h('button.primary', {
onClick: this.onSubmit.bind(this),
style: {
textTransform: 'uppercase',
},
}, 'Next'),
]),
//
// Optional Fields
//
h('h3.flex-center.text-transform-uppercase', {
style: {
background: '#EBEBEB',
color: '#AEAEAE',
marginTop: '16px',
marginBottom: '16px',
},
}, [
'Transaction Data (optional)',
]),
// 'data' field
h('section.flex-column.flex-center', [
h('input.large-input', {
name: 'txData',
placeholder: '0x01234',
style: {
width: '100%',
resize: 'none',
},
dataset: {
persistentFormId: 'tx-data',
},
}),
]),
])
)
}
SendTransactionScreen.prototype.navigateToAccounts = function (event) {
event.stopPropagation()
this.props.dispatch(actions.showAccountsPage())
}
SendTransactionScreen.prototype.back = function () {
var address = this.props.address
this.props.dispatch(actions.backToAccountDetail(address))
}
SendTransactionScreen.prototype.recipientDidChange = function (recipient, nickname) {
this.setState({
recipient: recipient,
nickname: nickname,
})
}
SendTransactionScreen.prototype.onSubmit = function () {
const state = this.state || {}
const recipient = state.recipient || document.querySelector('input[name="address"]').value.replace(/^[.\s]+|[.\s]+$/g, '')
const nickname = state.nickname || ' '
const input = document.querySelector('input[name="amount"]').value
const parts = input.split('')
let message
if (isNaN(input) || input === '') {
message = 'Invalid ether value.'
return this.props.dispatch(actions.displayWarning(message))
}
if (parts[1]) {
var decimal = parts[1]
if (decimal.length > 18) {
message = 'Ether amount is too precise.'
return this.props.dispatch(actions.displayWarning(message))
}
}
const value = util.normalizeEthStringToWei(input)
const txData = document.querySelector('input[name="txData"]').value
const balance = this.props.balance
if (value.gt(balance)) {
message = 'Insufficient funds.'
return this.props.dispatch(actions.displayWarning(message))
}
if (input < 0) {
message = 'Can not send negative amounts of ETH.'
return this.props.dispatch(actions.displayWarning(message))
}
if ((util.isInvalidChecksumAddress(recipient))) {
message = 'Recipient address checksum is invalid.'
return this.props.dispatch(actions.displayWarning(message))
}
if ((!util.isValidAddress(recipient) && !txData) || (!recipient && !txData)) {
message = 'Recipient address is invalid.'
return this.props.dispatch(actions.displayWarning(message))
}
if (!isHex(ethUtil.stripHexPrefix(txData)) && txData) {
message = 'Transaction data must be hex string.'
return this.props.dispatch(actions.displayWarning(message))
}
this.props.dispatch(actions.hideWarning())
this.props.dispatch(actions.addToAddressBook(recipient, nickname))
var txParams = {
from: this.props.address,
value: '0x' + value.toString(16),
}
if (recipient) txParams.to = ethUtil.addHexPrefix(recipient)
if (txData) txParams.data = txData
this.props.dispatch(actions.signTx(txParams))
}

@ -1,59 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
module.exports = connect(mapStateToProps)(AppSettingsPage)
function mapStateToProps (state) {
return {}
}
inherits(AppSettingsPage, Component)
function AppSettingsPage () {
Component.call(this)
}
AppSettingsPage.prototype.render = function () {
return (
h('.account-detail-section.flex-column.flex-grow', [
// subtitle and nav
h('.flex-row.flex-center', [
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
onClick: this.navigateToAccounts.bind(this),
}),
h('h2.page-subtitle', 'Settings'),
]),
h('label', {
htmlFor: 'settings-rpc-endpoint',
}, 'RPC Endpoint:'),
h('input', {
type: 'url',
id: 'settings-rpc-endpoint',
onKeyPress: this.onKeyPress.bind(this),
}),
])
)
}
AppSettingsPage.prototype.componentDidMount = function () {
document.querySelector('input').focus()
}
AppSettingsPage.prototype.onKeyPress = function (event) {
// get submit event
if (event.key === 'Enter') {
// this.submitPassword(event)
}
}
AppSettingsPage.prototype.navigateToAccounts = function (event) {
event.stopPropagation()
this.props.dispatch(actions.showAccountsPage())
}

@ -1,30 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
module.exports = connect(mapStateToProps)(COMPONENTNAME)
function mapStateToProps (state) {
return {}
}
inherits(COMPONENTNAME, Component)
function COMPONENTNAME () {
Component.call(this)
}
COMPONENTNAME.prototype.render = function () {
const props = this.props
return (
h('div', {
style: {
background: 'blue',
},
}, [
`Hello, ${props.sender}`,
])
)
}

@ -1,122 +0,0 @@
const inherits = require('util').inherits
const Component = require('react').Component
const h = require('react-hyperscript')
const connect = require('react-redux').connect
const actions = require('../../ui/app/actions')
const getCaretCoordinates = require('textarea-caret')
const EventEmitter = require('events').EventEmitter
const Mascot = require('./components/mascot')
module.exports = connect(mapStateToProps)(UnlockScreen)
inherits(UnlockScreen, Component)
function UnlockScreen () {
Component.call(this)
this.animationEventEmitter = new EventEmitter()
}
function mapStateToProps (state) {
return {
warning: state.appState.warning,
}
}
UnlockScreen.prototype.render = function () {
const state = this.props
const warning = state.warning
return (
h('.flex-column', {
style: {
width: 'inherit',
},
}, [
h('.unlock-screen.flex-column.flex-center.flex-grow', [
h(Mascot, {
animationEventEmitter: this.animationEventEmitter,
}),
h('h1', {
style: {
fontSize: '1.4em',
textTransform: 'uppercase',
color: '#7F8082',
},
}, 'MetaMask'),
h('input.large-input', {
type: 'password',
id: 'password-box',
placeholder: 'enter password',
style: {
},
onKeyPress: this.onKeyPress.bind(this),
onInput: this.inputChanged.bind(this),
}),
h('.error', {
style: {
display: warning ? 'block' : 'none',
padding: '0 20px',
textAlign: 'center',
},
}, warning),
h('button.primary.cursor-pointer', {
onClick: this.onSubmit.bind(this),
style: {
margin: 10,
},
}, 'Log In'),
]),
h('.flex-row.flex-center.flex-grow', [
h('p.pointer', {
onClick: () => this.props.dispatch(actions.forgotPassword()),
style: {
fontSize: '0.8em',
color: 'rgb(247, 134, 28)',
textDecoration: 'underline',
},
}, 'Restore from seed phrase'),
]),
])
)
}
UnlockScreen.prototype.componentDidMount = function () {
document.getElementById('password-box').focus()
}
UnlockScreen.prototype.onSubmit = function (event) {
const input = document.getElementById('password-box')
const password = input.value
this.props.dispatch(actions.tryUnlockMetamask(password))
}
UnlockScreen.prototype.onKeyPress = function (event) {
if (event.key === 'Enter') {
this.submitPassword(event)
}
}
UnlockScreen.prototype.submitPassword = function (event) {
var element = event.target
var password = element.value
// reset input
element.value = ''
this.props.dispatch(actions.tryUnlockMetamask(password))
}
UnlockScreen.prototype.inputChanged = function (event) {
// tell mascot to look at page action
var element = event.target
var boundingRect = element.getBoundingClientRect()
var coordinates = getCaretCoordinates(element, element.selectionEnd)
this.animationEventEmitter.emit('point', {
x: boundingRect.left + coordinates.left - element.scrollLeft,
y: boundingRect.top + coordinates.top - element.scrollTop,
})
}

@ -1,241 +0,0 @@
const ethUtil = require('ethereumjs-util')
var valueTable = {
wei: '1000000000000000000',
kwei: '1000000000000000',
mwei: '1000000000000',
gwei: '1000000000',
szabo: '1000000',
finney: '1000',
ether: '1',
kether: '0.001',
mether: '0.000001',
gether: '0.000000001',
tether: '0.000000000001',
}
var bnTable = {}
for (var currency in valueTable) {
bnTable[currency] = new ethUtil.BN(valueTable[currency], 10)
}
module.exports = {
valuesFor: valuesFor,
addressSummary: addressSummary,
miniAddressSummary: miniAddressSummary,
isAllOneCase: isAllOneCase,
isValidAddress: isValidAddress,
numericBalance: numericBalance,
parseBalance: parseBalance,
formatBalance: formatBalance,
generateBalanceObject: generateBalanceObject,
dataSize: dataSize,
readableDate: readableDate,
normalizeToWei: normalizeToWei,
normalizeEthStringToWei: normalizeEthStringToWei,
normalizeNumberToWei: normalizeNumberToWei,
valueTable: valueTable,
bnTable: bnTable,
isHex: isHex,
exportAsFile: exportAsFile,
isInvalidChecksumAddress,
}
function valuesFor (obj) {
if (!obj) return []
return Object.keys(obj)
.map(function (key) { return obj[key] })
}
function addressSummary (address, firstSegLength = 10, lastSegLength = 4, includeHex = true) {
if (!address) return ''
let checked = ethUtil.toChecksumAddress(address)
if (!includeHex) {
checked = ethUtil.stripHexPrefix(checked)
}
return checked ? checked.slice(0, firstSegLength) + '...' + checked.slice(checked.length - lastSegLength) : '...'
}
function miniAddressSummary (address) {
if (!address) return ''
var checked = ethUtil.toChecksumAddress(address)
return checked ? checked.slice(0, 4) + '...' + checked.slice(-4) : '...'
}
function isValidAddress (address) {
var prefixed = ethUtil.addHexPrefix(address)
if (address === '0x0000000000000000000000000000000000000000') return false
return (isAllOneCase(prefixed) && ethUtil.isValidAddress(prefixed)) || ethUtil.isValidChecksumAddress(prefixed)
}
function isInvalidChecksumAddress (address) {
var prefixed = ethUtil.addHexPrefix(address)
if (address === '0x0000000000000000000000000000000000000000') return false
return !isAllOneCase(prefixed) && !ethUtil.isValidChecksumAddress(prefixed) && ethUtil.isValidAddress(prefixed)
}
function isAllOneCase (address) {
if (!address) return true
var lower = address.toLowerCase()
var upper = address.toUpperCase()
return address === lower || address === upper
}
// Takes wei Hex, returns wei BN, even if input is null
function numericBalance (balance) {
if (!balance) return new ethUtil.BN(0, 16)
var stripped = ethUtil.stripHexPrefix(balance)
return new ethUtil.BN(stripped, 16)
}
// Takes hex, returns [beforeDecimal, afterDecimal]
function parseBalance (balance) {
var beforeDecimal, afterDecimal
const wei = numericBalance(balance)
var weiString = wei.toString()
const trailingZeros = /0+$/
beforeDecimal = weiString.length > 18 ? weiString.slice(0, weiString.length - 18) : '0'
afterDecimal = ('000000000000000000' + wei).slice(-18).replace(trailingZeros, '')
if (afterDecimal === '') { afterDecimal = '0' }
return [beforeDecimal, afterDecimal]
}
// Takes wei hex, returns an object with three properties.
// Its "formatted" property is what we generally use to render values.
function formatBalance (balance, decimalsToKeep, needsParse = true, ticker = 'ETH') {
var parsed = needsParse ? parseBalance(balance) : balance.split('.')
var beforeDecimal = parsed[0]
var afterDecimal = parsed[1]
var formatted = 'None'
if (decimalsToKeep === undefined) {
if (beforeDecimal === '0') {
if (afterDecimal !== '0') {
var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits
if (sigFigs) { afterDecimal = sigFigs[0] }
formatted = '0.' + afterDecimal + ` ${ticker}`
}
} else {
formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ` ${ticker}`
}
} else {
afterDecimal += Array(decimalsToKeep).join('0')
formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ` ${ticker}`
}
return formatted
}
function generateBalanceObject (formattedBalance, decimalsToKeep = 1) {
var balance = formattedBalance.split(' ')[0]
var label = formattedBalance.split(' ')[1]
var beforeDecimal = balance.split('.')[0]
var afterDecimal = balance.split('.')[1]
var shortBalance = shortenBalance(balance, decimalsToKeep)
if (beforeDecimal === '0' && afterDecimal.substr(0, 5) === '00000') {
// eslint-disable-next-line eqeqeq
if (afterDecimal == 0) {
balance = '0'
} else {
balance = '<1.0e-5'
}
} else if (beforeDecimal !== '0') {
balance = `${beforeDecimal}.${afterDecimal.slice(0, decimalsToKeep)}`
}
return { balance, label, shortBalance }
}
function shortenBalance (balance, decimalsToKeep = 1) {
var truncatedValue
var convertedBalance = parseFloat(balance)
if (convertedBalance > 1000000) {
truncatedValue = (balance / 1000000).toFixed(decimalsToKeep)
return `${truncatedValue}m`
} else if (convertedBalance > 1000) {
truncatedValue = (balance / 1000).toFixed(decimalsToKeep)
return `${truncatedValue}k`
} else if (convertedBalance === 0) {
return '0'
} else if (convertedBalance < 0.001) {
return '<0.001'
} else if (convertedBalance < 1) {
var stringBalance = convertedBalance.toString()
if (stringBalance.split('.')[1].length > 3) {
return convertedBalance.toFixed(3)
} else {
return stringBalance
}
} else {
return convertedBalance.toFixed(decimalsToKeep)
}
}
function dataSize (data) {
var size = data ? ethUtil.stripHexPrefix(data).length : 0
return size + ' bytes'
}
// Takes a BN and an ethereum currency name,
// returns a BN in wei
function normalizeToWei (amount, currency) {
try {
return amount.mul(bnTable.wei).div(bnTable[currency])
} catch (e) {}
return amount
}
function normalizeEthStringToWei (str) {
const parts = str.split('.')
let eth = new ethUtil.BN(parts[0], 10).mul(bnTable.wei)
if (parts[1]) {
var decimal = parts[1]
while (decimal.length < 18) {
decimal += '0'
}
const decimalBN = new ethUtil.BN(decimal, 10)
eth = eth.add(decimalBN)
}
return eth
}
var multiple = new ethUtil.BN('10000', 10)
function normalizeNumberToWei (n, currency) {
var enlarged = n * 10000
var amount = new ethUtil.BN(String(enlarged), 10)
return normalizeToWei(amount, currency).div(multiple)
}
function readableDate (ms) {
var date = new Date(ms)
var month = date.getMonth()
var day = date.getDate()
var year = date.getFullYear()
var hours = date.getHours()
var minutes = '0' + date.getMinutes()
var seconds = '0' + date.getSeconds()
var dateStr = `${month}/${day}/${year}`
var time = `${hours}:${minutes.substr(-2)}:${seconds.substr(-2)}`
return `${dateStr} ${time}`
}
function isHex (str) {
return Boolean(str.match(/^(0x)?[0-9a-fA-F]+$/))
}
function exportAsFile (filename, data) {
// source: https://stackoverflow.com/a/33542499 by Ludovic Feltz
const blob = new Blob([data], {type: 'text/csv'})
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename)
} else {
const elem = window.document.createElement('a')
elem.target = '_blank'
elem.href = window.URL.createObjectURL(blob)
elem.download = filename
document.body.appendChild(elem)
elem.click()
document.body.removeChild(elem)
}
}

@ -1,29 +0,0 @@
const fs = require('fs')
const path = require('path')
module.exports = bundleCss
var cssFiles = {
'fonts.css': fs.readFileSync(path.join(__dirname, '/app/css/fonts.css'), 'utf8'),
'reset.css': fs.readFileSync(path.join(__dirname, '/app/css/reset.css'), 'utf8'),
'lib.css': fs.readFileSync(path.join(__dirname, '/app/css/lib.css'), 'utf8'),
'index.css': fs.readFileSync(path.join(__dirname, '/app/css/index.css'), 'utf8'),
'transitions.css': fs.readFileSync(path.join(__dirname, '/app/css/transitions.css'), 'utf8'),
'react-tooltip-component.css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-tooltip-component', 'dist', 'react-tooltip-component.css'), 'utf8'),
'react-css': fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'react-select', 'dist', 'react-select.css'), 'utf8'),
}
function bundleCss () {
var cssBundle = Object.keys(cssFiles).reduce(function (bundle, fileName) {
var fileContent = cssFiles[fileName]
var output = String()
output += '/*========== ' + fileName + ' ==========*/\n\n'
output += fileContent
output += '\n\n'
return bundle + output
}, String())
return cssBundle
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save