mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge branch 'master' into EdgeCompatibility
This commit is contained in:
commit
e85418b11a
@ -1 +1,2 @@
|
|||||||
app/scripts/lib/extension-instance.js
|
app/scripts/lib/extension-instance.js
|
||||||
|
ui/app/conversion-util.js
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ builds/
|
|||||||
notes.txt
|
notes.txt
|
||||||
app/.DS_Store
|
app/.DS_Store
|
||||||
development/bundle.js
|
development/bundle.js
|
||||||
|
builds.zip
|
||||||
|
36
CHANGELOG.md
36
CHANGELOG.md
@ -2,7 +2,43 @@
|
|||||||
|
|
||||||
## Current Master
|
## Current Master
|
||||||
|
|
||||||
|
- Fix bug where provider menu did not allow switching to custom network from a custom network.
|
||||||
|
|
||||||
|
## 2.10.0 2016-08-29
|
||||||
|
|
||||||
|
- Changed transaction approval from notifications system to popup system.
|
||||||
|
- Add a back button to locked screen to allow restoring vault from seed words when password is forgotten.
|
||||||
|
- Forms now retain their values even when closing the popup and reopening it.
|
||||||
|
- Fixed a spelling error in provider menu.
|
||||||
|
|
||||||
|
## 2.9.2 2016-08-24
|
||||||
|
|
||||||
|
- Fixed shortcut bug from preventing installation.
|
||||||
|
|
||||||
|
## 2.9.1 2016-08-24
|
||||||
|
|
||||||
|
- Added static image as fallback for when WebGL isn't supported.
|
||||||
|
- Transaction history now has a hard limit.
|
||||||
|
- Added info link on account screen that visits Etherscan.
|
||||||
|
- Fixed bug where a message signing request would be lost if the vault was locked.
|
||||||
|
- Added shortcut to open MetaMask (Ctrl+Alt+M or Cmd+Opt/Alt+M)
|
||||||
|
- Prevent API calls in tests.
|
||||||
|
- Fixed bug where sign message confirmation would sometimes render blank.
|
||||||
|
|
||||||
|
## 2.9.0 2016-08-22
|
||||||
|
|
||||||
|
- Added ShapeShift to the transaction history
|
||||||
|
- Added affiliate key to Shapeshift requests
|
||||||
|
- Added feature to reflect current conversion rates of current vault balance.
|
||||||
|
- Modify balance display logic.
|
||||||
|
|
||||||
|
## 2.8.0 2016-08-15
|
||||||
|
|
||||||
|
- Integrate ShapeShift
|
||||||
|
- Add a form for Coinbase to specify amount to buy
|
||||||
- Fix various typos.
|
- Fix various typos.
|
||||||
|
- Make dapp-metamask connection more reliable
|
||||||
|
- Remove Ethereum Classic from provider menu.
|
||||||
|
|
||||||
## 2.7.3 2016-07-29
|
## 2.7.3 2016-07-29
|
||||||
|
|
||||||
|
10
app/_locales/es/messages.json
Normal file
10
app/_locales/es/messages.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"appName": {
|
||||||
|
"message": "MetaMask",
|
||||||
|
"description": "The name of the application"
|
||||||
|
},
|
||||||
|
"appDescription": {
|
||||||
|
"message": "Administración de identidad en Ethereum",
|
||||||
|
"description": "The description of the application"
|
||||||
|
}
|
||||||
|
}
|
10
app/_locales/es_419/messages.json
Normal file
10
app/_locales/es_419/messages.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"appName": {
|
||||||
|
"message": "MetaMask",
|
||||||
|
"description": "The name of the application"
|
||||||
|
},
|
||||||
|
"appDescription": {
|
||||||
|
"message": "Administración de identidad en Ethereum",
|
||||||
|
"description": "The description of the application"
|
||||||
|
}
|
||||||
|
}
|
10
app/_locales/zh_CN/messages.json
Normal file
10
app/_locales/zh_CN/messages.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"appName": {
|
||||||
|
"message": "MetaMask",
|
||||||
|
"description": "The name of the application"
|
||||||
|
},
|
||||||
|
"appDescription": {
|
||||||
|
"message": "以太坊身份管理",
|
||||||
|
"description": "The description of the application"
|
||||||
|
}
|
||||||
|
}
|
1
app/currencies.json
Normal file
1
app/currencies.json
Normal file
File diff suppressed because one or more lines are too long
BIN
app/images/icon-512.png
Normal file
BIN
app/images/icon-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
@ -1,10 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "MetaMask",
|
"name": "MetaMask",
|
||||||
"short_name": "Metamask",
|
"short_name": "Metamask",
|
||||||
"version": "2.7.3",
|
"version": "2.10.0",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"author": "https://metamask.io",
|
"author": "https://metamask.io",
|
||||||
"description": "Ethereum Browser Extension",
|
"description": "Ethereum Browser Extension",
|
||||||
|
"commands": {
|
||||||
|
"_execute_browser_action": {
|
||||||
|
"suggested_key": {
|
||||||
|
"windows": "Alt+Shift+M",
|
||||||
|
"mac": "Alt+Shift+M",
|
||||||
|
"chromeos": "Search+M",
|
||||||
|
"linux": "Alt+Shift+M"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "images/icon-16.png",
|
"16": "images/icon-16.png",
|
||||||
"128": "images/icon-128.png"
|
"128": "images/icon-128.png"
|
||||||
|
16
app/notification.html
Normal file
16
app/notification.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>MetaMask Notification</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app-content"></div>
|
||||||
|
<script src="./scripts/popup.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -3,9 +3,7 @@ const extend = require('xtend')
|
|||||||
const Dnode = require('dnode')
|
const Dnode = require('dnode')
|
||||||
const eos = require('end-of-stream')
|
const eos = require('end-of-stream')
|
||||||
const PortStream = require('./lib/port-stream.js')
|
const PortStream = require('./lib/port-stream.js')
|
||||||
const createUnlockRequestNotification = require('./lib/notifications.js').createUnlockRequestNotification
|
const notification = require('./lib/notifications.js')
|
||||||
const createTxNotification = require('./lib/notifications.js').createTxNotification
|
|
||||||
const createMsgNotification = require('./lib/notifications.js').createMsgNotification
|
|
||||||
const messageManager = require('./lib/message-manager')
|
const messageManager = require('./lib/message-manager')
|
||||||
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||||
const MetamaskController = require('./metamask-controller')
|
const MetamaskController = require('./metamask-controller')
|
||||||
@ -13,6 +11,7 @@ const extension = require('./lib/extension')
|
|||||||
|
|
||||||
const STORAGE_KEY = 'metamask-config'
|
const STORAGE_KEY = 'metamask-config'
|
||||||
|
|
||||||
|
|
||||||
const controller = new MetamaskController({
|
const controller = new MetamaskController({
|
||||||
// User confirmation callbacks:
|
// User confirmation callbacks:
|
||||||
showUnconfirmedMessage,
|
showUnconfirmedMessage,
|
||||||
@ -25,41 +24,15 @@ const controller = new MetamaskController({
|
|||||||
const idStore = controller.idStore
|
const idStore = controller.idStore
|
||||||
|
|
||||||
function unlockAccountMessage () {
|
function unlockAccountMessage () {
|
||||||
createUnlockRequestNotification({
|
notification.show()
|
||||||
title: 'Account Unlock Request',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUnconfirmedMessage (msgParams, msgId) {
|
function showUnconfirmedMessage (msgParams, msgId) {
|
||||||
var controllerState = controller.getState()
|
notification.show()
|
||||||
|
|
||||||
createMsgNotification({
|
|
||||||
imageifyIdenticons: false,
|
|
||||||
txData: {
|
|
||||||
msgParams: msgParams,
|
|
||||||
time: (new Date()).getTime(),
|
|
||||||
},
|
|
||||||
identities: controllerState.identities,
|
|
||||||
accounts: controllerState.accounts,
|
|
||||||
onConfirm: idStore.approveMessage.bind(idStore, msgId, noop),
|
|
||||||
onCancel: idStore.cancelMessage.bind(idStore, msgId),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
|
function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
|
||||||
var controllerState = controller.getState()
|
notification.show()
|
||||||
|
|
||||||
createTxNotification({
|
|
||||||
imageifyIdenticons: false,
|
|
||||||
txData: {
|
|
||||||
txParams: txParams,
|
|
||||||
time: (new Date()).getTime(),
|
|
||||||
},
|
|
||||||
identities: controllerState.identities,
|
|
||||||
accounts: controllerState.accounts,
|
|
||||||
onConfirm: idStore.approveTransaction.bind(idStore, txData.id, noop),
|
|
||||||
onCancel: idStore.cancelTransaction.bind(idStore, txData.id),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -68,7 +41,7 @@ function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
|
|||||||
|
|
||||||
extension.runtime.onConnect.addListener(connectRemote)
|
extension.runtime.onConnect.addListener(connectRemote)
|
||||||
function connectRemote (remotePort) {
|
function connectRemote (remotePort) {
|
||||||
var isMetaMaskInternalProcess = (remotePort.name === 'popup')
|
var isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification'
|
||||||
var portStream = new PortStream(remotePort)
|
var portStream = new PortStream(remotePort)
|
||||||
if (isMetaMaskInternalProcess) {
|
if (isMetaMaskInternalProcess) {
|
||||||
// communication with popup
|
// communication with popup
|
||||||
@ -108,7 +81,7 @@ function setupControllerConnection (stream) {
|
|||||||
dnode.on('remote', (remote) => {
|
dnode.on('remote', (remote) => {
|
||||||
// push updates to popup
|
// push updates to popup
|
||||||
controller.ethStore.on('update', controller.sendUpdate.bind(controller))
|
controller.ethStore.on('update', controller.sendUpdate.bind(controller))
|
||||||
controller.remote = remote
|
controller.listeners.push(remote)
|
||||||
idStore.on('update', controller.sendUpdate.bind(controller))
|
idStore.on('update', controller.sendUpdate.bind(controller))
|
||||||
|
|
||||||
// teardown on disconnect
|
// teardown on disconnect
|
||||||
@ -188,4 +161,3 @@ function setData (data) {
|
|||||||
window.localStorage[STORAGE_KEY] = JSON.stringify(data)
|
window.localStorage[STORAGE_KEY] = JSON.stringify(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function noop () {}
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
const MAINET_RPC_URL = 'https://mainnet.infura.io/'
|
const MAINET_RPC_URL = 'https://mainnet.infura.io/'
|
||||||
const TESTNET_RPC_URL = 'https://morden.infura.io/'
|
const TESTNET_RPC_URL = 'https://morden.infura.io/'
|
||||||
const DEFAULT_RPC_URL = TESTNET_RPC_URL
|
const DEFAULT_RPC_URL = TESTNET_RPC_URL
|
||||||
const CLASSIC_RPC_URL = 'https://mainnet-nf.infura.io/'
|
|
||||||
|
global.METAMASK_DEBUG = false
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
network: {
|
network: {
|
||||||
default: DEFAULT_RPC_URL,
|
default: DEFAULT_RPC_URL,
|
||||||
mainnet: MAINET_RPC_URL,
|
mainnet: MAINET_RPC_URL,
|
||||||
testnet: TESTNET_RPC_URL,
|
testnet: TESTNET_RPC_URL,
|
||||||
classic: CLASSIC_RPC_URL,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
|
const LocalMessageDuplexStream = require('post-message-stream')
|
||||||
const PortStream = require('./lib/port-stream.js')
|
const PortStream = require('./lib/port-stream.js')
|
||||||
const ObjectMultiplex = require('./lib/obj-multiplex')
|
const ObjectMultiplex = require('./lib/obj-multiplex')
|
||||||
const extension = require('./lib/extension')
|
const extension = require('./lib/extension')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*global Web3*/
|
/*global Web3*/
|
||||||
cleanContextForImports()
|
cleanContextForImports()
|
||||||
require('web3/dist/web3.min.js')
|
require('web3/dist/web3.min.js')
|
||||||
const LocalMessageDuplexStream = require('./lib/local-message-stream.js')
|
const LocalMessageDuplexStream = require('post-message-stream')
|
||||||
const setupDappAutoReload = require('./lib/auto-reload.js')
|
const setupDappAutoReload = require('./lib/auto-reload.js')
|
||||||
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
|
const MetamaskInpageProvider = require('./lib/inpage-provider.js')
|
||||||
restoreContextAfterImports()
|
restoreContextAfterImports()
|
||||||
@ -54,7 +54,7 @@ var __define
|
|||||||
function cleanContextForImports () {
|
function cleanContextForImports () {
|
||||||
__define = global.define
|
__define = global.define
|
||||||
try {
|
try {
|
||||||
delete global.define
|
global.define = undefined
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
console.warn('MetaMask - global.define could not be deleted.')
|
console.warn('MetaMask - global.define could not be deleted.')
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const once = require('once')
|
const once = require('once')
|
||||||
const ensnare = require('./ensnare.js')
|
const ensnare = require('ensnare')
|
||||||
|
|
||||||
module.exports = setupDappAutoReload
|
module.exports = setupDappAutoReload
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
const Migrator = require('pojo-migrator')
|
const Migrator = require('pojo-migrator')
|
||||||
const MetamaskConfig = require('../config.js')
|
const MetamaskConfig = require('../config.js')
|
||||||
const migrations = require('./migrations')
|
const migrations = require('./migrations')
|
||||||
|
const rp = require('request-promise')
|
||||||
|
|
||||||
const TESTNET_RPC = MetamaskConfig.network.testnet
|
const TESTNET_RPC = MetamaskConfig.network.testnet
|
||||||
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
||||||
const CLASSIC_RPC = MetamaskConfig.network.classic
|
const txLimit = 40
|
||||||
|
|
||||||
/* The config-manager is a convenience object
|
/* The config-manager is a convenience object
|
||||||
* wrapping a pojo-migrator.
|
* wrapping a pojo-migrator.
|
||||||
@ -15,6 +16,8 @@ const CLASSIC_RPC = MetamaskConfig.network.classic
|
|||||||
*/
|
*/
|
||||||
module.exports = ConfigManager
|
module.exports = ConfigManager
|
||||||
function ConfigManager (opts) {
|
function ConfigManager (opts) {
|
||||||
|
this.txLimit = txLimit
|
||||||
|
|
||||||
// ConfigManager is observable and will emit updates
|
// ConfigManager is observable and will emit updates
|
||||||
this._subs = []
|
this._subs = []
|
||||||
|
|
||||||
@ -145,9 +148,6 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
|
|||||||
case 'testnet':
|
case 'testnet':
|
||||||
return TESTNET_RPC
|
return TESTNET_RPC
|
||||||
|
|
||||||
case 'classic':
|
|
||||||
return CLASSIC_RPC
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
|
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
|
||||||
}
|
}
|
||||||
@ -184,6 +184,9 @@ ConfigManager.prototype._saveTxList = function (txList) {
|
|||||||
|
|
||||||
ConfigManager.prototype.addTx = function (tx) {
|
ConfigManager.prototype.addTx = function (tx) {
|
||||||
var transactions = this.getTxList()
|
var transactions = this.getTxList()
|
||||||
|
while (transactions.length > this.txLimit - 1) {
|
||||||
|
transactions.shift()
|
||||||
|
}
|
||||||
transactions.push(tx)
|
transactions.push(tx)
|
||||||
this._saveTxList(transactions)
|
this._saveTxList(transactions)
|
||||||
}
|
}
|
||||||
@ -274,9 +277,61 @@ ConfigManager.prototype.getConfirmed = function () {
|
|||||||
return ('isConfirmed' in data) && data.isConfirmed
|
return ('isConfirmed' in data) && data.isConfirmed
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigManager.prototype.setShouldntShowWarning = function (confirmed) {
|
ConfigManager.prototype.setCurrentFiat = function (currency) {
|
||||||
var data = this.getData()
|
var data = this.getData()
|
||||||
data.isEthConfirmed = confirmed
|
data.fiatCurrency = currency
|
||||||
|
this.setData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.getCurrentFiat = function () {
|
||||||
|
var data = this.getData()
|
||||||
|
return ('fiatCurrency' in data) && data.fiatCurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.updateConversionRate = function () {
|
||||||
|
var data = this.getData()
|
||||||
|
return rp(`https://www.cryptonator.com/api/ticker/eth-${data.fiatCurrency}`)
|
||||||
|
.then((response) => {
|
||||||
|
const parsedResponse = JSON.parse(response)
|
||||||
|
this.setConversionPrice(parsedResponse.ticker.price)
|
||||||
|
this.setConversionDate(parsedResponse.timestamp)
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error('Error in conversion.', err)
|
||||||
|
this.setConversionPrice(0)
|
||||||
|
this.setConversionDate('N/A')
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.setConversionPrice = function (price) {
|
||||||
|
var data = this.getData()
|
||||||
|
data.conversionRate = Number(price)
|
||||||
|
this.setData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.setConversionDate = function (datestring) {
|
||||||
|
var data = this.getData()
|
||||||
|
data.conversionDate = datestring
|
||||||
|
this.setData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.getConversionRate = function () {
|
||||||
|
var data = this.getData()
|
||||||
|
return (('conversionRate' in data) && data.conversionRate) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.getConversionDate = function () {
|
||||||
|
var data = this.getData()
|
||||||
|
return (('conversionDate' in data) && data.conversionDate) || 'N/A'
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.setShouldntShowWarning = function () {
|
||||||
|
var data = this.getData()
|
||||||
|
if (data.isEthConfirmed) {
|
||||||
|
data.isEthConfirmed = !data.isEthConfirmed
|
||||||
|
} else {
|
||||||
|
data.isEthConfirmed = true
|
||||||
|
}
|
||||||
this.setData(data)
|
this.setData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,3 +339,37 @@ ConfigManager.prototype.getShouldntShowWarning = function () {
|
|||||||
var data = this.getData()
|
var data = this.getData()
|
||||||
return ('isEthConfirmed' in data) && data.isEthConfirmed
|
return ('isEthConfirmed' in data) && data.isEthConfirmed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.getShapeShiftTxList = function () {
|
||||||
|
var data = this.getData()
|
||||||
|
var shapeShiftTxList = data.shapeShiftTxList ? data.shapeShiftTxList : []
|
||||||
|
shapeShiftTxList.forEach((tx) => {
|
||||||
|
if (tx.response.status !== 'complete') {
|
||||||
|
var requestListner = function (request) {
|
||||||
|
tx.response = JSON.parse(this.responseText)
|
||||||
|
if (tx.response.status === 'complete') {
|
||||||
|
tx.time = new Date().getTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var shapShiftReq = new XMLHttpRequest()
|
||||||
|
shapShiftReq.addEventListener('load', requestListner)
|
||||||
|
shapShiftReq.open('GET', `https://shapeshift.io/txStat/${tx.depositAddress}`, true)
|
||||||
|
shapShiftReq.send()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.setData(data)
|
||||||
|
return shapeShiftTxList
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.createShapeShiftTx = function (depositAddress, depositType) {
|
||||||
|
var data = this.getData()
|
||||||
|
|
||||||
|
var shapeShiftTx = {depositAddress, depositType, key: 'shapeshift', time: new Date().getTime(), response: {}}
|
||||||
|
if (!data.shapeShiftTxList) {
|
||||||
|
data.shapeShiftTxList = [shapeShiftTx]
|
||||||
|
} else {
|
||||||
|
data.shapeShiftTxList.push(shapeShiftTx)
|
||||||
|
}
|
||||||
|
this.setData(data)
|
||||||
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
module.exports = ensnare
|
|
||||||
|
|
||||||
// creates a proxy object that calls cb everytime the obj's properties/fns are accessed
|
|
||||||
function ensnare (obj, cb) {
|
|
||||||
var proxy = {}
|
|
||||||
Object.keys(obj).forEach(function (key) {
|
|
||||||
var val = obj[key]
|
|
||||||
switch (typeof val) {
|
|
||||||
case 'function':
|
|
||||||
proxy[key] = function () {
|
|
||||||
cb()
|
|
||||||
val.apply(obj, arguments)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
Object.defineProperty(proxy, key, {
|
|
||||||
get: function () { cb(); return obj[key] },
|
|
||||||
set: function (val) { cb(); obj[key] = val; return val },
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return proxy
|
|
||||||
}
|
|
@ -41,6 +41,12 @@ function Extension () {
|
|||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (browser[api]) {
|
||||||
|
_this[api] = browser[api]
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_this.api = browser.extension[api]
|
_this.api = browser.extension[api]
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
@ -45,7 +45,11 @@ function IdentityStore (opts = {}) {
|
|||||||
|
|
||||||
IdentityStore.prototype.createNewVault = function (password, entropy, cb) {
|
IdentityStore.prototype.createNewVault = function (password, entropy, cb) {
|
||||||
delete this._keyStore
|
delete this._keyStore
|
||||||
|
var serializedKeystore = this.configManager.getWallet()
|
||||||
|
|
||||||
|
if (serializedKeystore) {
|
||||||
|
this.configManager.setData({})
|
||||||
|
}
|
||||||
this._createIdmgmt(password, null, entropy, (err) => {
|
this._createIdmgmt(password, null, entropy, (err) => {
|
||||||
if (err) return cb(err)
|
if (err) return cb(err)
|
||||||
|
|
||||||
@ -100,6 +104,10 @@ IdentityStore.prototype.getState = function () {
|
|||||||
unconfMsgs: messageManager.unconfirmedMsgs(),
|
unconfMsgs: messageManager.unconfirmedMsgs(),
|
||||||
messages: messageManager.getMsgList(),
|
messages: messageManager.getMsgList(),
|
||||||
selectedAddress: configManager.getSelectedAccount(),
|
selectedAddress: configManager.getSelectedAccount(),
|
||||||
|
shapeShiftTxList: configManager.getShapeShiftTxList(),
|
||||||
|
currentFiat: configManager.getCurrentFiat(),
|
||||||
|
conversionRate: configManager.getConversionRate(),
|
||||||
|
conversionDate: configManager.getConversionDate(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,8 +161,9 @@ IdentityStore.prototype.getNetwork = function (err) {
|
|||||||
this._currentState.network = 'loading'
|
this._currentState.network = 'loading'
|
||||||
return this._didUpdate()
|
return this._didUpdate()
|
||||||
}
|
}
|
||||||
|
if (global.METAMASK_DEBUG) {
|
||||||
console.log('web3.getNetwork returned ' + network)
|
console.log('web3.getNetwork returned ' + network)
|
||||||
|
}
|
||||||
this._currentState.network = network
|
this._currentState.network = network
|
||||||
this._didUpdate()
|
this._didUpdate()
|
||||||
})
|
})
|
||||||
@ -432,6 +441,7 @@ IdentityStore.prototype.tryPassword = function (password, cb) {
|
|||||||
|
|
||||||
IdentityStore.prototype._createIdmgmt = function (password, seed, entropy, cb) {
|
IdentityStore.prototype._createIdmgmt = function (password, seed, entropy, cb) {
|
||||||
const configManager = this.configManager
|
const configManager = this.configManager
|
||||||
|
|
||||||
var keyStore = null
|
var keyStore = null
|
||||||
LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
|
LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
|
||||||
if (err) return cb(err)
|
if (err) return cb(err)
|
||||||
@ -475,7 +485,9 @@ IdentityStore.prototype._restoreFromSeed = function (password, seed, derivedKey)
|
|||||||
|
|
||||||
keyStore.generateNewAddress(derivedKey, 3)
|
keyStore.generateNewAddress(derivedKey, 3)
|
||||||
configManager.setWallet(keyStore.serialize())
|
configManager.setWallet(keyStore.serialize())
|
||||||
console.log('restored from seed. saved to keystore')
|
if (global.METAMASK_DEBUG) {
|
||||||
|
console.log('restored from seed. saved to keystore')
|
||||||
|
}
|
||||||
return keyStore
|
return keyStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
const HttpProvider = require('web3/lib/web3/httpprovider')
|
|
||||||
const Streams = require('mississippi')
|
const Streams = require('mississippi')
|
||||||
const ObjectMultiplex = require('./obj-multiplex')
|
const ObjectMultiplex = require('./obj-multiplex')
|
||||||
const StreamProvider = require('web3-stream-provider')
|
const StreamProvider = require('web3-stream-provider')
|
||||||
const RemoteStore = require('./remote-store.js').RemoteStore
|
const RemoteStore = require('./remote-store.js').RemoteStore
|
||||||
const MetamaskConfig = require('../config.js')
|
|
||||||
|
|
||||||
module.exports = MetamaskInpageProvider
|
module.exports = MetamaskInpageProvider
|
||||||
|
|
||||||
@ -27,13 +25,6 @@ function MetamaskInpageProvider (connectionStream) {
|
|||||||
})
|
})
|
||||||
self.publicConfigStore = publicConfigStore
|
self.publicConfigStore = publicConfigStore
|
||||||
|
|
||||||
// connect to sync provider
|
|
||||||
self.syncProvider = createSyncProvider(publicConfigStore.get('provider'))
|
|
||||||
// subscribe to publicConfig to update the syncProvider on change
|
|
||||||
publicConfigStore.subscribe(function (state) {
|
|
||||||
self.syncProvider = createSyncProvider(state.provider)
|
|
||||||
})
|
|
||||||
|
|
||||||
// connect to async provider
|
// connect to async provider
|
||||||
var asyncProvider = new StreamProvider()
|
var asyncProvider = new StreamProvider()
|
||||||
Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) {
|
Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) {
|
||||||
@ -42,15 +33,23 @@ function MetamaskInpageProvider (connectionStream) {
|
|||||||
})
|
})
|
||||||
asyncProvider.on('error', console.error.bind(console))
|
asyncProvider.on('error', console.error.bind(console))
|
||||||
self.asyncProvider = asyncProvider
|
self.asyncProvider = asyncProvider
|
||||||
// overwrite own sendAsync method
|
// handle sendAsync requests via asyncProvider
|
||||||
self.sendAsync = asyncProvider.sendAsync.bind(asyncProvider)
|
self.sendAsync = function(payload, cb){
|
||||||
|
// rewrite request ids
|
||||||
|
var request = jsonrpcMessageTransform(payload, (message) => {
|
||||||
|
message.id = createRandomId()
|
||||||
|
return message
|
||||||
|
})
|
||||||
|
// forward to asyncProvider
|
||||||
|
asyncProvider.sendAsync(request, cb)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MetamaskInpageProvider.prototype.send = function (payload) {
|
MetamaskInpageProvider.prototype.send = function (payload) {
|
||||||
const self = this
|
const self = this
|
||||||
|
|
||||||
let selectedAddress
|
let selectedAddress
|
||||||
|
let result = null
|
||||||
var result = null
|
|
||||||
switch (payload.method) {
|
switch (payload.method) {
|
||||||
|
|
||||||
case 'eth_accounts':
|
case 'eth_accounts':
|
||||||
@ -65,9 +64,10 @@ MetamaskInpageProvider.prototype.send = function (payload) {
|
|||||||
result = selectedAddress || '0x0000000000000000000000000000000000000000'
|
result = selectedAddress || '0x0000000000000000000000000000000000000000'
|
||||||
break
|
break
|
||||||
|
|
||||||
// fallback to normal rpc
|
// throw not-supported Error
|
||||||
default:
|
default:
|
||||||
return self.syncProvider.send(payload)
|
var message = 'The MetaMask Web3 object does not support synchronous methods. See https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#all-async---think-of-metamask-as-a-light-client for details.'
|
||||||
|
throw new Error(message)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,35 +89,6 @@ MetamaskInpageProvider.prototype.isConnected = function () {
|
|||||||
|
|
||||||
// util
|
// util
|
||||||
|
|
||||||
function createSyncProvider (providerConfig) {
|
|
||||||
providerConfig = providerConfig || {}
|
|
||||||
let syncProviderUrl
|
|
||||||
|
|
||||||
if (providerConfig.rpcTarget) {
|
|
||||||
syncProviderUrl = providerConfig.rpcTarget
|
|
||||||
} else {
|
|
||||||
switch (providerConfig.type) {
|
|
||||||
case 'testnet':
|
|
||||||
syncProviderUrl = MetamaskConfig.network.testnet
|
|
||||||
break
|
|
||||||
case 'mainnet':
|
|
||||||
syncProviderUrl = MetamaskConfig.network.mainnet
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
syncProviderUrl = MetamaskConfig.network.default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const provider = new HttpProvider(syncProviderUrl)
|
|
||||||
// Stubbing out the send method to throw on sync methods:
|
|
||||||
provider.send = function() {
|
|
||||||
var message = 'The MetaMask Web3 object does not support synchronous methods. See https://github.com/MetaMask/faq#all-async---think-of-metamask-as-a-light-client for details.'
|
|
||||||
throw new Error(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider
|
|
||||||
}
|
|
||||||
|
|
||||||
function remoteStoreWithLocalStorageCache (storageKey) {
|
function remoteStoreWithLocalStorageCache (storageKey) {
|
||||||
// read local cache
|
// read local cache
|
||||||
var initState = JSON.parse(localStorage[storageKey] || '{}')
|
var initState = JSON.parse(localStorage[storageKey] || '{}')
|
||||||
@ -129,3 +100,21 @@ function remoteStoreWithLocalStorageCache (storageKey) {
|
|||||||
|
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRandomId(){
|
||||||
|
const extraDigits = 3
|
||||||
|
// 13 time digits
|
||||||
|
const datePart = new Date().getTime() * Math.pow(10, extraDigits)
|
||||||
|
// 3 random digits
|
||||||
|
const extraPart = Math.floor(Math.random() * Math.pow(10, extraDigits))
|
||||||
|
// 16 digits
|
||||||
|
return datePart + extraPart
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsonrpcMessageTransform(payload, transformFn){
|
||||||
|
if (Array.isArray(payload)) {
|
||||||
|
return payload.map(transformFn)
|
||||||
|
} else {
|
||||||
|
return transformFn(payload)
|
||||||
|
}
|
||||||
|
}
|
8
app/scripts/lib/is-popup-or-notification.js
Normal file
8
app/scripts/lib/is-popup-or-notification.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module.exports = function isPopupOrNotification() {
|
||||||
|
const url = window.location.href
|
||||||
|
if (url.match(/popup.html$/)) {
|
||||||
|
return 'popup'
|
||||||
|
} else {
|
||||||
|
return 'notification'
|
||||||
|
}
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
const Duplex = require('readable-stream').Duplex
|
|
||||||
const inherits = require('util').inherits
|
|
||||||
|
|
||||||
module.exports = LocalMessageDuplexStream
|
|
||||||
|
|
||||||
inherits(LocalMessageDuplexStream, Duplex)
|
|
||||||
|
|
||||||
function LocalMessageDuplexStream (opts) {
|
|
||||||
Duplex.call(this, {
|
|
||||||
objectMode: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// this._origin = opts.origin
|
|
||||||
this._name = opts.name
|
|
||||||
this._target = opts.target
|
|
||||||
|
|
||||||
// console.log('LocalMessageDuplexStream ('+this._name+') - initialized...')
|
|
||||||
window.addEventListener('message', this._onMessage.bind(this), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
|
|
||||||
LocalMessageDuplexStream.prototype._onMessage = function (event) {
|
|
||||||
var msg = event.data
|
|
||||||
// console.log('LocalMessageDuplexStream ('+this._name+') - heard message...', event)
|
|
||||||
// validate message
|
|
||||||
if (event.origin !== location.origin) return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (event.origin !== location.origin) ')
|
|
||||||
if (typeof msg !== 'object') return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (typeof msg !== "object") ')
|
|
||||||
if (msg.target !== this._name) return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (msg.target !== this._name) ', msg.target, this._name)
|
|
||||||
if (!msg.data) return // console.log('LocalMessageDuplexStream ('+this._name+') - rejected - (!msg.data) ')
|
|
||||||
// console.log('LocalMessageDuplexStream ('+this._name+') - accepted', msg.data)
|
|
||||||
// forward message
|
|
||||||
try {
|
|
||||||
this.push(msg.data)
|
|
||||||
} catch (err) {
|
|
||||||
this.emit('error', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stream plumbing
|
|
||||||
|
|
||||||
LocalMessageDuplexStream.prototype._read = noop
|
|
||||||
|
|
||||||
LocalMessageDuplexStream.prototype._write = function (data, encoding, cb) {
|
|
||||||
// console.log('LocalMessageDuplexStream ('+this._name+') - sending message...')
|
|
||||||
var message = {
|
|
||||||
target: this._target,
|
|
||||||
data: data,
|
|
||||||
}
|
|
||||||
window.postMessage(message, location.origin)
|
|
||||||
cb()
|
|
||||||
}
|
|
||||||
|
|
||||||
// util
|
|
||||||
|
|
||||||
function noop () {}
|
|
@ -1,159 +1,48 @@
|
|||||||
const createId = require('hat')
|
|
||||||
const extend = require('xtend')
|
|
||||||
const unmountComponentAtNode = require('react-dom').unmountComponentAtNode
|
|
||||||
const findDOMNode = require('react-dom').findDOMNode
|
|
||||||
const render = require('react-dom').render
|
|
||||||
const h = require('react-hyperscript')
|
|
||||||
const PendingTxDetails = require('../../../ui/app/components/pending-tx-details')
|
|
||||||
const PendingMsgDetails = require('../../../ui/app/components/pending-msg-details')
|
|
||||||
const MetaMaskUiCss = require('../../../ui/css')
|
|
||||||
const extension = require('./extension')
|
const extension = require('./extension')
|
||||||
var notificationHandlers = {}
|
|
||||||
|
|
||||||
const notifications = {
|
const notifications = {
|
||||||
createUnlockRequestNotification: createUnlockRequestNotification,
|
show,
|
||||||
createTxNotification: createTxNotification,
|
getPopup,
|
||||||
createMsgNotification: createMsgNotification,
|
closePopup,
|
||||||
}
|
}
|
||||||
module.exports = notifications
|
module.exports = notifications
|
||||||
window.METAMASK_NOTIFIER = notifications
|
window.METAMASK_NOTIFIER = notifications
|
||||||
|
|
||||||
setupListeners()
|
function show () {
|
||||||
|
getPopup((popup) => {
|
||||||
function setupListeners () {
|
if (popup) {
|
||||||
// guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
|
return extension.windows.update(popup.id, { focused: true })
|
||||||
if (!extension.notifications) return console.error('Chrome notifications API missing...')
|
|
||||||
|
|
||||||
// notification button press
|
|
||||||
extension.notifications.onButtonClicked.addListener(function (notificationId, buttonIndex) {
|
|
||||||
var handlers = notificationHandlers[notificationId]
|
|
||||||
if (buttonIndex === 0) {
|
|
||||||
handlers.confirm()
|
|
||||||
} else {
|
|
||||||
handlers.cancel()
|
|
||||||
}
|
}
|
||||||
extension.notifications.clear(notificationId)
|
|
||||||
})
|
|
||||||
|
|
||||||
// notification teardown
|
extension.windows.create({
|
||||||
extension.notifications.onClosed.addListener(function (notificationId) {
|
url: 'notification.html',
|
||||||
delete notificationHandlers[notificationId]
|
type: 'detached_panel',
|
||||||
|
focused: true,
|
||||||
|
width: 360,
|
||||||
|
height: 500,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// creation helper
|
function getPopup(cb) {
|
||||||
function createUnlockRequestNotification (opts) {
|
|
||||||
// guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
|
|
||||||
if (!extension.notifications) return console.error('Chrome notifications API missing...')
|
|
||||||
var message = 'An Ethereum app has requested a signature. Please unlock your account.'
|
|
||||||
|
|
||||||
var id = createId()
|
// Ignore in test environment
|
||||||
extension.notifications.create(id, {
|
if (!extension.windows) {
|
||||||
type: 'basic',
|
return cb(null)
|
||||||
iconUrl: '/images/icon-128.png',
|
|
||||||
title: opts.title,
|
|
||||||
message: message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTxNotification (state) {
|
|
||||||
// guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
|
|
||||||
if (!extension.notifications) return console.error('Chrome notifications API missing...')
|
|
||||||
|
|
||||||
renderTxNotificationSVG(state, function (err, notificationSvgSource) {
|
|
||||||
if (err) throw err
|
|
||||||
|
|
||||||
showNotification(extend(state, {
|
|
||||||
title: 'New Unsigned Transaction',
|
|
||||||
imageUrl: toSvgUri(notificationSvgSource),
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMsgNotification (state) {
|
|
||||||
// guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
|
|
||||||
if (!extension.notifications) return console.error('Chrome notifications API missing...')
|
|
||||||
|
|
||||||
renderMsgNotificationSVG(state, function (err, notificationSvgSource) {
|
|
||||||
if (err) throw err
|
|
||||||
|
|
||||||
showNotification(extend(state, {
|
|
||||||
title: 'New Unsigned Message',
|
|
||||||
imageUrl: toSvgUri(notificationSvgSource),
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function showNotification (state) {
|
|
||||||
// guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
|
|
||||||
if (!extension.notifications) return console.error('Chrome notifications API missing...')
|
|
||||||
|
|
||||||
var id = createId()
|
|
||||||
extension.notifications.create(id, {
|
|
||||||
type: 'image',
|
|
||||||
requireInteraction: true,
|
|
||||||
iconUrl: '/images/icon-128.png',
|
|
||||||
imageUrl: state.imageUrl,
|
|
||||||
title: state.title,
|
|
||||||
message: '',
|
|
||||||
buttons: [{
|
|
||||||
title: 'Approve',
|
|
||||||
}, {
|
|
||||||
title: 'Reject',
|
|
||||||
}],
|
|
||||||
})
|
|
||||||
notificationHandlers[id] = {
|
|
||||||
confirm: state.onConfirm,
|
|
||||||
cancel: state.onCancel,
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function renderTxNotificationSVG (state, cb) {
|
extension.windows.getAll({}, (windows) => {
|
||||||
var content = h(PendingTxDetails, state)
|
let popup = windows.find((win) => {
|
||||||
renderNotificationSVG(content, cb)
|
return win.type === 'popup'
|
||||||
}
|
})
|
||||||
|
|
||||||
function renderMsgNotificationSVG (state, cb) {
|
cb(popup)
|
||||||
var content = h(PendingMsgDetails, state)
|
|
||||||
renderNotificationSVG(content, cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderNotificationSVG (content, cb) {
|
|
||||||
var container = document.createElement('div')
|
|
||||||
var confirmView = h('div.app-primary', {
|
|
||||||
style: {
|
|
||||||
width: '360px',
|
|
||||||
height: '240px',
|
|
||||||
padding: '16px',
|
|
||||||
// background: '#F7F7F7',
|
|
||||||
background: 'white',
|
|
||||||
},
|
|
||||||
}, [
|
|
||||||
h('style', MetaMaskUiCss()),
|
|
||||||
content,
|
|
||||||
])
|
|
||||||
|
|
||||||
render(confirmView, container, function ready() {
|
|
||||||
var rootElement = findDOMNode(this)
|
|
||||||
var viewSource = rootElement.outerHTML
|
|
||||||
unmountComponentAtNode(container)
|
|
||||||
var svgSource = svgWrapper(viewSource)
|
|
||||||
// insert content into svg wrapper
|
|
||||||
cb(null, svgSource)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function svgWrapper (content) {
|
function closePopup() {
|
||||||
var wrapperSource = `
|
getPopup((popup) => {
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="240">
|
if (!popup) return
|
||||||
<foreignObject x="0" y="0" width="100%" height="100%">
|
extension.windows.remove(popup.id, console.error)
|
||||||
<body xmlns="http://www.w3.org/1999/xhtml" height="100%">{{content}}</body>
|
})
|
||||||
</foreignObject>
|
|
||||||
</svg>
|
|
||||||
`
|
|
||||||
return wrapperSource.split('{{content}}').join(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
function toSvgUri (content) {
|
|
||||||
return 'data:image/svg+xml;utf8,' + encodeURIComponent(content)
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ module.exports = class MetamaskController {
|
|||||||
|
|
||||||
constructor (opts) {
|
constructor (opts) {
|
||||||
this.opts = opts
|
this.opts = opts
|
||||||
|
this.listeners = []
|
||||||
this.configManager = new ConfigManager(opts)
|
this.configManager = new ConfigManager(opts)
|
||||||
this.idStore = new IdentityStore({
|
this.idStore = new IdentityStore({
|
||||||
configManager: this.configManager,
|
configManager: this.configManager,
|
||||||
@ -21,6 +22,9 @@ module.exports = class MetamaskController {
|
|||||||
this.idStore.setStore(this.ethStore)
|
this.idStore.setStore(this.ethStore)
|
||||||
this.messageManager = messageManager
|
this.messageManager = messageManager
|
||||||
this.publicConfigStore = this.initPublicConfigStore()
|
this.publicConfigStore = this.initPublicConfigStore()
|
||||||
|
this.configManager.setCurrentFiat('USD')
|
||||||
|
this.configManager.updateConversionRate()
|
||||||
|
this.scheduleConversionInterval()
|
||||||
}
|
}
|
||||||
|
|
||||||
getState () {
|
getState () {
|
||||||
@ -40,7 +44,9 @@ module.exports = class MetamaskController {
|
|||||||
setProviderType: this.setProviderType.bind(this),
|
setProviderType: this.setProviderType.bind(this),
|
||||||
useEtherscanProvider: this.useEtherscanProvider.bind(this),
|
useEtherscanProvider: this.useEtherscanProvider.bind(this),
|
||||||
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
|
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
|
||||||
|
setCurrentFiat: this.setCurrentFiat.bind(this),
|
||||||
agreeToEthWarning: this.agreeToEthWarning.bind(this),
|
agreeToEthWarning: this.agreeToEthWarning.bind(this),
|
||||||
|
|
||||||
// forward directly to idStore
|
// forward directly to idStore
|
||||||
createNewVault: idStore.createNewVault.bind(idStore),
|
createNewVault: idStore.createNewVault.bind(idStore),
|
||||||
recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
|
recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
|
||||||
@ -59,6 +65,8 @@ module.exports = class MetamaskController {
|
|||||||
recoverSeed: idStore.recoverSeed.bind(idStore),
|
recoverSeed: idStore.recoverSeed.bind(idStore),
|
||||||
// coinbase
|
// coinbase
|
||||||
buyEth: this.buyEth.bind(this),
|
buyEth: this.buyEth.bind(this),
|
||||||
|
// shapeshift
|
||||||
|
createShapeShiftTx: this.createShapeShiftTx.bind(this),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +102,9 @@ module.exports = class MetamaskController {
|
|||||||
function logger (err, request, response) {
|
function logger (err, request, response) {
|
||||||
if (err) return console.error(err)
|
if (err) return console.error(err)
|
||||||
if (!request.isMetamaskInternal) {
|
if (!request.isMetamaskInternal) {
|
||||||
console.log(`RPC (${originDomain}):`, request, '->', response)
|
if (global.METAMASK_DEBUG) {
|
||||||
|
console.log(`RPC (${originDomain}):`, request, '->', response)
|
||||||
|
}
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
console.error('Error in RPC response:\n', response.error)
|
console.error('Error in RPC response:\n', response.error)
|
||||||
}
|
}
|
||||||
@ -103,9 +113,9 @@ module.exports = class MetamaskController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendUpdate () {
|
sendUpdate () {
|
||||||
if (this.remote) {
|
this.listeners.forEach((remote) => {
|
||||||
this.remote.sendUpdate(this.getState())
|
remote.sendUpdate(this.getState())
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeProvider (opts) {
|
initializeProvider (opts) {
|
||||||
@ -121,10 +131,17 @@ module.exports = class MetamaskController {
|
|||||||
},
|
},
|
||||||
// tx signing
|
// tx signing
|
||||||
approveTransaction: this.newUnsignedTransaction.bind(this),
|
approveTransaction: this.newUnsignedTransaction.bind(this),
|
||||||
signTransaction: idStore.signTransaction.bind(idStore),
|
signTransaction: (...args) => {
|
||||||
|
idStore.signTransaction(...args)
|
||||||
|
this.sendUpdate()
|
||||||
|
},
|
||||||
|
|
||||||
// msg signing
|
// msg signing
|
||||||
approveMessage: this.newUnsignedMessage.bind(this),
|
approveMessage: this.newUnsignedMessage.bind(this),
|
||||||
signMessage: idStore.signMessage.bind(idStore),
|
signMessage: (...args) => {
|
||||||
|
idStore.signMessage(...args)
|
||||||
|
this.sendUpdate()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = MetaMaskProvider(providerOpts)
|
var provider = MetaMaskProvider(providerOpts)
|
||||||
@ -184,6 +201,8 @@ module.exports = class MetamaskController {
|
|||||||
|
|
||||||
// It's locked
|
// It's locked
|
||||||
if (!state.isUnlocked) {
|
if (!state.isUnlocked) {
|
||||||
|
|
||||||
|
// Allow the environment to define an unlock message.
|
||||||
this.opts.unlockAccountMessage()
|
this.opts.unlockAccountMessage()
|
||||||
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, noop)
|
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, noop)
|
||||||
|
|
||||||
@ -191,6 +210,7 @@ module.exports = class MetamaskController {
|
|||||||
} else {
|
} else {
|
||||||
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
|
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
|
||||||
if (err) return onTxDoneCb(err)
|
if (err) return onTxDoneCb(err)
|
||||||
|
this.sendUpdate()
|
||||||
this.opts.showUnconfirmedTx(txParams, txData, onTxDoneCb)
|
this.opts.showUnconfirmedTx(txParams, txData, onTxDoneCb)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -199,9 +219,11 @@ module.exports = class MetamaskController {
|
|||||||
newUnsignedMessage (msgParams, cb) {
|
newUnsignedMessage (msgParams, cb) {
|
||||||
var state = this.idStore.getState()
|
var state = this.idStore.getState()
|
||||||
if (!state.isUnlocked) {
|
if (!state.isUnlocked) {
|
||||||
|
this.idStore.addUnconfirmedMessage(msgParams, cb)
|
||||||
this.opts.unlockAccountMessage()
|
this.opts.unlockAccountMessage()
|
||||||
} else {
|
} else {
|
||||||
this.addUnconfirmedMessage(msgParams, cb)
|
this.addUnconfirmedMessage(msgParams, cb)
|
||||||
|
this.sendUpdate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +240,9 @@ module.exports = class MetamaskController {
|
|||||||
|
|
||||||
// Log blocks
|
// Log blocks
|
||||||
processBlock (block) {
|
processBlock (block) {
|
||||||
console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
|
if (global.METAMASK_DEBUG) {
|
||||||
|
console.log(`BLOCK CHANGED: #${block.number.toString('hex')} 0x${block.hash.toString('hex')}`)
|
||||||
|
}
|
||||||
this.verifyNetwork()
|
this.verifyNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,9 +265,34 @@ module.exports = class MetamaskController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCurrentFiat (fiat, cb) {
|
||||||
|
try {
|
||||||
|
this.configManager.setCurrentFiat(fiat)
|
||||||
|
this.configManager.updateConversionRate()
|
||||||
|
this.scheduleConversionInterval()
|
||||||
|
const data = {
|
||||||
|
conversionRate: this.configManager.getConversionRate(),
|
||||||
|
currentFiat: this.configManager.getCurrentFiat(),
|
||||||
|
conversionDate: this.configManager.getConversionDate(),
|
||||||
|
}
|
||||||
|
cb(data)
|
||||||
|
} catch (e) {
|
||||||
|
cb(null, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleConversionInterval () {
|
||||||
|
if (this.conversionInterval) {
|
||||||
|
clearInterval(this.conversionInterval)
|
||||||
|
}
|
||||||
|
this.conversionInterval = setInterval(() => {
|
||||||
|
this.configManager.updateConversionRate()
|
||||||
|
}, 300000)
|
||||||
|
}
|
||||||
|
|
||||||
agreeToEthWarning (cb) {
|
agreeToEthWarning (cb) {
|
||||||
try {
|
try {
|
||||||
this.configManager.setShouldntShowWarning(true)
|
this.configManager.setShouldntShowWarning()
|
||||||
cb()
|
cb()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
cb(e)
|
cb(e)
|
||||||
@ -283,6 +332,9 @@ module.exports = class MetamaskController {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createShapeShiftTx (depositAddress, depositType) {
|
||||||
|
this.configManager.createShapeShiftTx(depositAddress, depositType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function noop () {}
|
function noop () {}
|
||||||
|
@ -9,7 +9,9 @@ const injectCss = require('inject-css')
|
|||||||
const PortStream = require('./lib/port-stream.js')
|
const PortStream = require('./lib/port-stream.js')
|
||||||
const StreamProvider = require('web3-stream-provider')
|
const StreamProvider = require('web3-stream-provider')
|
||||||
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||||
|
const isPopupOrNotification = require('./lib/is-popup-or-notification')
|
||||||
const extension = require('./lib/extension')
|
const extension = require('./lib/extension')
|
||||||
|
const notification = require('./lib/notifications')
|
||||||
|
|
||||||
// setup app
|
// setup app
|
||||||
var css = MetaMaskUiCss()
|
var css = MetaMaskUiCss()
|
||||||
@ -22,7 +24,11 @@ async.parallel({
|
|||||||
|
|
||||||
function connectToAccountManager (cb) {
|
function connectToAccountManager (cb) {
|
||||||
// setup communication with background
|
// setup communication with background
|
||||||
var pluginPort = extension.runtime.connect({name: 'popup'})
|
|
||||||
|
var name = isPopupOrNotification()
|
||||||
|
closePopupIfOpen(name)
|
||||||
|
window.METAMASK_UI_TYPE = name
|
||||||
|
var pluginPort = extension.runtime.connect({ name })
|
||||||
var portStream = new PortStream(pluginPort)
|
var portStream = new PortStream(pluginPort)
|
||||||
// setup multiplexing
|
// setup multiplexing
|
||||||
var mx = setupMultiplex(portStream)
|
var mx = setupMultiplex(portStream)
|
||||||
@ -93,3 +99,9 @@ function setupApp (err, opts) {
|
|||||||
networkVersion: opts.networkVersion,
|
networkVersion: opts.networkVersion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closePopupIfOpen(name) {
|
||||||
|
if (name !== 'notification') {
|
||||||
|
notification.closePopup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,3 +4,10 @@ machine:
|
|||||||
dependencies:
|
dependencies:
|
||||||
pre:
|
pre:
|
||||||
- "npm i -g testem"
|
- "npm i -g testem"
|
||||||
|
- "npm i -g mocha"
|
||||||
|
override:
|
||||||
|
- sudo apt-get install libxss1 libappindicator1 libindicator7 lsb-base
|
||||||
|
- curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
||||||
|
- sudo dpkg -i google-chrome.deb
|
||||||
|
- sudo sed -i 's|HERE/chrome\"|HERE/chrome\" --disable-setuid-sandbox|g' /opt/google/chrome/google-chrome
|
||||||
|
- rm google-chrome.deb
|
||||||
|
File diff suppressed because one or more lines are too long
163
development/states/account-detail-with-shapeshift-tx.json
Normal file
163
development/states/account-detail-with-shapeshift-tx.json
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 11.06608791,
|
||||||
|
"conversionDate": 1470421024,
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"currentDomain": "example.com",
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"shapeShiftTxList":[
|
||||||
|
{
|
||||||
|
"depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw",
|
||||||
|
"depositType": "BTC",
|
||||||
|
"key": "shapeshift",
|
||||||
|
"time": 1471564825772,
|
||||||
|
"response": {
|
||||||
|
"status": "complete",
|
||||||
|
"outgoingCoin": "100.00",
|
||||||
|
"incomingCoin": "1.000",
|
||||||
|
"transaction": "0x3701e0ac344a12a1fc5417cf251109a7c41f3edab922310202630d9c012414c8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw",
|
||||||
|
"depositType": "BTC",
|
||||||
|
"key": "shapeshift",
|
||||||
|
"time": 1471566579224,
|
||||||
|
"response": {
|
||||||
|
"status": "no_deposits",
|
||||||
|
"depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw",
|
||||||
|
"depositType": "BTC",
|
||||||
|
"key": "shapeshift",
|
||||||
|
"time": 1471566565378,
|
||||||
|
"response": {
|
||||||
|
"status": "received",
|
||||||
|
"depositAddress": "1L8BJCR6KHkCiVceDqibt7zJscqPpH7pFw"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transactions": [ {
|
||||||
|
"id": 1467921503489592,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||||
|
"to": "0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
|
||||||
|
"value": "0x66c899104aa57038000",
|
||||||
|
"origin": "thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com",
|
||||||
|
"metamaskId": 1467921503489592,
|
||||||
|
"metamaskNetworkId": "2"
|
||||||
|
},
|
||||||
|
"time": 1467921503489,
|
||||||
|
"status": "rejected",
|
||||||
|
"containsDelegateCall": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1467923203344608,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||||
|
"to": "0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
|
||||||
|
"value": "0x0",
|
||||||
|
"origin": "thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com",
|
||||||
|
"metamaskId": 1467923203344608,
|
||||||
|
"metamaskNetworkId": "2"
|
||||||
|
},
|
||||||
|
"time": 1467923203344,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": false,
|
||||||
|
"hash": "0x957bbba51e2732a86c10c5e7e8a484a093795a06f2e2c38ad02da1b20aeca620"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1467921503489592,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||||
|
"to": "0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
|
||||||
|
"value": "0x66c899104aa57038000",
|
||||||
|
"origin": "thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com",
|
||||||
|
"metamaskId": 1467921503489592,
|
||||||
|
"metamaskNetworkId": "2"
|
||||||
|
},
|
||||||
|
"time": 1467921503489,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": false
|
||||||
|
}],
|
||||||
|
"identities": {
|
||||||
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
|
||||||
|
"name": "Wallet 1",
|
||||||
|
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
|
||||||
|
"name": "Wallet 2",
|
||||||
|
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0xeb9e64b93097bc15f01f13eae97015c57ab64823": {
|
||||||
|
"name": "Wallet 3",
|
||||||
|
"address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0x704107d04affddd9b66ab9de3dd7b095852e9b69": {
|
||||||
|
"name": "Wallet 4",
|
||||||
|
"address": "0x704107d04affddd9b66ab9de3dd7b095852e9b69",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"accounts": {
|
||||||
|
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
|
||||||
|
},
|
||||||
|
"0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"balance": "0x0",
|
||||||
|
"address": "0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b"
|
||||||
|
},
|
||||||
|
"0xeb9e64b93097bc15f01f13eae97015c57ab64823": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"balance": "0x0",
|
||||||
|
"address": "0xeb9e64b93097bc15f01f13eae97015c57ab64823"
|
||||||
|
},
|
||||||
|
"0x704107d04affddd9b66ab9de3dd7b095852e9b69": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0x704107d04affddd9b66ab9de3dd7b095852e9b69"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||||
|
"network": "1",
|
||||||
|
"seedWords": null,
|
||||||
|
"isConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "mainnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "accountDetail",
|
||||||
|
"detailView": null,
|
||||||
|
"context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"currentDomain": "127.0.0.1:9966",
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 11.06608791,
|
||||||
|
"conversionDate": 1470421024,
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
"isUnlocked": true,
|
"isUnlocked": true,
|
||||||
"currentDomain": "example.com",
|
"currentDomain": "example.com",
|
||||||
@ -72,7 +75,7 @@
|
|||||||
"txParams": {
|
"txParams": {
|
||||||
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||||
"to": "0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
|
"to": "0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
|
||||||
"value": "0x99966c8104aa57038000",
|
"value": "0x0",
|
||||||
"origin": "thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com",
|
"origin": "thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com",
|
||||||
"metamaskId": 1467923203344608,
|
"metamaskId": 1467923203344608,
|
||||||
"metamaskNetworkId": "2"
|
"metamaskNetworkId": "2"
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 11.06608791,
|
||||||
|
"conversionDate": 1470421024,
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
"isUnlocked": true,
|
"isUnlocked": true,
|
||||||
"currentDomain": "example.com",
|
"currentDomain": "example.com",
|
||||||
@ -81,4 +84,4 @@
|
|||||||
"warning": null
|
"warning": null
|
||||||
},
|
},
|
||||||
"identities": {}
|
"identities": {}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"metamask": {
|
"metamask": {
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 11.06608791,
|
||||||
|
"conversionDate": 1470421024,
|
||||||
"isInitialized": true,
|
"isInitialized": true,
|
||||||
"isUnlocked": true,
|
"isUnlocked": true,
|
||||||
"currentDomain": "example.com",
|
"currentDomain": "example.com",
|
||||||
@ -68,7 +71,7 @@
|
|||||||
"appState": {
|
"appState": {
|
||||||
"menuOpen": false,
|
"menuOpen": false,
|
||||||
"currentView": {
|
"currentView": {
|
||||||
"name": "accounts"
|
"name": "config"
|
||||||
},
|
},
|
||||||
"accountDetail": {
|
"accountDetail": {
|
||||||
"subview": "transactions",
|
"subview": "transactions",
|
||||||
@ -82,4 +85,4 @@
|
|||||||
"scrollToBottom": true
|
"scrollToBottom": true
|
||||||
},
|
},
|
||||||
"identities": {}
|
"identities": {}
|
||||||
}
|
}
|
||||||
|
189
development/states/custom-rpc.json
Normal file
189
development/states/custom-rpc.json
Normal file
File diff suppressed because one or more lines are too long
406
development/states/pending-signature.json
Normal file
406
development/states/pending-signature.json
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"isEthConfirmed": true,
|
||||||
|
"currentDomain": "example.com",
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"name": "Wallet 1",
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
|
"name": "Wallet 2",
|
||||||
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
|
"name": "Wallet 3",
|
||||||
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 11.02269525,
|
||||||
|
"conversionDate": 1472076963,
|
||||||
|
"accounts": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"balance": "0x056ace16d84b1c7e78",
|
||||||
|
"nonce": "0x17",
|
||||||
|
"code": "0x0",
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
|
"balance": "0x00000000000000056bc75e2d63100000",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"code": "0x0",
|
||||||
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
|
||||||
|
},
|
||||||
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
|
"balance": "0x00000000000000056bc75e2d63100000",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"code": "0x0",
|
||||||
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [
|
||||||
|
{
|
||||||
|
"id": 1471975421223082,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471975421223082,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471975421223,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xd0fe393e2586ebded866c9f13b90494e902bc49047fbf25ba2ac96c805a2f5d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471975427199819,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471975427199819,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471975427199,
|
||||||
|
"status": "rejected",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xb1f2f63f3e265f05d7c353ab38dd8b73fce8e7214489037311ee1f58a994dae3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471975806981442,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471975806981442,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471975806981,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xeeb89e91aaeea14c4950016c45d60df8ee8874daa6f414de5cf267ea2c17bc6e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471975810133789,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471975810133789,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471975810133,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xe54cab2e54b8495691b6d8315ca24a190cba546a9fcb056642479ce5770cec8b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471976546865348,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb000000000000000000000000000000000000000000000000000000000000000a",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471976546865348,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471976546865,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0x54e10f77e17f28f4d12751749a2ca22f9b528592d1140ef53c6430a68e731542"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471976930101889,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471976930101889,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471976930101,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0x60b5af26fad18c5549949064b67c8f965c9f20cd3e890c69512ca3acad10ed8b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471977268048169,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471977268048169,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471977268048,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0x1f96e29305ef11a9c993302c29e5419d87017e8222d4034daea0d86e155dc3aa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471977310778630,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471977310778630,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471977310778,
|
||||||
|
"status": "rejected",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471977316241561,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471977316241561,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471977316240,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xdbd610c92d77a07c76b82e14e32674d382c45c4780dd2a550888b5cc40d54bcc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471977344018510,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000004",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471977344018510,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471977344018,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0x709d871d9ded0108de9f7718a7490b19d45e5e7562b1ba6c5bf6cce56e767d48"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471977403830380,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471977403830380,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471977403830,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0x94f5088a127bba181b303d6427ae93cbfa9867997bea1326f30da311e36c6aca"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471977431563703,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471977431563703,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471977431563,
|
||||||
|
"status": "rejected",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471977436074587,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471977436074587,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471977436074,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0x5f514dfe3bd48f6f301c809a7a75f73f0fc93bc3a0e469368b84dce032aff9ec"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471991826717707,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000004",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471991826717707,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471991826717,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xab1e706f8981680a6c921f9f57f8ce573392bbb4f0fe85cf45e5cbf858fa5f3e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471991851917592,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000004",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471991851917592,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471991851917,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xdea144f7a6f06969739f676d8702a9a11efc689e032f1981fe67afc9261dd4de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471992032999543,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471992032999543,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471992032999,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef",
|
||||||
|
"hash": "0xdf31b8cc0fbd2ab6727e0b63536bd4eab51a147aa29e04691e68fae28b866fb3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1471992043490878,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"to": "0x48ff0cbac0acefedf152281ee80e9a0a01d5da63",
|
||||||
|
"data": "0x90b98a11000000000000000000000000c5b8dbac4c1d3f152cdeb400e2313f309c410acb0000000000000000000000000000000000000000000000000000000000000005",
|
||||||
|
"origin": "localhost",
|
||||||
|
"metamaskId": 1471992043490878,
|
||||||
|
"metamaskNetworkId": "1471904489432"
|
||||||
|
},
|
||||||
|
"time": 1471992043490,
|
||||||
|
"status": "rejected",
|
||||||
|
"containsDelegateCall": true,
|
||||||
|
"estimatedGas": "0x89ef"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1472068030402279,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x3782dace9d90000",
|
||||||
|
"gas": "0x493e0",
|
||||||
|
"to": "0x18a672e11d637fffadccc99b152f4895da069601",
|
||||||
|
"data": "0x5b7d47a900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"origin": "www.rouleth.com",
|
||||||
|
"metamaskId": 1472068030402279,
|
||||||
|
"metamaskNetworkId": "1"
|
||||||
|
},
|
||||||
|
"time": 1472068030402,
|
||||||
|
"status": "rejected",
|
||||||
|
"containsDelegateCall": false,
|
||||||
|
"estimatedGas": "0x24704"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1472068061833258,
|
||||||
|
"txParams": {
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"value": "0x16345785d8a0000",
|
||||||
|
"gas": "0x493e0",
|
||||||
|
"to": "0x18a672e11d637fffadccc99b152f4895da069601",
|
||||||
|
"data": "0x5b7d47a900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"origin": "www.rouleth.com",
|
||||||
|
"metamaskId": 1472068061833258,
|
||||||
|
"metamaskNetworkId": "1"
|
||||||
|
},
|
||||||
|
"time": 1472068061833,
|
||||||
|
"status": "confirmed",
|
||||||
|
"containsDelegateCall": false,
|
||||||
|
"estimatedGas": "0x24704",
|
||||||
|
"hash": "0xb6e6ff57e7b5f6bd7f2e6dc44c39f4e858a227c9509586634ca547179345a13e"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"network": "1471904489432",
|
||||||
|
"seedWords": null,
|
||||||
|
"isConfirmed": true,
|
||||||
|
"unconfMsgs": {
|
||||||
|
"1472076978535283": {
|
||||||
|
"id": 1472076978535283,
|
||||||
|
"msgParams": {
|
||||||
|
"origin": "localhost",
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"data": "hello"
|
||||||
|
},
|
||||||
|
"time": 1472076978535,
|
||||||
|
"status": "unconfirmed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"id": 1472076978535283,
|
||||||
|
"msgParams": {
|
||||||
|
"origin": "localhost",
|
||||||
|
"from": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"data": "hello"
|
||||||
|
},
|
||||||
|
"time": 1472076978535,
|
||||||
|
"status": "unconfirmed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "rpc",
|
||||||
|
"rpcTarget": "http://localhost:8545"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "confTx",
|
||||||
|
"detailView": null,
|
||||||
|
"context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"currentDomain": "ebjbdknjcgcbchkagneicjfpneaghdhb",
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
39
development/states/restore-vault.json
Normal file
39
development/states/restore-vault.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": false,
|
||||||
|
"isUnlocked": false,
|
||||||
|
"isEthConfirmed": false,
|
||||||
|
"currentDomain": "example.com",
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 0,
|
||||||
|
"conversionDate": "N/A",
|
||||||
|
"accounts": {},
|
||||||
|
"transactions": [],
|
||||||
|
"seedWords": null,
|
||||||
|
"isConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"network": "2"
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "restoreVault"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"currentDomain": "extensions",
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
76
development/states/send.json
Normal file
76
development/states/send.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"isEthConfirmed": false,
|
||||||
|
"currentDomain": "example.com",
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"name": "Wallet 1",
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
|
"name": "Wallet 2",
|
||||||
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
|
"name": "Wallet 3",
|
||||||
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 11.21283484,
|
||||||
|
"conversionDate": 1472158984,
|
||||||
|
"accounts": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x34693f54a1e25900",
|
||||||
|
"nonce": "0x100013",
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x100000",
|
||||||
|
"balance": "0x18af912cee770000",
|
||||||
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
|
||||||
|
},
|
||||||
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x100000",
|
||||||
|
"balance": "0x2386f26fc10000",
|
||||||
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"network": "2",
|
||||||
|
"seedWords": null,
|
||||||
|
"isConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "sendTransaction"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"currentDomain": "127.0.0.1:9966",
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null,
|
||||||
|
"detailView": {}
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
348
development/states/shapeshift.json
Normal file
348
development/states/shapeshift.json
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"isEthConfirmed": true,
|
||||||
|
"currentDomain": "example.com",
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"name": "Wallet 1",
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
|
"name": "Wallet 2",
|
||||||
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
},
|
||||||
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
|
"name": "Wallet 3",
|
||||||
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d",
|
||||||
|
"mayBeFauceting": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 11.21274318,
|
||||||
|
"conversionDate": 1472159644,
|
||||||
|
"accounts": {
|
||||||
|
"0xfdea65c8e26263f6d9a1b5de9555d2931a33b825": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x13",
|
||||||
|
"balance": "0x461d4a64e937d3d1",
|
||||||
|
"address": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"balance": "0x0",
|
||||||
|
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb"
|
||||||
|
},
|
||||||
|
"0x2f8d4a878cfa04a6e60d46362f5644deab66572d": {
|
||||||
|
"code": "0x",
|
||||||
|
"balance": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"address": "0x2f8d4a878cfa04a6e60d46362f5644deab66572d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"selectedAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"network": "1",
|
||||||
|
"seedWords": null,
|
||||||
|
"isConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "mainnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "buyEth",
|
||||||
|
"context": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"currentDomain": "127.0.0.1:9966",
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"detailView": {},
|
||||||
|
"buyView": {
|
||||||
|
"subview": "buyForm",
|
||||||
|
"formView": {
|
||||||
|
"coinbase": false,
|
||||||
|
"shapeshift": true,
|
||||||
|
"marketinfo": {
|
||||||
|
"pair": "btc_eth",
|
||||||
|
"rate": 51.14252949,
|
||||||
|
"minerFee": 0.01,
|
||||||
|
"limit": 2.60306578,
|
||||||
|
"minimum": 0.00038935,
|
||||||
|
"maxLimit": 8.67688592
|
||||||
|
},
|
||||||
|
"coinOptions": {
|
||||||
|
"BTC": {
|
||||||
|
"name": "Bitcoin",
|
||||||
|
"symbol": "BTC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/bitcoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"BCY": {
|
||||||
|
"name": "BitCrystals",
|
||||||
|
"symbol": "BCY",
|
||||||
|
"image": "https://shapeshift.io/images/coins/bitcrystals.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"BLK": {
|
||||||
|
"name": "Blackcoin",
|
||||||
|
"symbol": "BLK",
|
||||||
|
"image": "https://shapeshift.io/images/coins/blackcoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"BTS": {
|
||||||
|
"name": "Bitshares",
|
||||||
|
"symbol": "BTS",
|
||||||
|
"specialReturn": false,
|
||||||
|
"specialOutgoing": true,
|
||||||
|
"specialIncoming": true,
|
||||||
|
"fieldName": "destTag",
|
||||||
|
"fieldKey": "destTag",
|
||||||
|
"image": "https://shapeshift.io/images/coins/bitshares.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"CLAM": {
|
||||||
|
"name": "Clams",
|
||||||
|
"symbol": "CLAM",
|
||||||
|
"image": "https://shapeshift.io/images/coins/clams.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"DASH": {
|
||||||
|
"name": "Dash",
|
||||||
|
"symbol": "DASH",
|
||||||
|
"image": "https://shapeshift.io/images/coins/dash.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"DGB": {
|
||||||
|
"name": "Digibyte",
|
||||||
|
"symbol": "DGB",
|
||||||
|
"image": "https://shapeshift.io/images/coins/digibyte.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"DAO": {
|
||||||
|
"name": "TheDao",
|
||||||
|
"symbol": "DAO",
|
||||||
|
"image": "https://shapeshift.io/images/coins/thedao.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"DGD": {
|
||||||
|
"name": "DigixDao",
|
||||||
|
"symbol": "DGD",
|
||||||
|
"image": "https://shapeshift.io/images/coins/digixdao.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"DOGE": {
|
||||||
|
"name": "Dogecoin",
|
||||||
|
"symbol": "DOGE",
|
||||||
|
"image": "https://shapeshift.io/images/coins/dogecoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"EMC": {
|
||||||
|
"name": "Emercoin",
|
||||||
|
"symbol": "EMC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/emercoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"ETH": {
|
||||||
|
"name": "Ether",
|
||||||
|
"symbol": "ETH",
|
||||||
|
"image": "https://shapeshift.io/images/coins/ether.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"ETC": {
|
||||||
|
"name": "Ether Classic",
|
||||||
|
"symbol": "ETC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/etherclassic.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"FCT": {
|
||||||
|
"name": "Factoids",
|
||||||
|
"symbol": "FCT",
|
||||||
|
"image": "https://shapeshift.io/images/coins/factoids.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"LBC": {
|
||||||
|
"name": "LBRY Credits",
|
||||||
|
"symbol": "LBC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/lbry.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"LSK": {
|
||||||
|
"name": "Lisk",
|
||||||
|
"symbol": "LSK",
|
||||||
|
"image": "https://shapeshift.io/images/coins/lisk.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"LTC": {
|
||||||
|
"name": "Litecoin",
|
||||||
|
"symbol": "LTC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/litecoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"MAID": {
|
||||||
|
"name": "Maidsafe",
|
||||||
|
"symbol": "MAID",
|
||||||
|
"image": "https://shapeshift.io/images/coins/maidsafe.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"MINT": {
|
||||||
|
"name": "Mintcoin",
|
||||||
|
"symbol": "MINT",
|
||||||
|
"image": "https://shapeshift.io/images/coins/mintcoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"MONA": {
|
||||||
|
"name": "Monacoin",
|
||||||
|
"symbol": "MONA",
|
||||||
|
"image": "https://shapeshift.io/images/coins/monacoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"MSC": {
|
||||||
|
"name": "Omni",
|
||||||
|
"symbol": "MSC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/mastercoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"NBT": {
|
||||||
|
"name": "Nubits",
|
||||||
|
"symbol": "NBT",
|
||||||
|
"image": "https://shapeshift.io/images/coins/nubits.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"NMC": {
|
||||||
|
"name": "Namecoin",
|
||||||
|
"symbol": "NMC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/namecoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"NVC": {
|
||||||
|
"name": "Novacoin",
|
||||||
|
"symbol": "NVC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/novacoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"NXT": {
|
||||||
|
"name": "Nxt",
|
||||||
|
"symbol": "NXT",
|
||||||
|
"specialReturn": false,
|
||||||
|
"specialOutgoing": true,
|
||||||
|
"specialIncoming": true,
|
||||||
|
"specialIncomingStatus": false,
|
||||||
|
"fieldName": "Public Key (only for unfunded accounts!)",
|
||||||
|
"fieldKey": "rsAddress",
|
||||||
|
"image": "https://shapeshift.io/images/coins/nxt.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"PPC": {
|
||||||
|
"name": "Peercoin",
|
||||||
|
"symbol": "PPC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/peercoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"RDD": {
|
||||||
|
"name": "Reddcoin",
|
||||||
|
"symbol": "RDD",
|
||||||
|
"image": "https://shapeshift.io/images/coins/reddcoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"SDC": {
|
||||||
|
"name": "Shadowcash",
|
||||||
|
"symbol": "SDC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/shadowcash.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"SC": {
|
||||||
|
"name": "Siacoin",
|
||||||
|
"symbol": "SC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/siacoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"SJCX": {
|
||||||
|
"name": "StorjX",
|
||||||
|
"symbol": "SJCX",
|
||||||
|
"image": "https://shapeshift.io/images/coins/storjcoinx.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"START": {
|
||||||
|
"name": "Startcoin",
|
||||||
|
"symbol": "START",
|
||||||
|
"image": "https://shapeshift.io/images/coins/startcoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"STEEM": {
|
||||||
|
"name": "Steem",
|
||||||
|
"symbol": "STEEM",
|
||||||
|
"specialReturn": false,
|
||||||
|
"specialOutgoing": true,
|
||||||
|
"specialIncoming": true,
|
||||||
|
"fieldName": "destTag",
|
||||||
|
"fieldKey": "destTag",
|
||||||
|
"image": "https://shapeshift.io/images/coins/steem.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"USDT": {
|
||||||
|
"name": "Tether",
|
||||||
|
"symbol": "USDT",
|
||||||
|
"image": "https://shapeshift.io/images/coins/tether.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"VOX": {
|
||||||
|
"name": "Voxels",
|
||||||
|
"symbol": "VOX",
|
||||||
|
"image": "https://shapeshift.io/images/coins/voxels.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"VRC": {
|
||||||
|
"name": "Vericoin",
|
||||||
|
"symbol": "VRC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/vericoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"VTC": {
|
||||||
|
"name": "Vertcoin",
|
||||||
|
"symbol": "VTC",
|
||||||
|
"image": "https://shapeshift.io/images/coins/vertcoin.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"XCP": {
|
||||||
|
"name": "Counterparty",
|
||||||
|
"symbol": "XCP",
|
||||||
|
"image": "https://shapeshift.io/images/coins/counterparty.png",
|
||||||
|
"status": "available"
|
||||||
|
},
|
||||||
|
"XMR": {
|
||||||
|
"name": "Monero",
|
||||||
|
"symbol": "XMR",
|
||||||
|
"specialReturn": false,
|
||||||
|
"specialOutgoing": true,
|
||||||
|
"specialIncoming": true,
|
||||||
|
"fieldName": "Payment Id",
|
||||||
|
"qrName": "tx_payment_id",
|
||||||
|
"fieldKey": "paymentId",
|
||||||
|
"image": "https://shapeshift.io/images/coins/monero.png",
|
||||||
|
"status": "available"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buyAddress": "0xfdea65c8e26263f6d9a1b5de9555d2931a33b825",
|
||||||
|
"amount": "5.00",
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"isSubLoading": false
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
28
docs/form_persisting_architecture.md
Normal file
28
docs/form_persisting_architecture.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Form Persisting Architecture
|
||||||
|
|
||||||
|
Since:
|
||||||
|
- The popup is torn down completely on every click outside of it.
|
||||||
|
- We have forms with multiple fields (like passwords & seed phrases) that might encourage a user to leave our panel to refer to a password manager.
|
||||||
|
|
||||||
|
We cause user friction when we lose the contents of certain forms.
|
||||||
|
|
||||||
|
This calls for an architecture of a form component that can completely persist its values to LocalStorage on every relevant change, and restore those values on reopening.
|
||||||
|
|
||||||
|
To achieve this, we have defined a class, a subclass of `React.Component`, called `PersistentForm`, and it's stored at `ui/lib/persistent-form.js`.
|
||||||
|
|
||||||
|
To use this class, simply take your form component (the component that renders `input`, `select`, or `textarea` elements), and make it subclass from `PersistentForm` instead of `React.Component`.
|
||||||
|
|
||||||
|
You can see an example of this in use in `ui/app/first-time/restore-vault.js`.
|
||||||
|
|
||||||
|
Additionally, any field whose value should be persisted, should have a `persistentFormId` attribute, which needs to be assigned under a `dataset` key on the main `attributes` hash. For example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
return h('textarea.twelve-word-phrase.letter-spacey', {
|
||||||
|
dataset: {
|
||||||
|
persistentFormId: 'wallet-seed',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! This field should be persisted to `localStorage` on each `keyUp`, those values should be restored on view load, and the cached values should be cleared when navigating deliberately away from the form.
|
||||||
|
|
@ -19,6 +19,7 @@ const Root = require('./ui/app/root')
|
|||||||
const configureStore = require('./ui/app/store')
|
const configureStore = require('./ui/app/store')
|
||||||
const actions = require('./ui/app/actions')
|
const actions = require('./ui/app/actions')
|
||||||
const states = require('./development/states')
|
const states = require('./development/states')
|
||||||
|
const Selector = require('./development/selector')
|
||||||
const MetamaskController = require('./app/scripts/metamask-controller')
|
const MetamaskController = require('./app/scripts/metamask-controller')
|
||||||
const extension = require('./development/mockExtension')
|
const extension = require('./development/mockExtension')
|
||||||
|
|
||||||
@ -139,6 +140,8 @@ render(
|
|||||||
},
|
},
|
||||||
}, 'Reset State'),
|
}, 'Reset State'),
|
||||||
|
|
||||||
|
h(Selector, { actions, selectedKey: selectedView, states, store }),
|
||||||
|
|
||||||
h('.mock-app-root', {
|
h('.mock-app-root', {
|
||||||
style: {
|
style: {
|
||||||
height: '500px',
|
height: '500px',
|
||||||
|
16
package.json
16
package.json
@ -5,6 +5,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "gulp dev",
|
"start": "gulp dev",
|
||||||
|
"lint": "gulp lint",
|
||||||
|
"dev": "gulp dev",
|
||||||
|
"dist": "gulp dist",
|
||||||
"test": "npm run fastTest && npm run ci",
|
"test": "npm run fastTest && npm run ci",
|
||||||
"fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
"fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
||||||
"watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
"watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
||||||
@ -35,6 +38,7 @@
|
|||||||
"debounce": "^1.0.0",
|
"debounce": "^1.0.0",
|
||||||
"dnode": "^1.2.2",
|
"dnode": "^1.2.2",
|
||||||
"end-of-stream": "^1.1.0",
|
"end-of-stream": "^1.1.0",
|
||||||
|
"ensnare": "^1.0.0",
|
||||||
"eth-bin-to-ops": "^1.0.0",
|
"eth-bin-to-ops": "^1.0.0",
|
||||||
"eth-lightwallet": "^2.3.3",
|
"eth-lightwallet": "^2.3.3",
|
||||||
"eth-query": "^1.0.3",
|
"eth-query": "^1.0.3",
|
||||||
@ -47,12 +51,13 @@
|
|||||||
"inject-css": "^0.1.1",
|
"inject-css": "^0.1.1",
|
||||||
"jazzicon": "^1.1.3",
|
"jazzicon": "^1.1.3",
|
||||||
"menu-droppo": "^1.1.0",
|
"menu-droppo": "^1.1.0",
|
||||||
"metamask-logo": "^1.1.5",
|
"metamask-logo": "^1.3.1",
|
||||||
"mississippi": "^1.2.0",
|
"mississippi": "^1.2.0",
|
||||||
"multiplex": "^6.7.0",
|
"multiplex": "^6.7.0",
|
||||||
"once": "^1.3.3",
|
"once": "^1.3.3",
|
||||||
"pojo-migrator": "^2.1.0",
|
"pojo-migrator": "^2.1.0",
|
||||||
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
||||||
|
"post-message-stream": "^1.0.0",
|
||||||
"pumpify": "^1.3.4",
|
"pumpify": "^1.3.4",
|
||||||
"react": "^15.0.2",
|
"react": "^15.0.2",
|
||||||
"react-addons-css-transition-group": "^15.0.2",
|
"react-addons-css-transition-group": "^15.0.2",
|
||||||
@ -65,14 +70,15 @@
|
|||||||
"redux": "^3.0.5",
|
"redux": "^3.0.5",
|
||||||
"redux-logger": "^2.3.1",
|
"redux-logger": "^2.3.1",
|
||||||
"redux-thunk": "^1.0.2",
|
"redux-thunk": "^1.0.2",
|
||||||
|
"request-promise": "^4.1.1",
|
||||||
"sandwich-expando": "^1.0.5",
|
"sandwich-expando": "^1.0.5",
|
||||||
"textarea-caret": "^3.0.1",
|
"textarea-caret": "^3.0.1",
|
||||||
"three.js": "^0.73.2",
|
"three.js": "^0.73.2",
|
||||||
"through2": "^2.0.1",
|
"through2": "^2.0.1",
|
||||||
"vreme": "^3.0.2",
|
"vreme": "^3.0.2",
|
||||||
"web3": "ethereum/web3.js#0.16.0",
|
"web3": "^0.17.0-alpha",
|
||||||
"web3-provider-engine": "^7.8.3",
|
"web3-provider-engine": "^8.0.2",
|
||||||
"web3-stream-provider": "^2.0.5",
|
"web3-stream-provider": "^2.0.6",
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -102,10 +108,12 @@
|
|||||||
"mocha-eslint": "^2.1.1",
|
"mocha-eslint": "^2.1.1",
|
||||||
"mocha-jsdom": "^1.1.0",
|
"mocha-jsdom": "^1.1.0",
|
||||||
"mocha-sinon": "^1.1.5",
|
"mocha-sinon": "^1.1.5",
|
||||||
|
"nock": "^8.0.0",
|
||||||
"qs": "^6.2.0",
|
"qs": "^6.2.0",
|
||||||
"qunit": "^0.9.1",
|
"qunit": "^0.9.1",
|
||||||
"sinon": "^1.17.3",
|
"sinon": "^1.17.3",
|
||||||
"tape": "^4.5.1",
|
"tape": "^4.5.1",
|
||||||
|
"testem": "^1.10.3",
|
||||||
"uglifyify": "^3.0.1",
|
"uglifyify": "^3.0.1",
|
||||||
"vinyl-buffer": "^1.0.0",
|
"vinyl-buffer": "^1.0.0",
|
||||||
"vinyl-source-stream": "^1.1.0",
|
"vinyl-source-stream": "^1.1.0",
|
||||||
|
12
test/unit/account-link-test.js
Normal file
12
test/unit/account-link-test.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
var assert = require('assert')
|
||||||
|
var linkGen = require('../../ui/lib/account-link')
|
||||||
|
|
||||||
|
describe('account-link', function() {
|
||||||
|
|
||||||
|
it('adds testnet prefix to morden test network', function() {
|
||||||
|
var result = linkGen('account', '2')
|
||||||
|
assert.notEqual(result.indexOf('testnet'), -1, 'testnet injected')
|
||||||
|
assert.notEqual(result.indexOf('account'), -1, 'account included')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -1,8 +1,10 @@
|
|||||||
var assert = require('assert')
|
const assert = require('assert')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const STORAGE_KEY = 'metamask-persistance-key'
|
const STORAGE_KEY = 'metamask-persistance-key'
|
||||||
var configManagerGen = require('../lib/mock-config-manager')
|
var configManagerGen = require('../lib/mock-config-manager')
|
||||||
var configManager
|
var configManager
|
||||||
|
const rp = require('request-promise')
|
||||||
|
const nock = require('nock')
|
||||||
|
|
||||||
describe('config-manager', function() {
|
describe('config-manager', function() {
|
||||||
|
|
||||||
@ -11,6 +13,91 @@ describe('config-manager', function() {
|
|||||||
configManager = configManagerGen()
|
configManager = configManagerGen()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('currency conversions', function() {
|
||||||
|
|
||||||
|
describe('#getCurrentFiat', function() {
|
||||||
|
it('should return false if no previous key exists', function() {
|
||||||
|
var result = configManager.getCurrentFiat()
|
||||||
|
assert.ok(!result)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#setCurrentFiat', function() {
|
||||||
|
it('should make getCurrentFiat return true once set', function() {
|
||||||
|
assert.equal(configManager.getCurrentFiat(), false)
|
||||||
|
configManager.setCurrentFiat('USD')
|
||||||
|
var result = configManager.getCurrentFiat()
|
||||||
|
assert.equal(result, 'USD')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work with other currencies as well', function() {
|
||||||
|
assert.equal(configManager.getCurrentFiat(), false)
|
||||||
|
configManager.setCurrentFiat('JPY')
|
||||||
|
var result = configManager.getCurrentFiat()
|
||||||
|
assert.equal(result, 'JPY')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#getConversionRate', function() {
|
||||||
|
it('should return false if non-existent', function() {
|
||||||
|
var result = configManager.getConversionRate()
|
||||||
|
assert.ok(!result)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#updateConversionRate', function() {
|
||||||
|
it('should retrieve an update for ETH to USD and set it in memory', function(done) {
|
||||||
|
this.timeout(15000)
|
||||||
|
var usdMock = nock('https://www.cryptonator.com')
|
||||||
|
.get('/api/ticker/eth-USD')
|
||||||
|
.reply(200, '{"ticker":{"base":"ETH","target":"USD","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
|
||||||
|
|
||||||
|
assert.equal(configManager.getConversionRate(), false)
|
||||||
|
var promise = new Promise(
|
||||||
|
function (resolve, reject) {
|
||||||
|
configManager.setCurrentFiat('USD')
|
||||||
|
configManager.updateConversionRate().then(function() {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
promise.then(function() {
|
||||||
|
var result = configManager.getConversionRate()
|
||||||
|
assert.equal(typeof result, 'number')
|
||||||
|
done()
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should work for JPY as well.', function() {
|
||||||
|
this.timeout(15000)
|
||||||
|
assert.equal(configManager.getConversionRate(), false)
|
||||||
|
|
||||||
|
var jpyMock = nock('https://www.cryptonator.com')
|
||||||
|
.get('/api/ticker/eth-JPY')
|
||||||
|
.reply(200, '{"ticker":{"base":"ETH","target":"JPY","price":"11.02456145","volume":"44948.91745289","change":"-0.01472534"},"timestamp":1472072136,"success":true,"error":""}')
|
||||||
|
|
||||||
|
|
||||||
|
var promise = new Promise(
|
||||||
|
function (resolve, reject) {
|
||||||
|
configManager.setCurrentFiat('JPY')
|
||||||
|
configManager.updateConversionRate().then(function() {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
promise.then(function() {
|
||||||
|
var result = configManager.getConversionRate()
|
||||||
|
assert.equal(typeof result, 'number')
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('confirmation', function() {
|
describe('confirmation', function() {
|
||||||
|
|
||||||
describe('#getConfirmed', function() {
|
describe('#getConfirmed', function() {
|
||||||
@ -157,6 +244,17 @@ describe('config-manager', function() {
|
|||||||
assert.equal(result.length, 1)
|
assert.equal(result.length, 1)
|
||||||
assert.equal(result[0].id, 1)
|
assert.equal(result[0].id, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('cuts off early txs beyond a limit', function() {
|
||||||
|
const limit = configManager.txLimit
|
||||||
|
for (let i = 0; i < limit + 1; i++) {
|
||||||
|
let tx = { id: i }
|
||||||
|
configManager.addTx(tx)
|
||||||
|
}
|
||||||
|
var result = configManager.getTxList()
|
||||||
|
assert.equal(result.length, limit, `limit of ${limit} txs enforced`)
|
||||||
|
assert.equal(result[0].id, 1, 'early txs truncted')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#confirmTx', function() {
|
describe('#confirmTx', function() {
|
||||||
@ -215,4 +313,3 @@ describe('config-manager', function() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -156,7 +156,12 @@ describe('util', function() {
|
|||||||
var result = util.formatBalance(input)
|
var result = util.formatBalance(input)
|
||||||
assert.equal(result, '0.00032 ETH')
|
assert.equal(result, '0.00032 ETH')
|
||||||
})
|
})
|
||||||
|
it('should not parse the balance and return value with 2 decimal points with ETH at the end', function() {
|
||||||
|
var value = '1.2456789'
|
||||||
|
var needsParse = false
|
||||||
|
var result = util.formatBalance(value, 2, needsParse)
|
||||||
|
assert.equal(result, '1.24 ETH')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('normalizing values', function() {
|
describe('normalizing values', function() {
|
||||||
|
@ -4,22 +4,24 @@ const Component = require('react').Component
|
|||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const CopyButton = require('./components/copyButton')
|
const CopyButton = require('./components/copyButton')
|
||||||
|
const AccountInfoLink = require('./components/account-info-link')
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
|
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
|
||||||
const valuesFor = require('./util').valuesFor
|
const valuesFor = require('./util').valuesFor
|
||||||
|
|
||||||
const Identicon = require('./components/identicon')
|
const Identicon = require('./components/identicon')
|
||||||
const EtherBalance = require('./components/eth-balance')
|
const AccountEtherBalance = require('./components/account-eth-balance')
|
||||||
const TransactionList = require('./components/transaction-list')
|
const TransactionList = require('./components/transaction-list')
|
||||||
const ExportAccountView = require('./components/account-export')
|
const ExportAccountView = require('./components/account-export')
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
const EditableLabel = require('./components/editable-label')
|
const EditableLabel = require('./components/editable-label')
|
||||||
const Tooltip = require('./components/tooltip')
|
const Tooltip = require('./components/tooltip')
|
||||||
|
const BuyButtonSubview = require('./components/buy-button-subview')
|
||||||
module.exports = connect(mapStateToProps)(AccountDetailScreen)
|
module.exports = connect(mapStateToProps)(AccountDetailScreen)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
return {
|
return {
|
||||||
|
metamask: state.metamask,
|
||||||
identities: state.metamask.identities,
|
identities: state.metamask.identities,
|
||||||
accounts: state.metamask.accounts,
|
accounts: state.metamask.accounts,
|
||||||
address: state.metamask.selectedAccount,
|
address: state.metamask.selectedAccount,
|
||||||
@ -29,6 +31,7 @@ function mapStateToProps (state) {
|
|||||||
unconfTxs: valuesFor(state.metamask.unconfTxs),
|
unconfTxs: valuesFor(state.metamask.unconfTxs),
|
||||||
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
|
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
|
||||||
isEthWarningConfirmed: state.metamask.isEthConfirmed,
|
isEthWarningConfirmed: state.metamask.isEthConfirmed,
|
||||||
|
shapeShiftTxList: state.metamask.shapeShiftTxList,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +45,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
var selected = props.address || Object.keys(props.accounts)[0]
|
var selected = props.address || Object.keys(props.accounts)[0]
|
||||||
var identity = props.identities[selected]
|
var identity = props.identities[selected]
|
||||||
var account = props.accounts[selected]
|
var account = props.accounts[selected]
|
||||||
|
const { network } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@ -125,6 +129,9 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
bottom: '15px',
|
bottom: '15px',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
|
h(AccountInfoLink, { selected, network }),
|
||||||
|
|
||||||
h(CopyButton, {
|
h(CopyButton, {
|
||||||
value: ethUtil.toChecksumAddress(selected),
|
value: ethUtil.toChecksumAddress(selected),
|
||||||
}),
|
}),
|
||||||
@ -134,16 +141,15 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
}, [
|
}, [
|
||||||
h('div', {
|
h('div', {
|
||||||
style: {
|
style: {
|
||||||
margin: '5px',
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
h('img.cursor-pointer.color-orange', {
|
h('img.cursor-pointer.color-orange', {
|
||||||
src: 'images/key-32.png',
|
src: 'images/key-32.png',
|
||||||
onClick: () => this.requestAccountExport(selected),
|
onClick: () => this.requestAccountExport(selected),
|
||||||
style: {
|
style: {
|
||||||
margin: '0px 5px',
|
height: '19px',
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
@ -162,9 +168,8 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
h(EtherBalance, {
|
h(AccountEtherBalance, {
|
||||||
value: account && account.balance,
|
value: account && account.balance,
|
||||||
mainBalance: true,
|
|
||||||
style: {
|
style: {
|
||||||
lineHeight: '7px',
|
lineHeight: '7px',
|
||||||
marginTop: '10px',
|
marginTop: '10px',
|
||||||
@ -172,7 +177,7 @@ AccountDetailScreen.prototype.render = function () {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
h('button', {
|
h('button', {
|
||||||
onClick: () => props.dispatch(actions.buyEth(selected)),
|
onClick: () => props.dispatch(actions.buyEthView(selected)),
|
||||||
style: {
|
style: {
|
||||||
marginBottom: '20px',
|
marginBottom: '20px',
|
||||||
marginRight: '8px',
|
marginRight: '8px',
|
||||||
@ -220,13 +225,15 @@ AccountDetailScreen.prototype.subview = function () {
|
|||||||
case 'export':
|
case 'export':
|
||||||
var state = extend({key: 'export'}, this.props)
|
var state = extend({key: 'export'}, this.props)
|
||||||
return h(ExportAccountView, state)
|
return h(ExportAccountView, state)
|
||||||
|
case 'buyForm':
|
||||||
|
return h(BuyButtonSubview, extend({key: 'buyForm'}, this.props))
|
||||||
default:
|
default:
|
||||||
return this.transactionList()
|
return this.transactionList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountDetailScreen.prototype.transactionList = function () {
|
AccountDetailScreen.prototype.transactionList = function () {
|
||||||
const { transactions, unconfTxs, unconfMsgs, address, network } = this.props
|
const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props
|
||||||
|
|
||||||
var txsToRender = transactions
|
var txsToRender = transactions
|
||||||
// only transactions that are from the current address
|
// only transactions that are from the current address
|
||||||
@ -241,6 +248,8 @@ AccountDetailScreen.prototype.transactionList = function () {
|
|||||||
network,
|
network,
|
||||||
unconfTxs,
|
unconfTxs,
|
||||||
unconfMsgs,
|
unconfMsgs,
|
||||||
|
address,
|
||||||
|
shapeShiftTxList,
|
||||||
viewPendingTx: (txId) => {
|
viewPendingTx: (txId) => {
|
||||||
this.props.dispatch(actions.viewPendingTx(txId))
|
this.props.dispatch(actions.viewPendingTx(txId))
|
||||||
},
|
},
|
||||||
@ -251,3 +260,13 @@ AccountDetailScreen.prototype.requestAccountExport = function () {
|
|||||||
this.props.dispatch(actions.requestExportAccount())
|
this.props.dispatch(actions.requestExportAccount())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AccountDetailScreen.prototype.buyButtonDeligator = function () {
|
||||||
|
var props = this.props
|
||||||
|
|
||||||
|
if (this.props.accountDetail.subview === 'buyForm') {
|
||||||
|
props.dispatch(actions.backToAccountDetail(props.address))
|
||||||
|
} else {
|
||||||
|
props.dispatch(actions.buyEthView())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@ const h = require('react-hyperscript')
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const ethUtil = require('ethereumjs-util')
|
const ethUtil = require('ethereumjs-util')
|
||||||
|
|
||||||
const EtherBalance = require('../components/eth-balance')
|
const AccountEtherBalance = require('../components/account-eth-balance')
|
||||||
const CopyButton = require('../components/copyButton')
|
const CopyButton = require('../components/copyButton')
|
||||||
const Identicon = require('../components/identicon')
|
const Identicon = require('../components/identicon')
|
||||||
|
|
||||||
@ -50,8 +50,12 @@ NewComponent.prototype.render = function () {
|
|||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
},
|
},
|
||||||
}, ethUtil.toChecksumAddress(identity.address)),
|
}, ethUtil.toChecksumAddress(identity.address)),
|
||||||
h(EtherBalance, {
|
h(AccountEtherBalance, {
|
||||||
value: account.balance,
|
value: account.balance,
|
||||||
|
style: {
|
||||||
|
lineHeight: '7px',
|
||||||
|
marginTop: '10px',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ var actions = {
|
|||||||
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
|
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
|
||||||
REVEAL_ACCOUNT: 'REVEAL_ACCOUNT',
|
REVEAL_ACCOUNT: 'REVEAL_ACCOUNT',
|
||||||
revealAccount: revealAccount,
|
revealAccount: revealAccount,
|
||||||
|
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
|
||||||
|
setCurrentFiat: setCurrentFiat,
|
||||||
// account detail screen
|
// account detail screen
|
||||||
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
||||||
showSendPage: showSendPage,
|
showSendPage: showSendPage,
|
||||||
@ -113,6 +115,34 @@ var actions = {
|
|||||||
// buy Eth with coinbase
|
// buy Eth with coinbase
|
||||||
BUY_ETH: 'BUY_ETH',
|
BUY_ETH: 'BUY_ETH',
|
||||||
buyEth: buyEth,
|
buyEth: buyEth,
|
||||||
|
buyEthView: buyEthView,
|
||||||
|
BUY_ETH_VIEW: 'BUY_ETH_VIEW',
|
||||||
|
UPDATE_COINBASE_AMOUNT: 'UPDATE_COIBASE_AMOUNT',
|
||||||
|
updateCoinBaseAmount: updateCoinBaseAmount,
|
||||||
|
UPDATE_BUY_ADDRESS: 'UPDATE_BUY_ADDRESS',
|
||||||
|
updateBuyAddress: updateBuyAddress,
|
||||||
|
COINBASE_SUBVIEW: 'COINBASE_SUBVIEW',
|
||||||
|
coinBaseSubview: coinBaseSubview,
|
||||||
|
SHAPESHIFT_SUBVIEW: 'SHAPESHIFT_SUBVIEW',
|
||||||
|
shapeShiftSubview: shapeShiftSubview,
|
||||||
|
PAIR_UPDATE: 'PAIR_UPDATE',
|
||||||
|
pairUpdate: pairUpdate,
|
||||||
|
coinShiftRquest: coinShiftRquest,
|
||||||
|
SHOW_SUB_LOADING_INDICATION: 'SHOW_SUB_LOADING_INDICATION',
|
||||||
|
showSubLoadingIndication: showSubLoadingIndication,
|
||||||
|
HIDE_SUB_LOADING_INDICATION: 'HIDE_SUB_LOADING_INDICATION',
|
||||||
|
hideSubLoadingIndication: hideSubLoadingIndication,
|
||||||
|
// QR STUFF:
|
||||||
|
SHOW_QR: 'SHOW_QR',
|
||||||
|
getQr: getQr,
|
||||||
|
reshowQrCode: reshowQrCode,
|
||||||
|
SHOW_QR_VIEW: 'SHOW_QR_VIEW',
|
||||||
|
// FORGOT PASSWORD:
|
||||||
|
BACK_TO_INIT_MENU: 'BACK_TO_INIT_MENU',
|
||||||
|
goBackToInitView: goBackToInitView,
|
||||||
|
RECOVERY_IN_PROGRESS: 'RECOVERY_IN_PROGRESS',
|
||||||
|
BACK_TO_UNLOCK_VIEW: 'BACK_TO_UNLOCK_VIEW',
|
||||||
|
backToUnlockView: backToUnlockView,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = actions
|
module.exports = actions
|
||||||
@ -214,6 +244,23 @@ function revealAccount () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setCurrentFiat (fiat) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(this.showLoadingIndication())
|
||||||
|
_accountManager.setCurrentFiat(fiat, (data, err) => {
|
||||||
|
dispatch(this.hideLoadingIndication())
|
||||||
|
dispatch({
|
||||||
|
type: this.SET_CURRENT_FIAT,
|
||||||
|
value: {
|
||||||
|
currentFiat: data.currentFiat,
|
||||||
|
conversionRate: data.conversionRate,
|
||||||
|
conversionDate: data.conversionDate,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function signMsg (msgData) {
|
function signMsg (msgData) {
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
dispatch(actions.showLoadingIndication())
|
dispatch(actions.showLoadingIndication())
|
||||||
@ -329,6 +376,12 @@ function showNewVaultSeed (seed) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function backToUnlockView () {
|
||||||
|
return {
|
||||||
|
type: actions.BACK_TO_UNLOCK_VIEW,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// unlock screen
|
// unlock screen
|
||||||
//
|
//
|
||||||
@ -457,6 +510,12 @@ function showConfigPage (transitionForward = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goBackToInitView () {
|
||||||
|
return {
|
||||||
|
type: actions.BACK_TO_INIT_MENU,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// config
|
// config
|
||||||
//
|
//
|
||||||
@ -496,6 +555,18 @@ function hideLoadingIndication () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showSubLoadingIndication () {
|
||||||
|
return {
|
||||||
|
type: actions.SHOW_SUB_LOADING_INDICATION,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSubLoadingIndication () {
|
||||||
|
return {
|
||||||
|
type: actions.HIDE_SUB_LOADING_INDICATION,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showWarning (text) {
|
function showWarning (text) {
|
||||||
return this.displayWarning(text)
|
return this.displayWarning(text)
|
||||||
}
|
}
|
||||||
@ -594,3 +665,159 @@ function buyEth (address, amount) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buyEthView (address) {
|
||||||
|
return {
|
||||||
|
type: actions.BUY_ETH_VIEW,
|
||||||
|
value: address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCoinBaseAmount (value) {
|
||||||
|
return {
|
||||||
|
type: actions.UPDATE_COINBASE_AMOUNT,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBuyAddress (value) {
|
||||||
|
return {
|
||||||
|
type: actions.UPDATE_BUY_ADDRESS,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function coinBaseSubview () {
|
||||||
|
return {
|
||||||
|
type: actions.COINBASE_SUBVIEW,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pairUpdate (coin) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(actions.showSubLoadingIndication())
|
||||||
|
dispatch(actions.hideWarning())
|
||||||
|
shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => {
|
||||||
|
dispatch(actions.hideSubLoadingIndication())
|
||||||
|
dispatch({
|
||||||
|
type: actions.PAIR_UPDATE,
|
||||||
|
value: {
|
||||||
|
marketinfo: mktResponse,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shapeShiftSubview (network) {
|
||||||
|
var pair = 'btc_eth'
|
||||||
|
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(actions.showSubLoadingIndication())
|
||||||
|
shapeShiftRequest('marketinfo', {pair}, (mktResponse) => {
|
||||||
|
shapeShiftRequest('getcoins', {}, (response) => {
|
||||||
|
dispatch(actions.hideSubLoadingIndication())
|
||||||
|
if (mktResponse.error) return dispatch(actions.showWarning(mktResponse.error))
|
||||||
|
dispatch({
|
||||||
|
type: actions.SHAPESHIFT_SUBVIEW,
|
||||||
|
value: {
|
||||||
|
marketinfo: mktResponse,
|
||||||
|
coinOptions: response,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function coinShiftRquest (data, marketData) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(actions.showLoadingIndication())
|
||||||
|
shapeShiftRequest('shift', { method: 'POST', data}, (response) => {
|
||||||
|
if (response.error) return dispatch(actions.showWarning(response.error))
|
||||||
|
var message = `
|
||||||
|
Deposit your ${response.depositType} to the address bellow:`
|
||||||
|
_accountManager.createShapeShiftTx(response.deposit, response.depositType)
|
||||||
|
dispatch(actions.getQr(response.deposit, '125x125', [message].concat(marketData)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQr (data, size, message) {
|
||||||
|
return (dispatch) => {
|
||||||
|
qrRequest(data, size, (response) => {
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
|
if (response.error) return dispatch(actions.showWarning(response.error))
|
||||||
|
dispatch({
|
||||||
|
type: actions.SHOW_QR,
|
||||||
|
value: {
|
||||||
|
qr: response,
|
||||||
|
message: message,
|
||||||
|
data: data,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function reshowQrCode (data, coin) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(actions.showLoadingIndication())
|
||||||
|
shapeShiftRequest('marketinfo', {pair: `${coin.toLowerCase()}_eth`}, (mktResponse) => {
|
||||||
|
if (mktResponse.error) return dispatch(actions.showWarning(mktResponse.error))
|
||||||
|
|
||||||
|
var message = [
|
||||||
|
`Deposit your ${coin} to the address bellow:`,
|
||||||
|
`Deposit Limit: ${mktResponse.limit}`,
|
||||||
|
`Deposit Minimum:${mktResponse.minimum}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
qrRequest(data, '125x125', (response) => {
|
||||||
|
dispatch(actions.hideLoadingIndication())
|
||||||
|
dispatch({
|
||||||
|
type: actions.SHOW_QR_VIEW,
|
||||||
|
value: {
|
||||||
|
qr: response,
|
||||||
|
message: message,
|
||||||
|
data: data,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shapeShiftRequest (query, options, cb) {
|
||||||
|
var queryResponse, method
|
||||||
|
!options ? options = {} : null
|
||||||
|
options.method ? method = options.method : method = 'GET'
|
||||||
|
|
||||||
|
var requestListner = function (request) {
|
||||||
|
queryResponse = JSON.parse(this.responseText)
|
||||||
|
cb ? cb(queryResponse) : null
|
||||||
|
return queryResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
var shapShiftReq = new XMLHttpRequest()
|
||||||
|
shapShiftReq.addEventListener('load', requestListner)
|
||||||
|
shapShiftReq.open(method, `https://shapeshift.io/${query}/${options.pair ? options.pair : ''}`, true)
|
||||||
|
|
||||||
|
if (options.method === 'POST') {
|
||||||
|
var jsonObj = JSON.stringify(options.data)
|
||||||
|
shapShiftReq.setRequestHeader('Content-Type', 'application/json')
|
||||||
|
return shapShiftReq.send(jsonObj)
|
||||||
|
} else {
|
||||||
|
return shapShiftReq.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function qrRequest (data, size, cb) {
|
||||||
|
var requestListner = function (request) {
|
||||||
|
cb ? cb(this.responseText) : null
|
||||||
|
return this.responseText
|
||||||
|
}
|
||||||
|
|
||||||
|
var qrReq = new XMLHttpRequest()
|
||||||
|
qrReq.addEventListener('load', requestListner)
|
||||||
|
qrReq.open('GET', `https://api.qrserver.com/v1/create-qr-code/?size=${size}&format=svg&data=${data}`, true)
|
||||||
|
qrReq.send()
|
||||||
|
}
|
||||||
|
151
ui/app/app.js
151
ui/app/app.js
@ -28,7 +28,8 @@ const DropMenuItem = require('./components/drop-menu-item')
|
|||||||
const NetworkIndicator = require('./components/network')
|
const NetworkIndicator = require('./components/network')
|
||||||
const Tooltip = require('./components/tooltip')
|
const Tooltip = require('./components/tooltip')
|
||||||
const EthStoreWarning = require('./eth-store-warning')
|
const EthStoreWarning = require('./eth-store-warning')
|
||||||
|
const BuyView = require('./components/buy-button-subview')
|
||||||
|
const QrView = require('./components/qr-code')
|
||||||
module.exports = connect(mapStateToProps)(App)
|
module.exports = connect(mapStateToProps)(App)
|
||||||
|
|
||||||
inherits(App, Component)
|
inherits(App, Component)
|
||||||
@ -50,6 +51,7 @@ function mapStateToProps (state) {
|
|||||||
menuOpen: state.appState.menuOpen,
|
menuOpen: state.appState.menuOpen,
|
||||||
network: state.metamask.network,
|
network: state.metamask.network,
|
||||||
provider: state.metamask.provider,
|
provider: state.metamask.provider,
|
||||||
|
forgottenPassword: state.appState.forgottenPassword,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +90,7 @@ App.prototype.render = function () {
|
|||||||
transitionLeaveTimeout: 300,
|
transitionLeaveTimeout: 300,
|
||||||
}, [
|
}, [
|
||||||
this.renderPrimary(),
|
this.renderPrimary(),
|
||||||
|
this.renderBackToInitButton(),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
@ -95,6 +98,11 @@ App.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
App.prototype.renderAppBar = function () {
|
App.prototype.renderAppBar = function () {
|
||||||
|
|
||||||
|
if (window.METAMASK_UI_TYPE === 'notification') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const props = this.props
|
const props = this.props
|
||||||
const state = this.state || {}
|
const state = this.state || {}
|
||||||
const isNetworkMenuOpen = state.isNetworkMenuOpen || false
|
const isNetworkMenuOpen = state.isNetworkMenuOpen || false
|
||||||
@ -225,15 +233,6 @@ App.prototype.renderNetworkDropdown = function () {
|
|||||||
provider: props.provider,
|
provider: props.provider,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h(DropMenuItem, {
|
|
||||||
label: 'Ethereum Classic Network',
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
|
||||||
action: () => props.dispatch(actions.setProviderType('classic')),
|
|
||||||
icon: h('.menu-icon.hollow-diamond'),
|
|
||||||
activeNetworkRender: props.network,
|
|
||||||
provider: props.provider,
|
|
||||||
}),
|
|
||||||
|
|
||||||
h(DropMenuItem, {
|
h(DropMenuItem, {
|
||||||
label: 'Morden Test Network',
|
label: 'Morden Test Network',
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
@ -250,6 +249,13 @@ App.prototype.renderNetworkDropdown = function () {
|
|||||||
activeNetworkRender: props.provider.rpcTarget,
|
activeNetworkRender: props.provider.rpcTarget,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
h(DropMenuItem, {
|
||||||
|
label: 'Custom RPC',
|
||||||
|
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||||
|
action: () => this.props.dispatch(actions.showConfigPage()),
|
||||||
|
icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
|
||||||
|
}),
|
||||||
|
|
||||||
this.renderCustomOption(props.provider.rpcTarget),
|
this.renderCustomOption(props.provider.rpcTarget),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@ -301,6 +307,92 @@ App.prototype.renderDropdown = function () {
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
App.prototype.renderBackButton = function (style, justArrow = false) {
|
||||||
|
var props = this.props
|
||||||
|
return (
|
||||||
|
h('.flex-row', {
|
||||||
|
key: 'leftArrow',
|
||||||
|
transForward: false,
|
||||||
|
style: style,
|
||||||
|
onClick: () => props.dispatch(actions.goBackToInitView()),
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-arrow-left.cursor-pointer'),
|
||||||
|
justArrow ? null : h('div.cursor-pointer', {
|
||||||
|
style: {
|
||||||
|
marginLeft: '3px',
|
||||||
|
},
|
||||||
|
onClick: () => props.dispatch(actions.goBackToInitView()),
|
||||||
|
}, 'BACK'),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
App.prototype.renderBackToInitButton = function () {
|
||||||
|
var props = this.props
|
||||||
|
var button = null
|
||||||
|
if (!props.isUnlocked) {
|
||||||
|
if (props.currentView.name === 'InitMenu') {
|
||||||
|
button = props.forgottenPassword ? h('.flex-row', {
|
||||||
|
key: 'rightArrow',
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '10px',
|
||||||
|
right: '15px',
|
||||||
|
fontSize: '21px',
|
||||||
|
fontFamily: 'Montserrat Light',
|
||||||
|
color: '#7F8082',
|
||||||
|
width: '77.578px',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('div.cursor-pointer', {
|
||||||
|
style: {
|
||||||
|
marginRight: '3px',
|
||||||
|
},
|
||||||
|
onClick: () => props.dispatch(actions.backToUnlockView()),
|
||||||
|
}, 'LOGIN'),
|
||||||
|
h('i.fa.fa-arrow-right.cursor-pointer'),
|
||||||
|
]) : null
|
||||||
|
} else if (props.isInitialized) {
|
||||||
|
var style
|
||||||
|
switch (props.currentView.name) {
|
||||||
|
case 'createVault':
|
||||||
|
style = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '41px',
|
||||||
|
left: '80px',
|
||||||
|
fontSize: '21px',
|
||||||
|
fontFamily: 'Montserrat Bold',
|
||||||
|
color: 'rgb(174, 174, 174)',
|
||||||
|
}
|
||||||
|
return this.renderBackButton(style, true)
|
||||||
|
case 'restoreVault':
|
||||||
|
style = {
|
||||||
|
position: 'absolute',
|
||||||
|
top: '41px',
|
||||||
|
left: '70px',
|
||||||
|
fontSize: '21px',
|
||||||
|
fontFamily: 'Montserrat Bold',
|
||||||
|
color: 'rgb(174, 174, 174)',
|
||||||
|
}
|
||||||
|
return this.renderBackButton(style, true)
|
||||||
|
default:
|
||||||
|
style = {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '10px',
|
||||||
|
left: '15px',
|
||||||
|
fontSize: '21px',
|
||||||
|
fontFamily: 'Montserrat Light',
|
||||||
|
color: '#7F8082',
|
||||||
|
width: '71.969px',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
}
|
||||||
|
return this.renderBackButton(style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
App.prototype.renderPrimary = function () {
|
App.prototype.renderPrimary = function () {
|
||||||
var props = this.props
|
var props = this.props
|
||||||
@ -314,7 +406,7 @@ App.prototype.renderPrimary = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show initialize screen
|
// show initialize screen
|
||||||
if (!props.isInitialized) {
|
if (!props.isInitialized || props.forgottenPassword) {
|
||||||
// show current view
|
// show current view
|
||||||
switch (props.currentView.name) {
|
switch (props.currentView.name) {
|
||||||
|
|
||||||
@ -366,6 +458,35 @@ App.prototype.renderPrimary = function () {
|
|||||||
|
|
||||||
case 'createVault':
|
case 'createVault':
|
||||||
return h(CreateVaultScreen, {key: 'createVault'})
|
return h(CreateVaultScreen, {key: 'createVault'})
|
||||||
|
case 'buyEth':
|
||||||
|
return h(BuyView, {key: 'buyEthView'})
|
||||||
|
case 'qr':
|
||||||
|
return h('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
height: '100%',
|
||||||
|
top: '0px',
|
||||||
|
left: '0px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
|
||||||
|
onClick: () => props.dispatch(actions.backToAccountDetail(props.activeAddress)),
|
||||||
|
style: {
|
||||||
|
marginLeft: '10px',
|
||||||
|
marginTop: '50px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '115px',
|
||||||
|
left: '44px',
|
||||||
|
width: '285px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h(QrView, {key: 'qr'}),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return h(AccountDetailScreen, {key: 'account-detail'})
|
return h(AccountDetailScreen, {key: 'account-detail'})
|
||||||
@ -387,12 +508,8 @@ App.prototype.toggleMetamaskActive = function () {
|
|||||||
App.prototype.renderCustomOption = function (rpcTarget) {
|
App.prototype.renderCustomOption = function (rpcTarget) {
|
||||||
switch (rpcTarget) {
|
switch (rpcTarget) {
|
||||||
case undefined:
|
case undefined:
|
||||||
return h(DropMenuItem, {
|
return null
|
||||||
label: 'Custom RPC',
|
|
||||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
|
||||||
action: () => this.props.dispatch(actions.showConfigPage()),
|
|
||||||
icon: h('i.fa.fa-question-circle.fa-lg', { ariaHidden: true }),
|
|
||||||
})
|
|
||||||
case 'http://localhost:8545':
|
case 'http://localhost:8545':
|
||||||
return h(DropMenuItem, {
|
return h(DropMenuItem, {
|
||||||
label: 'Custom RPC',
|
label: 'Custom RPC',
|
||||||
|
140
ui/app/components/account-eth-balance.js
Normal file
140
ui/app/components/account-eth-balance.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const formatBalance = require('../util').formatBalance
|
||||||
|
const generateBalanceObject = require('../util').generateBalanceObject
|
||||||
|
const Tooltip = require('./tooltip.js')
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(EthBalanceComponent)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
conversionRate: state.metamask.conversionRate,
|
||||||
|
conversionDate: state.metamask.conversionDate,
|
||||||
|
currentFiat: state.metamask.currentFiat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(EthBalanceComponent, Component)
|
||||||
|
function EthBalanceComponent () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
EthBalanceComponent.prototype.render = function () {
|
||||||
|
var state = this.props
|
||||||
|
var style = state.style
|
||||||
|
|
||||||
|
const value = formatBalance(state.value, 6)
|
||||||
|
var width = state.width
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
h('.ether-balance', {
|
||||||
|
style: style,
|
||||||
|
}, [
|
||||||
|
h('.ether-balance-amount', {
|
||||||
|
style: {
|
||||||
|
display: 'inline',
|
||||||
|
width: width,
|
||||||
|
},
|
||||||
|
}, this.renderBalance(value, state)),
|
||||||
|
])
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
EthBalanceComponent.prototype.renderBalance = function (value, state) {
|
||||||
|
if (value === 'None') return value
|
||||||
|
var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
|
||||||
|
var balance, fiatDisplayNumber, fiatTooltipNumber
|
||||||
|
var splitBalance = value.split(' ')
|
||||||
|
var ethNumber = splitBalance[0]
|
||||||
|
var ethSuffix = splitBalance[1]
|
||||||
|
|
||||||
|
|
||||||
|
if (state.conversionRate !== 0) {
|
||||||
|
fiatTooltipNumber = Number(splitBalance[0]) * state.conversionRate
|
||||||
|
fiatDisplayNumber = fiatTooltipNumber.toFixed(2)
|
||||||
|
} else {
|
||||||
|
fiatDisplayNumber = 'N/A'
|
||||||
|
}
|
||||||
|
|
||||||
|
var fiatSuffix = state.currentFiat
|
||||||
|
|
||||||
|
if (state.shorten) {
|
||||||
|
balance = balanceObj.shortBalance
|
||||||
|
} else {
|
||||||
|
balance = balanceObj.balance
|
||||||
|
}
|
||||||
|
|
||||||
|
var label = balanceObj.label
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('.flex-column', [
|
||||||
|
h(Tooltip, {
|
||||||
|
position: 'bottom',
|
||||||
|
title: `${ethNumber} ${ethSuffix}`,
|
||||||
|
}, [
|
||||||
|
h('.flex-row', {
|
||||||
|
style: {
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
lineHeight: '13px',
|
||||||
|
fontFamily: 'Montserrat Light',
|
||||||
|
textRendering: 'geometricPrecision',
|
||||||
|
marginBottom: '5px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
textAlign: 'right',
|
||||||
|
},
|
||||||
|
}, balance),
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
color: '#AEAEAE',
|
||||||
|
marginLeft: '5px',
|
||||||
|
},
|
||||||
|
}, label),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
h(Tooltip, {
|
||||||
|
position: 'bottom',
|
||||||
|
title: `${fiatTooltipNumber} ${fiatSuffix}`,
|
||||||
|
}, [
|
||||||
|
fiatDisplay(fiatDisplayNumber, fiatSuffix),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
42
ui/app/components/account-info-link.js
Normal file
42
ui/app/components/account-info-link.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const Tooltip = require('./tooltip')
|
||||||
|
const genAccountLink = require('../../lib/account-link')
|
||||||
|
const extension = require('../../../app/scripts/lib/extension')
|
||||||
|
|
||||||
|
module.exports = AccountInfoLink
|
||||||
|
|
||||||
|
inherits(AccountInfoLink, Component)
|
||||||
|
function AccountInfoLink () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountInfoLink.prototype.render = function () {
|
||||||
|
const { selected, network } = this.props
|
||||||
|
const title = 'View account on etherscan'
|
||||||
|
const url = genAccountLink(selected, network)
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return h('.account-info-link', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
|
||||||
|
h(Tooltip, {
|
||||||
|
title,
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-info-circle.cursor-pointer.color-orange', {
|
||||||
|
style: {
|
||||||
|
margin: '5px',
|
||||||
|
},
|
||||||
|
onClick () { extension.tabs.create({ url }) },
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
123
ui/app/components/buy-button-subview.js
Normal file
123
ui/app/components/buy-button-subview.js
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const actions = require('../actions')
|
||||||
|
const CoinbaseForm = require('./coinbase-form')
|
||||||
|
const ShapeshiftForm = require('./shapeshift-form')
|
||||||
|
const extension = require('../../../app/scripts/lib/extension')
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(BuyButtonSubview)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
selectedAccount: state.selectedAccount,
|
||||||
|
warning: state.appState.warning,
|
||||||
|
buyView: state.appState.buyView,
|
||||||
|
network: state.metamask.network,
|
||||||
|
provider: state.metamask.provider,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(BuyButtonSubview, Component)
|
||||||
|
function BuyButtonSubview () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
BuyButtonSubview.prototype.render = function () {
|
||||||
|
const props = this.props
|
||||||
|
const currentForm = props.buyView.formView
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('.buy-eth-section', [
|
||||||
|
// back button
|
||||||
|
h('.flex-row', {
|
||||||
|
style: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer.color-orange', {
|
||||||
|
onClick: () => props.dispatch(actions.backToAccountDetail(props.selectedAccount)),
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
left: '10px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('h2.page-subtitle', 'Buy Eth'),
|
||||||
|
]),
|
||||||
|
h('h3.flex-row.text-transform-uppercase', {
|
||||||
|
style: {
|
||||||
|
background: '#EBEBEB',
|
||||||
|
color: '#AEAEAE',
|
||||||
|
paddingTop: '4px',
|
||||||
|
justifyContent: 'space-around',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h(currentForm.coinbase ? '.activeForm' : '.inactiveForm.pointer', {
|
||||||
|
onClick: () => props.dispatch(actions.coinBaseSubview()),
|
||||||
|
}, 'Coinbase'),
|
||||||
|
h('a', {
|
||||||
|
onClick: (event) => this.navigateTo('https://github.com/MetaMask/faq/blob/master/COINBASE.md'),
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle', {
|
||||||
|
style: {
|
||||||
|
position: 'relative',
|
||||||
|
right: '33px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
h(currentForm.shapeshift ? '.activeForm' : '.inactiveForm.pointer', {
|
||||||
|
onClick: () => props.dispatch(actions.shapeShiftSubview(props.provider.type)),
|
||||||
|
}, 'Shapeshift'),
|
||||||
|
|
||||||
|
h('a', {
|
||||||
|
href: 'https://github.com/MetaMask/faq/blob/master/COINBASE.md',
|
||||||
|
onClick: (event) => this.navigateTo('https://info.shapeshift.io/about'),
|
||||||
|
}, [
|
||||||
|
h('i.fa.fa-question-circle', {
|
||||||
|
style: {
|
||||||
|
position: 'relative',
|
||||||
|
right: '28px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
this.formVersionSubview(),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BuyButtonSubview.prototype.formVersionSubview = function () {
|
||||||
|
if (this.props.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)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return h('div.flex-column', {
|
||||||
|
style: {
|
||||||
|
alignItems: 'center',
|
||||||
|
margin: '50px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('h3.text-transform-uppercase', {
|
||||||
|
style: {
|
||||||
|
width: '225px',
|
||||||
|
},
|
||||||
|
}, 'In order to access this feature please switch too the Main Network'),
|
||||||
|
h('h3.text-transform-uppercase', 'or:'),
|
||||||
|
this.props.network === '2' ? h('button.text-transform-uppercase', {
|
||||||
|
onClick: () => this.props.dispatch(actions.buyEth()),
|
||||||
|
style: {
|
||||||
|
marginTop: '15px',
|
||||||
|
},
|
||||||
|
}, 'Go To Test Faucet') : null,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BuyButtonSubview.prototype.navigateTo = function (url) {
|
||||||
|
extension.tabs.create({ url })
|
||||||
|
}
|
162
ui/app/components/coinbase-form.js
Normal file
162
ui/app/components/coinbase-form.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const actions = require('../actions')
|
||||||
|
|
||||||
|
const isValidAddress = require('../util').isValidAddress
|
||||||
|
module.exports = connect(mapStateToProps)(CoinbaseForm)
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
selectedAccount: state.selectedAccount,
|
||||||
|
warning: state.appState.warning,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(CoinbaseForm, Component)
|
||||||
|
|
||||||
|
function CoinbaseForm() {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
CoinbaseForm.prototype.render = function () {
|
||||||
|
var props = this.props
|
||||||
|
var amount = props.buyView.amount
|
||||||
|
var address = props.buyView.buyAddress
|
||||||
|
|
||||||
|
return h('.flex-column', {
|
||||||
|
style: {
|
||||||
|
// margin: '10px',
|
||||||
|
padding: '25px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.flex-column', {
|
||||||
|
style: {
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.flex-row', [
|
||||||
|
h('div', 'Address:'),
|
||||||
|
h('.ellip-address', address),
|
||||||
|
]),
|
||||||
|
h('.flex-row', [
|
||||||
|
h('div', 'Amount: $'),
|
||||||
|
h('.input-container', [
|
||||||
|
h('input.buy-inputs', {
|
||||||
|
style: {
|
||||||
|
width: '3em',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
},
|
||||||
|
defaultValue: amount,
|
||||||
|
onChange: this.handleAmount.bind(this),
|
||||||
|
}),
|
||||||
|
h('i.fa.fa-pencil-square-o.edit-text', {
|
||||||
|
style: {
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#F7861C',
|
||||||
|
position: 'relative',
|
||||||
|
bottom: '5px',
|
||||||
|
right: '11px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('.info-gray', {
|
||||||
|
style: {
|
||||||
|
fontSize: '10px',
|
||||||
|
fontFamily: 'Montserrat Light',
|
||||||
|
margin: '15px',
|
||||||
|
lineHeight: '13px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`there is a USD$ 5 a day max and a USD$ 50
|
||||||
|
dollar limit per the life time of an account without a
|
||||||
|
coinbase account. A fee of 3.75% will be aplied to debit/credit cards.`),
|
||||||
|
|
||||||
|
!props.warning ? h('div', {
|
||||||
|
style: {
|
||||||
|
width: '340px',
|
||||||
|
height: '22px',
|
||||||
|
},
|
||||||
|
}) : props.warning && h('span.error.flex-center', props.warning),
|
||||||
|
|
||||||
|
|
||||||
|
h('.flex-row', {
|
||||||
|
style: {
|
||||||
|
justifyContent: 'space-around',
|
||||||
|
margin: '33px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('button', {
|
||||||
|
onClick: this.toCoinbase.bind(this),
|
||||||
|
}, 'Continue to Coinbase'),
|
||||||
|
|
||||||
|
h('button', {
|
||||||
|
onClick: () => props.dispatch(actions.backTobuyView(props.accounts.address)),
|
||||||
|
}, 'Cancel'),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
CoinbaseForm.prototype.handleAmount = function (event) {
|
||||||
|
this.props.dispatch(actions.updateCoinBaseAmount(event.target.value))
|
||||||
|
}
|
||||||
|
CoinbaseForm.prototype.handleAddress = function (event) {
|
||||||
|
this.props.dispatch(actions.updateBuyAddress(event.target.value))
|
||||||
|
}
|
||||||
|
CoinbaseForm.prototype.toCoinbase = function () {
|
||||||
|
var props = this.props
|
||||||
|
var amount = props.buyView.amount
|
||||||
|
var address = props.buyView.buyAddress
|
||||||
|
var message
|
||||||
|
|
||||||
|
if (isValidAddress(address) && isValidAmountforCoinBase(amount).valid) {
|
||||||
|
props.dispatch(actions.buyEth(address, props.buyView.amount))
|
||||||
|
} else if (!isValidAmountforCoinBase(amount).valid) {
|
||||||
|
message = isValidAmountforCoinBase(amount).message
|
||||||
|
return props.dispatch(actions.showWarning(message))
|
||||||
|
} else {
|
||||||
|
message = 'Receiving address is invalid.'
|
||||||
|
return props.dispatch(actions.showWarning(message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CoinbaseForm.prototype.renderLoading = function () {
|
||||||
|
|
||||||
|
return h('img', {
|
||||||
|
style: {
|
||||||
|
width: '27px',
|
||||||
|
marginRight: '-27px',
|
||||||
|
},
|
||||||
|
src: 'images/loading.svg',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidAmountforCoinBase(amount) {
|
||||||
|
amount = parseFloat(amount)
|
||||||
|
|
||||||
|
if (amount) {
|
||||||
|
if (amount <= 5 && amount > 0) {
|
||||||
|
return {
|
||||||
|
valid: true,
|
||||||
|
}
|
||||||
|
} else if (amount > 5) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'The amount can not be greater then $5',
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'Can not buy amounts less then $0',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
message: 'The amount entered is not a number',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -41,9 +41,6 @@ DropMenuItem.prototype.activeNetworkRender = function () {
|
|||||||
case 'Main Ethereum Network':
|
case 'Main Ethereum Network':
|
||||||
if (providerType === 'mainnet') return h('.check', '✓')
|
if (providerType === 'mainnet') return h('.check', '✓')
|
||||||
break
|
break
|
||||||
case 'Ethereum Classic Network':
|
|
||||||
if (providerType === 'classic') return h('.check', '✓')
|
|
||||||
break
|
|
||||||
case 'Morden Test Network':
|
case 'Morden Test Network':
|
||||||
if (activeNetwork === '2') return h('.check', '✓')
|
if (activeNetwork === '2') return h('.check', '✓')
|
||||||
break
|
break
|
||||||
|
@ -4,6 +4,7 @@ const inherits = require('util').inherits
|
|||||||
const formatBalance = require('../util').formatBalance
|
const formatBalance = require('../util').formatBalance
|
||||||
const generateBalanceObject = require('../util').generateBalanceObject
|
const generateBalanceObject = require('../util').generateBalanceObject
|
||||||
const Tooltip = require('./tooltip.js')
|
const Tooltip = require('./tooltip.js')
|
||||||
|
|
||||||
module.exports = EthBalanceComponent
|
module.exports = EthBalanceComponent
|
||||||
|
|
||||||
inherits(EthBalanceComponent, Component)
|
inherits(EthBalanceComponent, Component)
|
||||||
@ -14,29 +15,33 @@ function EthBalanceComponent () {
|
|||||||
EthBalanceComponent.prototype.render = function () {
|
EthBalanceComponent.prototype.render = function () {
|
||||||
var state = this.props
|
var state = this.props
|
||||||
var style = state.style
|
var style = state.style
|
||||||
|
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
|
||||||
const value = formatBalance(state.value, 6)
|
const value = formatBalance(state.value, 6, needsParse)
|
||||||
var width = state.width
|
var width = state.width
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
h('.ether-balance', {
|
h('.ether-balance.ether-balance-amount', {
|
||||||
style: style,
|
style: style,
|
||||||
}, [
|
}, [
|
||||||
h('.ether-balance-amount', {
|
h('div', {
|
||||||
style: {
|
style: {
|
||||||
display: 'inline',
|
display: 'inline',
|
||||||
width: width,
|
width: width,
|
||||||
},
|
},
|
||||||
}, this.renderBalance(value, state)),
|
}, this.renderBalance(value)),
|
||||||
])
|
])
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
EthBalanceComponent.prototype.renderBalance = function (value, state) {
|
EthBalanceComponent.prototype.renderBalance = function (value) {
|
||||||
|
var state = this.props
|
||||||
if (value === 'None') return value
|
if (value === 'None') return value
|
||||||
var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
|
var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
|
||||||
var balance
|
var balance
|
||||||
|
var splitBalance = value.split(' ')
|
||||||
|
var ethNumber = splitBalance[0]
|
||||||
|
var ethSuffix = splitBalance[1]
|
||||||
|
|
||||||
if (state.shorten) {
|
if (state.shorten) {
|
||||||
balance = balanceObj.shortBalance
|
balance = balanceObj.shortBalance
|
||||||
@ -49,7 +54,7 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) {
|
|||||||
return (
|
return (
|
||||||
h(Tooltip, {
|
h(Tooltip, {
|
||||||
position: 'bottom',
|
position: 'bottom',
|
||||||
title: value.split(' ')[0],
|
title: `${ethNumber} ${ethSuffix}`,
|
||||||
}, [
|
}, [
|
||||||
h('.flex-column', {
|
h('.flex-column', {
|
||||||
style: {
|
style: {
|
||||||
@ -64,7 +69,7 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
},
|
},
|
||||||
}, balance),
|
}, this.props.incoming ? `+${balance}` : balance),
|
||||||
h('div', {
|
h('div', {
|
||||||
style: {
|
style: {
|
||||||
color: ' #AEAEAE',
|
color: ' #AEAEAE',
|
||||||
|
@ -14,8 +14,9 @@ function Mascot () {
|
|||||||
pxNotRatio: true,
|
pxNotRatio: true,
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 200,
|
height: 200,
|
||||||
|
staticImage: './images/icon-512.png',
|
||||||
})
|
})
|
||||||
if (!this.logo) return
|
if (!this.logo.webGLSupport) return
|
||||||
this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000)
|
this.refollowMouse = debounce(this.logo.setFollowMouse.bind(this.logo, true), 1000)
|
||||||
this.unfollowMouse = this.logo.setFollowMouse.bind(this.logo, false)
|
this.unfollowMouse = this.logo.setFollowMouse.bind(this.logo, false)
|
||||||
}
|
}
|
||||||
@ -34,19 +35,24 @@ Mascot.prototype.render = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mascot.prototype.componentDidMount = function () {
|
Mascot.prototype.componentDidMount = function () {
|
||||||
if (!this.logo) return
|
|
||||||
var targetDivId = 'metamask-mascot-container'
|
var targetDivId = 'metamask-mascot-container'
|
||||||
var container = document.getElementById(targetDivId)
|
var container = document.getElementById(targetDivId)
|
||||||
container.appendChild(this.logo.canvas)
|
if (!this.logo.webGLSupport) {
|
||||||
|
var staticLogo = this.logo.staticLogo
|
||||||
|
staticLogo.style.marginBottom = '40px'
|
||||||
|
container.appendChild(staticLogo)
|
||||||
|
} else {
|
||||||
|
container.appendChild(this.logo.canvas)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mascot.prototype.componentWillUnmount = function () {
|
Mascot.prototype.componentWillUnmount = function () {
|
||||||
if (!this.logo) return
|
if (!this.logo.webGLSupport) return
|
||||||
this.logo.canvas.remove()
|
this.logo.canvas.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
Mascot.prototype.handleAnimationEvents = function () {
|
Mascot.prototype.handleAnimationEvents = function () {
|
||||||
if (!this.logo) return
|
if (!this.logo.webGLSupport) return
|
||||||
// only setup listeners once
|
// only setup listeners once
|
||||||
if (this.animations) return
|
if (this.animations) return
|
||||||
this.animations = this.props.animationEventEmitter
|
this.animations = this.props.animationEventEmitter
|
||||||
@ -55,7 +61,7 @@ Mascot.prototype.handleAnimationEvents = function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mascot.prototype.lookAt = function (target) {
|
Mascot.prototype.lookAt = function (target) {
|
||||||
if (!this.logo) return
|
if (!this.logo.webGLSupport) return
|
||||||
this.unfollowMouse()
|
this.unfollowMouse()
|
||||||
this.logo.lookAt(target)
|
this.logo.lookAt(target)
|
||||||
this.refollowMouse()
|
this.refollowMouse()
|
||||||
|
@ -23,7 +23,7 @@ Network.prototype.render = function () {
|
|||||||
|
|
||||||
if (networkNumber === 'loading') {
|
if (networkNumber === 'loading') {
|
||||||
|
|
||||||
return h('img', {
|
return h('img.network-indicator', {
|
||||||
title: 'Attempting to connect to blockchain.',
|
title: 'Attempting to connect to blockchain.',
|
||||||
onClick: (event) => this.props.onClick(event),
|
onClick: (event) => this.props.onClick(event),
|
||||||
style: {
|
style: {
|
||||||
@ -36,9 +36,6 @@ Network.prototype.render = function () {
|
|||||||
} else if (providerName === 'mainnet') {
|
} else if (providerName === 'mainnet') {
|
||||||
hoverText = 'Main Ethereum Network'
|
hoverText = 'Main Ethereum Network'
|
||||||
iconName = 'ethereum-network'
|
iconName = 'ethereum-network'
|
||||||
} else if (providerName === 'classic') {
|
|
||||||
hoverText = 'Ethereum Classic Network'
|
|
||||||
iconName = 'classic-network'
|
|
||||||
} else if (parseInt(networkNumber) === 2) {
|
} else if (parseInt(networkNumber) === 2) {
|
||||||
hoverText = 'Morden Test Network'
|
hoverText = 'Morden Test Network'
|
||||||
iconName = 'morden-test-network'
|
iconName = 'morden-test-network'
|
||||||
@ -64,16 +61,7 @@ Network.prototype.render = function () {
|
|||||||
style: {
|
style: {
|
||||||
color: '#039396',
|
color: '#039396',
|
||||||
}},
|
}},
|
||||||
'Etherum Main Net'),
|
'Ethereum Main Net'),
|
||||||
])
|
|
||||||
case 'classic-network':
|
|
||||||
return h('.network-indicator', [
|
|
||||||
h('.menu-icon.hollow-diamond'),
|
|
||||||
h('.network-name', {
|
|
||||||
style: {
|
|
||||||
color: '#039396',
|
|
||||||
}},
|
|
||||||
'Etherum Classic'),
|
|
||||||
])
|
])
|
||||||
case 'morden-test-network':
|
case 'morden-test-network':
|
||||||
return h('.network-indicator', [
|
return h('.network-indicator', [
|
||||||
|
@ -3,7 +3,6 @@ const h = require('react-hyperscript')
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
|
|
||||||
const AccountPanel = require('./account-panel')
|
const AccountPanel = require('./account-panel')
|
||||||
const readableDate = require('../util').readableDate
|
|
||||||
|
|
||||||
module.exports = PendingMsgDetails
|
module.exports = PendingMsgDetails
|
||||||
|
|
||||||
@ -24,6 +23,9 @@ PendingMsgDetails.prototype.render = function () {
|
|||||||
return (
|
return (
|
||||||
h('div', {
|
h('div', {
|
||||||
key: msgData.id,
|
key: msgData.id,
|
||||||
|
style: {
|
||||||
|
margin: '10px 20px',
|
||||||
|
},
|
||||||
}, [
|
}, [
|
||||||
|
|
||||||
// account that will sign
|
// account that will sign
|
||||||
@ -36,11 +38,6 @@ PendingMsgDetails.prototype.render = function () {
|
|||||||
|
|
||||||
// message data
|
// message data
|
||||||
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
|
h('.tx-data.flex-column.flex-justify-center.flex-grow.select-none', [
|
||||||
h('.flex-row.flex-space-between', [
|
|
||||||
h('label.font-small', 'DATE'),
|
|
||||||
h('span.font-small', readableDate(msgData.time)),
|
|
||||||
]),
|
|
||||||
|
|
||||||
h('.flex-row.flex-space-between', [
|
h('.flex-row.flex-space-between', [
|
||||||
h('label.font-small', 'MESSAGE'),
|
h('label.font-small', 'MESSAGE'),
|
||||||
h('span.font-small', msgParams.data),
|
h('span.font-small', msgParams.data),
|
||||||
|
71
ui/app/components/qr-code.js
Normal file
71
ui/app/components/qr-code.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const CopyButton = require('./copyButton')
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(QrCodeView)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
Qr: state.appState.Qr,
|
||||||
|
buyView: state.appState.buyView,
|
||||||
|
warning: state.appState.warning,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(QrCodeView, Component)
|
||||||
|
|
||||||
|
function QrCodeView () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
QrCodeView.prototype.render = function () {
|
||||||
|
var props = this.props
|
||||||
|
var Qr = props.Qr
|
||||||
|
return h('.main-container.flex-column', {
|
||||||
|
key: 'qr',
|
||||||
|
style: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '45px',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
Array.isArray(Qr.message) ? h('.message-container', this.renderMultiMessage()) : h('h3', Qr.message),
|
||||||
|
|
||||||
|
this.props.warning ? this.props.warning && h('span.error.flex-center', {
|
||||||
|
style: {
|
||||||
|
textAlign: 'center',
|
||||||
|
width: '229px',
|
||||||
|
height: '82px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
this.props.warning) : null,
|
||||||
|
|
||||||
|
h('#qr-container.flex-column', {
|
||||||
|
style: {
|
||||||
|
marginTop: '25px',
|
||||||
|
marginBottom: '15px',
|
||||||
|
},
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: Qr.image,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('.flex-row', [
|
||||||
|
h('h3.ellip-address', {
|
||||||
|
style: {
|
||||||
|
width: '247px',
|
||||||
|
},
|
||||||
|
}, Qr.data),
|
||||||
|
h(CopyButton, {
|
||||||
|
value: Qr.data,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
QrCodeView.prototype.renderMultiMessage = function () {
|
||||||
|
var Qr = this.props.Qr
|
||||||
|
var multiMessage = Qr.message.map((message) => h('.qr-message', message))
|
||||||
|
return multiMessage
|
||||||
|
}
|
322
ui/app/components/shapeshift-form.js
Normal file
322
ui/app/components/shapeshift-form.js
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
const PersistentForm = require('../../lib/persistent-form')
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const inherits = require('util').inherits
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
|
||||||
|
const actions = require('../actions')
|
||||||
|
const Qr = require('./qr-code')
|
||||||
|
const isValidAddress = require('../util').isValidAddress
|
||||||
|
module.exports = connect(mapStateToProps)(ShapeshiftForm)
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
return {
|
||||||
|
selectedAccount: state.selectedAccount,
|
||||||
|
warning: state.appState.warning,
|
||||||
|
isSubLoading: state.appState.isSubLoading,
|
||||||
|
qrRequested: state.appState.qrRequested,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(ShapeshiftForm, PersistentForm)
|
||||||
|
|
||||||
|
function ShapeshiftForm () {
|
||||||
|
PersistentForm.call(this)
|
||||||
|
this.persistentFormParentId = 'shapeshift-buy-form'
|
||||||
|
}
|
||||||
|
|
||||||
|
ShapeshiftForm.prototype.render = function () {
|
||||||
|
|
||||||
|
return h(ReactCSSTransitionGroup, {
|
||||||
|
className: 'css-transition-group',
|
||||||
|
transitionName: 'main',
|
||||||
|
transitionEnterTimeout: 300,
|
||||||
|
transitionLeaveTimeout: 300,
|
||||||
|
}, [
|
||||||
|
this.props.qrRequested ? h(Qr, {key: 'qr'}) : 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: {
|
||||||
|
// marginTop: '10px',
|
||||||
|
padding: '25px',
|
||||||
|
width: '100%',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('.flex-row', {
|
||||||
|
style: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'baseline',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('img', {
|
||||||
|
src: coinOptions[coin].image,
|
||||||
|
width: '25px',
|
||||||
|
height: '25px',
|
||||||
|
style: {
|
||||||
|
marginRight: '5px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
h('.input-container', [
|
||||||
|
h('input#fromCoin.buy-inputs.ex-coins', {
|
||||||
|
type: 'text',
|
||||||
|
list: 'coinList',
|
||||||
|
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: 'relative',
|
||||||
|
bottom: '48px',
|
||||||
|
left: '106px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('.icon-control', [
|
||||||
|
h('i.fa.fa-refresh.fa-4.orange', {
|
||||||
|
style: {
|
||||||
|
position: 'relative',
|
||||||
|
bottom: '5px',
|
||||||
|
left: '5px',
|
||||||
|
color: '#F7861C',
|
||||||
|
},
|
||||||
|
onClick: this.updateCoin.bind(this),
|
||||||
|
}),
|
||||||
|
h('i.fa.fa-chevron-right.fa-4.orange', {
|
||||||
|
style: {
|
||||||
|
position: 'relative',
|
||||||
|
bottom: '26px',
|
||||||
|
left: '10px',
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
|
this.props.isSubLoading ? this.renderLoading() : null,
|
||||||
|
h('.flex-column', {
|
||||||
|
style: {
|
||||||
|
width: '235px',
|
||||||
|
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(),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('.flex-row', {
|
||||||
|
style: {
|
||||||
|
padding: '10px',
|
||||||
|
paddingBottom: '2px',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('div', 'Receiving address:'),
|
||||||
|
h('.ellip-address', this.props.buyView.buyAddress),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h(this.activeToggle('.input-container'), {
|
||||||
|
style: {
|
||||||
|
padding: '10px',
|
||||||
|
paddingTop: '0px',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
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: '278px',
|
||||||
|
height: '20px',
|
||||||
|
padding: ' 5px ',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
h('i.fa.fa-pencil-square-o.edit-text', {
|
||||||
|
style: {
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#F7861C',
|
||||||
|
position: 'relative',
|
||||||
|
bottom: '5px',
|
||||||
|
right: '11px',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
h('.flex-row', {
|
||||||
|
style: {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('button', {
|
||||||
|
onClick: this.shift.bind(this),
|
||||||
|
style: {
|
||||||
|
marginTop: '10px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'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.showWarning(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: {
|
||||||
|
marginTop: '15px',
|
||||||
|
marginBottom: '15px',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('h3.flex-row.text-transform-uppercase', {
|
||||||
|
style: {
|
||||||
|
color: '#AEAEAE',
|
||||||
|
paddingTop: '4px',
|
||||||
|
justifyContent: 'space-around',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: '14px',
|
||||||
|
},
|
||||||
|
}, `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.handleAddress = function (event) {
|
||||||
|
this.props.dispatch(actions.updateBuyAddress(event.target.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
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',
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
202
ui/app/components/shift-list-item.js
Normal file
202
ui/app/components/shift-list-item.js
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const vreme = new (require('vreme'))
|
||||||
|
const explorerLink = require('../../lib/explorer-link')
|
||||||
|
const extension = require('../../../app/scripts/lib/extension')
|
||||||
|
const actions = require('../actions')
|
||||||
|
const addressSummary = require('../util').addressSummary
|
||||||
|
|
||||||
|
const CopyButton = require('./copyButton')
|
||||||
|
const EtherBalance = require('./eth-balance')
|
||||||
|
const Tooltip = require('./tooltip')
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(ShiftListItem)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 vreme.format(new Date(date), 'March 16 2014 14:30')
|
||||||
|
}
|
||||||
|
|
||||||
|
ShiftListItem.prototype.renderUtilComponents = function () {
|
||||||
|
var props = this.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(EtherBalance, {
|
||||||
|
value: `${props.response.outgoingCoin}`,
|
||||||
|
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: () => extension.tabs.create({
|
||||||
|
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 ''
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ const vreme = new (require('vreme'))
|
|||||||
const extension = require('../../../app/scripts/lib/extension')
|
const extension = require('../../../app/scripts/lib/extension')
|
||||||
|
|
||||||
const TransactionIcon = require('./transaction-list-item-icon')
|
const TransactionIcon = require('./transaction-list-item-icon')
|
||||||
|
const ShiftListItem = require('./shift-list-item')
|
||||||
module.exports = TransactionListItem
|
module.exports = TransactionListItem
|
||||||
|
|
||||||
inherits(TransactionListItem, Component)
|
inherits(TransactionListItem, Component)
|
||||||
@ -19,8 +19,10 @@ function TransactionListItem () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionListItem.prototype.render = function () {
|
TransactionListItem.prototype.render = function () {
|
||||||
const { transaction, i, network } = this.props
|
const { transaction, network } = this.props
|
||||||
|
if (transaction.key === 'shapeshift') {
|
||||||
|
if (network === '1') return h(ShiftListItem, transaction)
|
||||||
|
}
|
||||||
var date = formatDate(transaction.time)
|
var date = formatDate(transaction.time)
|
||||||
|
|
||||||
let isLinkable = false
|
let isLinkable = false
|
||||||
@ -42,7 +44,6 @@ TransactionListItem.prototype.render = function () {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||||
key: `tx-${transaction.id + i}`,
|
|
||||||
onClick: (event) => {
|
onClick: (event) => {
|
||||||
if (isPending) {
|
if (isPending) {
|
||||||
this.props.showTx(transaction.id)
|
this.props.showTx(transaction.id)
|
||||||
|
@ -14,7 +14,11 @@ function TransactionList () {
|
|||||||
|
|
||||||
TransactionList.prototype.render = function () {
|
TransactionList.prototype.render = function () {
|
||||||
const { txsToRender, network, unconfMsgs } = this.props
|
const { txsToRender, network, unconfMsgs } = this.props
|
||||||
const transactions = txsToRender.concat(unconfMsgs)
|
var shapeShiftTxList
|
||||||
|
if (network === '1') {
|
||||||
|
shapeShiftTxList = this.props.shapeShiftTxList
|
||||||
|
}
|
||||||
|
const transactions = !shapeShiftTxList ? txsToRender.concat(unconfMsgs) : txsToRender.concat(unconfMsgs, shapeShiftTxList)
|
||||||
.sort((a, b) => b.time - a.time)
|
.sort((a, b) => b.time - a.time)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -39,33 +43,46 @@ TransactionList.prototype.render = function () {
|
|||||||
paddingBottom: '4px',
|
paddingBottom: '4px',
|
||||||
},
|
},
|
||||||
}, [
|
}, [
|
||||||
'Transactions',
|
'History',
|
||||||
]),
|
]),
|
||||||
|
|
||||||
h('.tx-list', {
|
h('.tx-list', {
|
||||||
style: {
|
style: {
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
height: '305px',
|
height: '300px',
|
||||||
padding: '0 20px',
|
padding: '0 20px',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
}, (
|
}, [
|
||||||
|
|
||||||
transactions.length
|
transactions.length
|
||||||
? transactions.map((transaction, i) => {
|
? transactions.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, {
|
return h(TransactionListItem, {
|
||||||
transaction, i, network,
|
transaction, i, network, key,
|
||||||
showTx: (txId) => {
|
showTx: (txId) => {
|
||||||
this.props.viewPendingTx(txId)
|
this.props.viewPendingTx(txId)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
: [h('.flex-center', {
|
: h('.flex-center', {
|
||||||
style: {
|
style: {
|
||||||
|
flexDirection: 'column',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
},
|
},
|
||||||
}, 'No transaction history...')]
|
}, [
|
||||||
)),
|
'No transaction history.',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ const h = require('react-hyperscript')
|
|||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
const txHelper = require('../lib/tx-helper')
|
const txHelper = require('../lib/tx-helper')
|
||||||
|
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
|
||||||
|
|
||||||
const PendingTx = require('./components/pending-tx')
|
const PendingTx = require('./components/pending-tx')
|
||||||
const PendingMsg = require('./components/pending-msg')
|
const PendingMsg = require('./components/pending-msg')
|
||||||
@ -35,7 +36,8 @@ ConfirmTxScreen.prototype.render = function () {
|
|||||||
var unconfMsgs = state.unconfMsgs
|
var unconfMsgs = state.unconfMsgs
|
||||||
var unconfTxList = txHelper(unconfTxs, unconfMsgs)
|
var unconfTxList = txHelper(unconfTxs, unconfMsgs)
|
||||||
var index = state.index !== undefined ? state.index : 0
|
var index = state.index !== undefined ? state.index : 0
|
||||||
var txData = unconfTxList[index] || {}
|
var txData = unconfTxList[index] || unconfTxList[0] || {}
|
||||||
|
var isNotification = isPopupOrNotification() === 'notification'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
@ -43,9 +45,9 @@ ConfirmTxScreen.prototype.render = function () {
|
|||||||
|
|
||||||
// subtitle and nav
|
// subtitle and nav
|
||||||
h('.section-title.flex-row.flex-center', [
|
h('.section-title.flex-row.flex-center', [
|
||||||
h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
!isNotification ? h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
|
||||||
onClick: this.goHome.bind(this),
|
onClick: this.goHome.bind(this),
|
||||||
}),
|
}) : null,
|
||||||
h('h2.page-subtitle', 'Confirm Transaction'),
|
h('h2.page-subtitle', 'Confirm Transaction'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ const Component = require('react').Component
|
|||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
|
const currencies = require('./conversion-util').availableCurrencies.rows
|
||||||
module.exports = connect(mapStateToProps)(ConfigScreen)
|
module.exports = connect(mapStateToProps)(ConfigScreen)
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
@ -74,6 +74,8 @@ ConfigScreen.prototype.render = function () {
|
|||||||
}, 'Save'),
|
}, 'Save'),
|
||||||
]),
|
]),
|
||||||
h('hr.horizontal-line'),
|
h('hr.horizontal-line'),
|
||||||
|
currentConversionInformation(metamaskState, state),
|
||||||
|
h('hr.horizontal-line'),
|
||||||
|
|
||||||
h('div', {
|
h('div', {
|
||||||
style: {
|
style: {
|
||||||
@ -97,6 +99,27 @@ ConfigScreen.prototype.render = function () {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function currentConversionInformation (metamaskState, state) {
|
||||||
|
var currentFiat = metamaskState.currentFiat
|
||||||
|
var conversionDate = metamaskState.conversionDate
|
||||||
|
return h('div', [
|
||||||
|
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'),
|
||||||
|
h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`),
|
||||||
|
h('select#currentFiat', {
|
||||||
|
onChange (event) {
|
||||||
|
event.preventDefault()
|
||||||
|
var element = document.getElementById('currentFiat')
|
||||||
|
var newFiat = element.value
|
||||||
|
state.dispatch(actions.setCurrentFiat(newFiat))
|
||||||
|
},
|
||||||
|
defaultValue: currentFiat,
|
||||||
|
}, currencies.map((currency) => {
|
||||||
|
return h('option', {key: currency.code, value: currency.code}, `${currency.code} - ${currency.name}`)
|
||||||
|
})
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
function currentProviderDisplay (metamaskState) {
|
function currentProviderDisplay (metamaskState) {
|
||||||
var provider = metamaskState.provider
|
var provider = metamaskState.provider
|
||||||
var title, value
|
var title, value
|
||||||
|
5
ui/app/conversion-util.js
Normal file
5
ui/app/conversion-util.js
Normal file
File diff suppressed because one or more lines are too long
@ -471,3 +471,128 @@ input.large-input {
|
|||||||
.eth-warning{
|
.eth-warning{
|
||||||
transition: opacity 400ms ease-in, transform 400ms ease-in;
|
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: 12px;
|
||||||
|
line-height: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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-message {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #F7861C;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.message-container > div:first-child {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #4D4D4D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pop-hover:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
@ -35,9 +35,10 @@ EthStoreWarning.prototype.render = function () {
|
|||||||
margin: '10px 10px 10px 10px',
|
margin: '10px 10px 10px 10px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
`MetaMask is currently in beta -
|
`The MetaMask team would like to
|
||||||
exercise caution while handling
|
remind you that MetaMask is currently in beta - so
|
||||||
and storing your ether.
|
don't store large
|
||||||
|
amounts of ether in MetaMask.
|
||||||
`),
|
`),
|
||||||
|
|
||||||
h('i.fa.fa-exclamation-triangle.fa-4', {
|
h('i.fa.fa-exclamation-triangle.fa-4', {
|
||||||
|
@ -73,9 +73,7 @@ InitializeMenuScreen.prototype.renderMenu = function () {
|
|||||||
margin: 12,
|
margin: 12,
|
||||||
},
|
},
|
||||||
}, 'Restore Existing Vault'),
|
}, 'Restore Existing Vault'),
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const Component = require('react').Component
|
const PersistentForm = require('../../lib/persistent-form')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const actions = require('../actions')
|
const actions = require('../actions')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
|
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
|
||||||
|
|
||||||
inherits(RestoreVaultScreen, Component)
|
inherits(RestoreVaultScreen, PersistentForm)
|
||||||
function RestoreVaultScreen () {
|
function RestoreVaultScreen () {
|
||||||
Component.call(this)
|
PersistentForm.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
@ -19,6 +19,8 @@ function mapStateToProps (state) {
|
|||||||
|
|
||||||
RestoreVaultScreen.prototype.render = function () {
|
RestoreVaultScreen.prototype.render = function () {
|
||||||
var state = this.props
|
var state = this.props
|
||||||
|
this.persistentFormParentId = 'restore-vault-form'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
h('.initialize-screen.flex-column.flex-center.flex-grow', [
|
h('.initialize-screen.flex-column.flex-center.flex-grow', [
|
||||||
@ -39,6 +41,9 @@ RestoreVaultScreen.prototype.render = function () {
|
|||||||
// wallet seed entry
|
// wallet seed entry
|
||||||
h('h3', 'Wallet Seed'),
|
h('h3', 'Wallet Seed'),
|
||||||
h('textarea.twelve-word-phrase.letter-spacey', {
|
h('textarea.twelve-word-phrase.letter-spacey', {
|
||||||
|
dataset: {
|
||||||
|
persistentFormId: 'wallet-seed',
|
||||||
|
},
|
||||||
placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
|
placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -47,6 +52,9 @@ RestoreVaultScreen.prototype.render = function () {
|
|||||||
type: 'password',
|
type: 'password',
|
||||||
id: 'password-box',
|
id: 'password-box',
|
||||||
placeholder: 'New Password (min 8 chars)',
|
placeholder: 'New Password (min 8 chars)',
|
||||||
|
dataset: {
|
||||||
|
persistentFormId: 'password',
|
||||||
|
},
|
||||||
style: {
|
style: {
|
||||||
width: 260,
|
width: 260,
|
||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
@ -59,6 +67,9 @@ RestoreVaultScreen.prototype.render = function () {
|
|||||||
id: 'password-box-confirm',
|
id: 'password-box-confirm',
|
||||||
placeholder: 'Confirm Password',
|
placeholder: 'Confirm Password',
|
||||||
onKeyPress: this.onMaybeCreate.bind(this),
|
onKeyPress: this.onMaybeCreate.bind(this),
|
||||||
|
dataset: {
|
||||||
|
persistentFormId: 'password-confirmation',
|
||||||
|
},
|
||||||
style: {
|
style: {
|
||||||
width: 260,
|
width: 260,
|
||||||
marginTop: 16,
|
marginTop: 16,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const actions = require('../actions')
|
const actions = require('../actions')
|
||||||
const txHelper = require('../../lib/tx-helper')
|
const txHelper = require('../../lib/tx-helper')
|
||||||
|
const notification = require('../../../app/scripts/lib/notifications')
|
||||||
|
|
||||||
module.exports = reduceApp
|
module.exports = reduceApp
|
||||||
|
|
||||||
@ -123,6 +124,7 @@ function reduceApp (state, action) {
|
|||||||
|
|
||||||
case actions.UNLOCK_METAMASK:
|
case actions.UNLOCK_METAMASK:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
|
forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null,
|
||||||
detailView: {},
|
detailView: {},
|
||||||
transForward: true,
|
transForward: true,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
@ -136,6 +138,25 @@ function reduceApp (state, action) {
|
|||||||
warning: null,
|
warning: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.BACK_TO_INIT_MENU:
|
||||||
|
return extend(appState, {
|
||||||
|
warning: null,
|
||||||
|
transForward: false,
|
||||||
|
forgottenPassword: true,
|
||||||
|
currentView: {
|
||||||
|
name: 'InitMenu',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.BACK_TO_UNLOCK_VIEW:
|
||||||
|
return extend(appState, {
|
||||||
|
warning: null,
|
||||||
|
transForward: true,
|
||||||
|
forgottenPassword: !appState.forgottenPassword,
|
||||||
|
currentView: {
|
||||||
|
name: 'UnlockScreen',
|
||||||
|
},
|
||||||
|
})
|
||||||
// reveal seed words
|
// reveal seed words
|
||||||
|
|
||||||
case actions.REVEAL_SEED_CONFIRMATION:
|
case actions.REVEAL_SEED_CONFIRMATION:
|
||||||
@ -170,6 +191,7 @@ function reduceApp (state, action) {
|
|||||||
|
|
||||||
case actions.SHOW_ACCOUNT_DETAIL:
|
case actions.SHOW_ACCOUNT_DETAIL:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
|
forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null,
|
||||||
currentView: {
|
currentView: {
|
||||||
name: 'accountDetail',
|
name: 'accountDetail',
|
||||||
context: action.value,
|
context: action.value,
|
||||||
@ -250,6 +272,9 @@ function reduceApp (state, action) {
|
|||||||
warning: null,
|
warning: null,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
notification.closePopup()
|
||||||
|
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
transForward: false,
|
transForward: false,
|
||||||
warning: null,
|
warning: null,
|
||||||
@ -317,6 +342,15 @@ function reduceApp (state, action) {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.SHOW_SUB_LOADING_INDICATION:
|
||||||
|
return extend(appState, {
|
||||||
|
isSubLoading: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.HIDE_SUB_LOADING_INDICATION:
|
||||||
|
return extend(appState, {
|
||||||
|
isSubLoading: false,
|
||||||
|
})
|
||||||
case actions.CLEAR_SEED_WORD_CACHE:
|
case actions.CLEAR_SEED_WORD_CACHE:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
transForward: true,
|
transForward: true,
|
||||||
@ -369,15 +403,116 @@ function reduceApp (state, action) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
case actions.SHOW_ETH_WARNING:
|
case actions.BUY_ETH_VIEW:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
transForward: true,
|
transForward: true,
|
||||||
currentView: {
|
currentView: {
|
||||||
name: 'accountDetail',
|
name: 'buyEth',
|
||||||
context: appState.currentView.context,
|
context: appState.currentView.context,
|
||||||
},
|
},
|
||||||
accountDetail: {
|
buyView: {
|
||||||
subview: 'buy-eth-warning',
|
subview: 'buyForm',
|
||||||
|
amount: '5.00',
|
||||||
|
buyAddress: action.value,
|
||||||
|
formView: {
|
||||||
|
coinbase: true,
|
||||||
|
shapeshift: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.UPDATE_BUY_ADDRESS:
|
||||||
|
return extend(appState, {
|
||||||
|
buyView: {
|
||||||
|
subview: 'buyForm',
|
||||||
|
formView: {
|
||||||
|
coinbase: appState.buyView.formView.coinbase,
|
||||||
|
shapeshift: appState.buyView.formView.shapeshift,
|
||||||
|
},
|
||||||
|
buyAddress: action.value,
|
||||||
|
amount: appState.buyView.amount,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.UPDATE_COINBASE_AMOUNT:
|
||||||
|
return extend(appState, {
|
||||||
|
buyView: {
|
||||||
|
subview: 'buyForm',
|
||||||
|
formView: {
|
||||||
|
coinbase: true,
|
||||||
|
shapeshift: false,
|
||||||
|
},
|
||||||
|
buyAddress: appState.buyView.buyAddress,
|
||||||
|
amount: action.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.COINBASE_SUBVIEW:
|
||||||
|
return extend(appState, {
|
||||||
|
buyView: {
|
||||||
|
subview: 'buyForm',
|
||||||
|
formView: {
|
||||||
|
coinbase: true,
|
||||||
|
shapeshift: false,
|
||||||
|
},
|
||||||
|
buyAddress: appState.buyView.buyAddress,
|
||||||
|
amount: appState.buyView.amount,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.SHAPESHIFT_SUBVIEW:
|
||||||
|
return extend(appState, {
|
||||||
|
buyView: {
|
||||||
|
subview: 'buyForm',
|
||||||
|
formView: {
|
||||||
|
coinbase: false,
|
||||||
|
shapeshift: true,
|
||||||
|
marketinfo: action.value.marketinfo,
|
||||||
|
coinOptions: action.value.coinOptions,
|
||||||
|
},
|
||||||
|
buyAddress: appState.buyView.buyAddress,
|
||||||
|
amount: appState.buyView.amount,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.PAIR_UPDATE:
|
||||||
|
return extend(appState, {
|
||||||
|
buyView: {
|
||||||
|
subview: 'buyForm',
|
||||||
|
formView: {
|
||||||
|
coinbase: false,
|
||||||
|
shapeshift: true,
|
||||||
|
marketinfo: action.value.marketinfo,
|
||||||
|
coinOptions: appState.buyView.formView.coinOptions,
|
||||||
|
},
|
||||||
|
buyAddress: appState.buyView.buyAddress,
|
||||||
|
amount: appState.buyView.amount,
|
||||||
|
warning: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.SHOW_QR:
|
||||||
|
return extend(appState, {
|
||||||
|
qrRequested: true,
|
||||||
|
transForward: true,
|
||||||
|
Qr: {
|
||||||
|
message: action.value.message,
|
||||||
|
image: action.value.qr,
|
||||||
|
data: action.value.data,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.SHOW_QR_VIEW:
|
||||||
|
return extend(appState, {
|
||||||
|
currentView: {
|
||||||
|
name: 'qr',
|
||||||
|
context: appState.currentView.context,
|
||||||
|
},
|
||||||
|
transForward: true,
|
||||||
|
Qr: {
|
||||||
|
message: action.value.message,
|
||||||
|
image: action.value.qr,
|
||||||
|
data: action.value.data,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
@ -405,4 +540,3 @@ function indexForPending (state, txId) {
|
|||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,10 +10,14 @@ function reduceMetamask (state, action) {
|
|||||||
var metamaskState = extend({
|
var metamaskState = extend({
|
||||||
isInitialized: false,
|
isInitialized: false,
|
||||||
isUnlocked: false,
|
isUnlocked: false,
|
||||||
|
isEthConfirmed: false,
|
||||||
currentDomain: 'example.com',
|
currentDomain: 'example.com',
|
||||||
rpcTarget: 'https://rawtestrpc.metamask.io/',
|
rpcTarget: 'https://rawtestrpc.metamask.io/',
|
||||||
identities: {},
|
identities: {},
|
||||||
unconfTxs: {},
|
unconfTxs: {},
|
||||||
|
currentFiat: 'USD',
|
||||||
|
conversionRate: 0,
|
||||||
|
conversionDate: 'N/A',
|
||||||
}, state.metamask)
|
}, state.metamask)
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -33,7 +37,7 @@ function reduceMetamask (state, action) {
|
|||||||
|
|
||||||
case actions.AGREE_TO_ETH_WARNING:
|
case actions.AGREE_TO_ETH_WARNING:
|
||||||
return extend(metamaskState, {
|
return extend(metamaskState, {
|
||||||
isEthConfirmed: true,
|
isEthConfirmed: !metamaskState.isEthConfirmed,
|
||||||
})
|
})
|
||||||
|
|
||||||
case actions.UNLOCK_METAMASK:
|
case actions.UNLOCK_METAMASK:
|
||||||
@ -114,6 +118,13 @@ function reduceMetamask (state, action) {
|
|||||||
var identities = extend(metamaskState.identities, id)
|
var identities = extend(metamaskState.identities, id)
|
||||||
return extend(metamaskState, { identities })
|
return extend(metamaskState, { identities })
|
||||||
|
|
||||||
|
case actions.SET_CURRENT_FIAT:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
currentFiat: action.value.currentFiat,
|
||||||
|
conversionRate: action.value.conversionRate,
|
||||||
|
conversionDate: action.value.conversionDate,
|
||||||
|
})
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return metamaskState
|
return metamaskState
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const inherits = require('util').inherits
|
const inherits = require('util').inherits
|
||||||
const Component = require('react').Component
|
const PersistentForm = require('../lib/persistent-form')
|
||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const Identicon = require('./components/identicon')
|
const Identicon = require('./components/identicon')
|
||||||
@ -29,12 +29,14 @@ function mapStateToProps (state) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(SendTransactionScreen, Component)
|
inherits(SendTransactionScreen, PersistentForm)
|
||||||
function SendTransactionScreen () {
|
function SendTransactionScreen () {
|
||||||
Component.call(this)
|
PersistentForm.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTransactionScreen.prototype.render = function () {
|
SendTransactionScreen.prototype.render = function () {
|
||||||
|
this.persistentFormParentId = 'send-tx-form'
|
||||||
|
|
||||||
var state = this.props
|
var state = this.props
|
||||||
var address = state.address
|
var address = state.address
|
||||||
var account = state.account
|
var account = state.account
|
||||||
@ -137,6 +139,9 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
h('input.large-input', {
|
h('input.large-input', {
|
||||||
name: 'address',
|
name: 'address',
|
||||||
placeholder: 'Recipient Address',
|
placeholder: 'Recipient Address',
|
||||||
|
dataset: {
|
||||||
|
persistentFormId: 'recipient-address',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -150,6 +155,9 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
style: {
|
style: {
|
||||||
marginRight: 6,
|
marginRight: 6,
|
||||||
},
|
},
|
||||||
|
dataset: {
|
||||||
|
persistentFormId: 'tx-amount',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
h('button.primary', {
|
h('button.primary', {
|
||||||
@ -185,11 +193,12 @@ SendTransactionScreen.prototype.render = function () {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
resize: 'none',
|
resize: 'none',
|
||||||
},
|
},
|
||||||
|
dataset: {
|
||||||
|
persistentFormId: 'tx-data',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
const createStore = require('redux').createStore
|
const createStore = require('redux').createStore
|
||||||
const applyMiddleware = require('redux').applyMiddleware
|
const applyMiddleware = require('redux').applyMiddleware
|
||||||
const thunkMiddleware = require('redux-thunk')
|
const thunkMiddleware = require('redux-thunk')
|
||||||
const createLogger = require('redux-logger')
|
|
||||||
const rootReducer = require('./reducers')
|
const rootReducer = require('./reducers')
|
||||||
|
const createLogger = require('redux-logger')
|
||||||
|
|
||||||
|
global.METAMASK_DEBUG = false
|
||||||
|
|
||||||
module.exports = configureStore
|
module.exports = configureStore
|
||||||
|
|
||||||
const loggerMiddleware = createLogger()
|
const loggerMiddleware = createLogger({
|
||||||
|
predicate: () => global.METAMASK_DEBUG,
|
||||||
|
})
|
||||||
|
|
||||||
const createStoreWithMiddleware = applyMiddleware(
|
const middlewares = [thunkMiddleware, loggerMiddleware]
|
||||||
thunkMiddleware,
|
|
||||||
loggerMiddleware
|
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore)
|
||||||
)(createStore)
|
|
||||||
|
|
||||||
function configureStore (initialState) {
|
function configureStore (initialState) {
|
||||||
return createStoreWithMiddleware(rootReducer, initialState)
|
return createStoreWithMiddleware(rootReducer, initialState)
|
||||||
|
@ -3,10 +3,11 @@ const Component = require('react').Component
|
|||||||
const h = require('react-hyperscript')
|
const h = require('react-hyperscript')
|
||||||
const connect = require('react-redux').connect
|
const connect = require('react-redux').connect
|
||||||
const actions = require('./actions')
|
const actions = require('./actions')
|
||||||
const Mascot = require('./components/mascot')
|
|
||||||
const getCaretCoordinates = require('textarea-caret')
|
const getCaretCoordinates = require('textarea-caret')
|
||||||
const EventEmitter = require('events').EventEmitter
|
const EventEmitter = require('events').EventEmitter
|
||||||
|
|
||||||
|
const Mascot = require('./components/mascot')
|
||||||
|
|
||||||
module.exports = connect(mapStateToProps)(UnlockScreen)
|
module.exports = connect(mapStateToProps)(UnlockScreen)
|
||||||
|
|
||||||
inherits(UnlockScreen, Component)
|
inherits(UnlockScreen, Component)
|
||||||
@ -25,47 +26,46 @@ UnlockScreen.prototype.render = function () {
|
|||||||
const state = this.props
|
const state = this.props
|
||||||
const warning = state.warning
|
const warning = state.warning
|
||||||
return (
|
return (
|
||||||
|
h('.flex-column.hey-im-here', [
|
||||||
|
h('.unlock-screen.flex-column.flex-center.flex-grow', [
|
||||||
|
|
||||||
h('.unlock-screen.flex-column.flex-center.flex-grow', [
|
h(Mascot, {
|
||||||
|
animationEventEmitter: this.animationEventEmitter,
|
||||||
|
}),
|
||||||
|
|
||||||
h(Mascot, {
|
h('h1', {
|
||||||
animationEventEmitter: this.animationEventEmitter,
|
style: {
|
||||||
}),
|
fontSize: '1.4em',
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
color: '#7F8082',
|
||||||
|
},
|
||||||
|
}, 'MetaMask'),
|
||||||
|
|
||||||
h('h1', {
|
h('input.large-input', {
|
||||||
style: {
|
type: 'password',
|
||||||
fontSize: '1.4em',
|
id: 'password-box',
|
||||||
textTransform: 'uppercase',
|
placeholder: 'enter password',
|
||||||
color: '#7F8082',
|
style: {
|
||||||
},
|
|
||||||
}, 'MetaMask'),
|
|
||||||
|
|
||||||
h('input.large-input', {
|
},
|
||||||
type: 'password',
|
onKeyPress: this.onKeyPress.bind(this),
|
||||||
id: 'password-box',
|
onInput: this.inputChanged.bind(this),
|
||||||
placeholder: 'enter password',
|
}),
|
||||||
style: {
|
|
||||||
|
|
||||||
},
|
h('.error', {
|
||||||
onKeyPress: this.onKeyPress.bind(this),
|
style: {
|
||||||
onInput: this.inputChanged.bind(this),
|
display: warning ? 'block' : 'none',
|
||||||
}),
|
},
|
||||||
|
}, warning),
|
||||||
h('.error', {
|
|
||||||
style: {
|
|
||||||
display: warning ? 'block' : 'none',
|
|
||||||
},
|
|
||||||
}, warning),
|
|
||||||
|
|
||||||
h('button.primary.cursor-pointer', {
|
|
||||||
onClick: this.onSubmit.bind(this),
|
|
||||||
style: {
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
}, 'Unlock'),
|
|
||||||
|
|
||||||
|
h('button.primary.cursor-pointer', {
|
||||||
|
onClick: this.onSubmit.bind(this),
|
||||||
|
style: {
|
||||||
|
margin: 10,
|
||||||
|
},
|
||||||
|
}, 'Unlock'),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +92,8 @@ function parseBalance (balance) {
|
|||||||
|
|
||||||
// Takes wei hex, returns an object with three properties.
|
// Takes wei hex, returns an object with three properties.
|
||||||
// Its "formatted" property is what we generally use to render values.
|
// Its "formatted" property is what we generally use to render values.
|
||||||
function formatBalance (balance, decimalsToKeep) {
|
function formatBalance (balance, decimalsToKeep, needsParse = true) {
|
||||||
var parsed = parseBalance(balance)
|
var parsed = needsParse ? parseBalance(balance) : balance.split('.')
|
||||||
var beforeDecimal = parsed[0]
|
var beforeDecimal = parsed[0]
|
||||||
var afterDecimal = parsed[1]
|
var afterDecimal = parsed[1]
|
||||||
var formatted = 'None'
|
var formatted = 'None'
|
||||||
@ -141,14 +141,21 @@ function shortenBalance (balance, decimalsToKeep = 1) {
|
|||||||
var convertedBalance = parseFloat(balance)
|
var convertedBalance = parseFloat(balance)
|
||||||
if (convertedBalance > 1000000) {
|
if (convertedBalance > 1000000) {
|
||||||
truncatedValue = (balance / 1000000).toFixed(decimalsToKeep)
|
truncatedValue = (balance / 1000000).toFixed(decimalsToKeep)
|
||||||
return `>${truncatedValue}m`
|
return `${truncatedValue}m`
|
||||||
} else if (convertedBalance > 1000) {
|
} else if (convertedBalance > 1000) {
|
||||||
truncatedValue = (balance / 1000).toFixed(decimalsToKeep)
|
truncatedValue = (balance / 1000).toFixed(decimalsToKeep)
|
||||||
return `>${truncatedValue}k`
|
return `${truncatedValue}k`
|
||||||
|
} else if (convertedBalance === 0) {
|
||||||
|
return '0'
|
||||||
|
} else if (convertedBalance < 0.001) {
|
||||||
|
return '<0.001'
|
||||||
} else if (convertedBalance < 1) {
|
} else if (convertedBalance < 1) {
|
||||||
var exponent = balance.match(/\.0*/)[0].length
|
var stringBalance = convertedBalance.toString()
|
||||||
truncatedValue = (convertedBalance * Math.pow(10, exponent)).toFixed(decimalsToKeep)
|
if (stringBalance.split('.')[1].length > 3) {
|
||||||
return `<${truncatedValue}e-${exponent}`
|
return convertedBalance.toFixed(3)
|
||||||
|
} else {
|
||||||
|
return stringBalance
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return convertedBalance.toFixed(decimalsToKeep)
|
return convertedBalance.toFixed(decimalsToKeep)
|
||||||
}
|
}
|
||||||
|
18
ui/lib/account-link.js
Normal file
18
ui/lib/account-link.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module.exports = function(address, network) {
|
||||||
|
const net = parseInt(network)
|
||||||
|
let link
|
||||||
|
|
||||||
|
switch (net) {
|
||||||
|
case 1: // main net
|
||||||
|
link = `http://etherscan.io/address/${address}`
|
||||||
|
break
|
||||||
|
case 2: // morden test net
|
||||||
|
link = `http://testnet.etherscan.io/address/${address}`
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
link = ''
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return link
|
||||||
|
}
|
57
ui/lib/persistent-form.js
Normal file
57
ui/lib/persistent-form.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
|
const defaultKey = 'persistent-form-default'
|
||||||
|
const eventName = 'keyup'
|
||||||
|
|
||||||
|
module.exports = PersistentForm
|
||||||
|
|
||||||
|
function PersistentForm () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(PersistentForm, Component)
|
||||||
|
|
||||||
|
PersistentForm.prototype.componentDidMount = function () {
|
||||||
|
const fields = document.querySelectorAll('[data-persistent-formid]')
|
||||||
|
const store = this.getPersistentStore()
|
||||||
|
fields.forEach((field) => {
|
||||||
|
const key = field.getAttribute('data-persistent-formid')
|
||||||
|
const cached = store[key]
|
||||||
|
if (cached !== undefined) {
|
||||||
|
field.value = cached
|
||||||
|
}
|
||||||
|
|
||||||
|
field.addEventListener(eventName, this.persistentFieldDidUpdate.bind(this))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentForm.prototype.getPersistentStore = function () {
|
||||||
|
let store = window.localStorage[this.persistentFormParentId || defaultKey]
|
||||||
|
if (store && store !== 'null') {
|
||||||
|
store = JSON.parse(store)
|
||||||
|
} else {
|
||||||
|
store = {}
|
||||||
|
}
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentForm.prototype.setPersistentStore = function (newStore) {
|
||||||
|
window.localStorage[this.persistentFormParentId || defaultKey] = JSON.stringify(newStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentForm.prototype.persistentFieldDidUpdate = function (event) {
|
||||||
|
const field = event.target
|
||||||
|
const store = this.getPersistentStore()
|
||||||
|
const key = field.getAttribute('data-persistent-formid')
|
||||||
|
const val = field.value
|
||||||
|
store[key] = val
|
||||||
|
this.setPersistentStore(store)
|
||||||
|
}
|
||||||
|
|
||||||
|
PersistentForm.prototype.componentWillUnmount = function () {
|
||||||
|
const fields = document.querySelectorAll('[data-persistent-formid]')
|
||||||
|
fields.forEach((field) => {
|
||||||
|
field.removeEventListener(eventName, this.persistentFieldDidUpdate.bind(this))
|
||||||
|
})
|
||||||
|
this.setPersistentStore({})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user