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
|
||||
ui/app/conversion-util.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ builds/
|
||||
notes.txt
|
||||
app/.DS_Store
|
||||
development/bundle.js
|
||||
builds.zip
|
||||
|
36
CHANGELOG.md
36
CHANGELOG.md
@ -2,7 +2,43 @@
|
||||
|
||||
## 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.
|
||||
- Make dapp-metamask connection more reliable
|
||||
- Remove Ethereum Classic from provider menu.
|
||||
|
||||
## 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",
|
||||
"short_name": "Metamask",
|
||||
"version": "2.7.3",
|
||||
"version": "2.10.0",
|
||||
"manifest_version": 2,
|
||||
"author": "https://metamask.io",
|
||||
"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": {
|
||||
"16": "images/icon-16.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 eos = require('end-of-stream')
|
||||
const PortStream = require('./lib/port-stream.js')
|
||||
const createUnlockRequestNotification = require('./lib/notifications.js').createUnlockRequestNotification
|
||||
const createTxNotification = require('./lib/notifications.js').createTxNotification
|
||||
const createMsgNotification = require('./lib/notifications.js').createMsgNotification
|
||||
const notification = require('./lib/notifications.js')
|
||||
const messageManager = require('./lib/message-manager')
|
||||
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||
const MetamaskController = require('./metamask-controller')
|
||||
@ -13,6 +11,7 @@ const extension = require('./lib/extension')
|
||||
|
||||
const STORAGE_KEY = 'metamask-config'
|
||||
|
||||
|
||||
const controller = new MetamaskController({
|
||||
// User confirmation callbacks:
|
||||
showUnconfirmedMessage,
|
||||
@ -25,41 +24,15 @@ const controller = new MetamaskController({
|
||||
const idStore = controller.idStore
|
||||
|
||||
function unlockAccountMessage () {
|
||||
createUnlockRequestNotification({
|
||||
title: 'Account Unlock Request',
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
function showUnconfirmedMessage (msgParams, msgId) {
|
||||
var controllerState = controller.getState()
|
||||
|
||||
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),
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
|
||||
var controllerState = controller.getState()
|
||||
|
||||
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),
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
//
|
||||
@ -68,7 +41,7 @@ function showUnconfirmedTx (txParams, txData, onTxDoneCb) {
|
||||
|
||||
extension.runtime.onConnect.addListener(connectRemote)
|
||||
function connectRemote (remotePort) {
|
||||
var isMetaMaskInternalProcess = (remotePort.name === 'popup')
|
||||
var isMetaMaskInternalProcess = remotePort.name === 'popup' || remotePort.name === 'notification'
|
||||
var portStream = new PortStream(remotePort)
|
||||
if (isMetaMaskInternalProcess) {
|
||||
// communication with popup
|
||||
@ -108,7 +81,7 @@ function setupControllerConnection (stream) {
|
||||
dnode.on('remote', (remote) => {
|
||||
// push updates to popup
|
||||
controller.ethStore.on('update', controller.sendUpdate.bind(controller))
|
||||
controller.remote = remote
|
||||
controller.listeners.push(remote)
|
||||
idStore.on('update', controller.sendUpdate.bind(controller))
|
||||
|
||||
// teardown on disconnect
|
||||
@ -188,4 +161,3 @@ function setData (data) {
|
||||
window.localStorage[STORAGE_KEY] = JSON.stringify(data)
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
@ -1,14 +1,13 @@
|
||||
const MAINET_RPC_URL = 'https://mainnet.infura.io/'
|
||||
const TESTNET_RPC_URL = 'https://morden.infura.io/'
|
||||
const DEFAULT_RPC_URL = TESTNET_RPC_URL
|
||||
const CLASSIC_RPC_URL = 'https://mainnet-nf.infura.io/'
|
||||
|
||||
global.METAMASK_DEBUG = false
|
||||
|
||||
module.exports = {
|
||||
network: {
|
||||
default: DEFAULT_RPC_URL,
|
||||
mainnet: MAINET_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 ObjectMultiplex = require('./lib/obj-multiplex')
|
||||
const extension = require('./lib/extension')
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*global Web3*/
|
||||
cleanContextForImports()
|
||||
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 MetamaskInpageProvider = require('./lib/inpage-provider.js')
|
||||
restoreContextAfterImports()
|
||||
@ -54,7 +54,7 @@ var __define
|
||||
function cleanContextForImports () {
|
||||
__define = global.define
|
||||
try {
|
||||
delete global.define
|
||||
global.define = undefined
|
||||
} catch (_) {
|
||||
console.warn('MetaMask - global.define could not be deleted.')
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
const once = require('once')
|
||||
const ensnare = require('./ensnare.js')
|
||||
const ensnare = require('ensnare')
|
||||
|
||||
module.exports = setupDappAutoReload
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
const Migrator = require('pojo-migrator')
|
||||
const MetamaskConfig = require('../config.js')
|
||||
const migrations = require('./migrations')
|
||||
const rp = require('request-promise')
|
||||
|
||||
const TESTNET_RPC = MetamaskConfig.network.testnet
|
||||
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
||||
const CLASSIC_RPC = MetamaskConfig.network.classic
|
||||
const txLimit = 40
|
||||
|
||||
/* The config-manager is a convenience object
|
||||
* wrapping a pojo-migrator.
|
||||
@ -15,6 +16,8 @@ const CLASSIC_RPC = MetamaskConfig.network.classic
|
||||
*/
|
||||
module.exports = ConfigManager
|
||||
function ConfigManager (opts) {
|
||||
this.txLimit = txLimit
|
||||
|
||||
// ConfigManager is observable and will emit updates
|
||||
this._subs = []
|
||||
|
||||
@ -145,9 +148,6 @@ ConfigManager.prototype.getCurrentRpcAddress = function () {
|
||||
case 'testnet':
|
||||
return TESTNET_RPC
|
||||
|
||||
case 'classic':
|
||||
return CLASSIC_RPC
|
||||
|
||||
default:
|
||||
return provider && provider.rpcTarget ? provider.rpcTarget : TESTNET_RPC
|
||||
}
|
||||
@ -184,6 +184,9 @@ ConfigManager.prototype._saveTxList = function (txList) {
|
||||
|
||||
ConfigManager.prototype.addTx = function (tx) {
|
||||
var transactions = this.getTxList()
|
||||
while (transactions.length > this.txLimit - 1) {
|
||||
transactions.shift()
|
||||
}
|
||||
transactions.push(tx)
|
||||
this._saveTxList(transactions)
|
||||
}
|
||||
@ -274,9 +277,61 @@ ConfigManager.prototype.getConfirmed = function () {
|
||||
return ('isConfirmed' in data) && data.isConfirmed
|
||||
}
|
||||
|
||||
ConfigManager.prototype.setShouldntShowWarning = function (confirmed) {
|
||||
ConfigManager.prototype.setCurrentFiat = function (currency) {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -284,3 +339,37 @@ ConfigManager.prototype.getShouldntShowWarning = function () {
|
||||
var data = this.getData()
|
||||
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) {}
|
||||
|
||||
try {
|
||||
if (browser[api]) {
|
||||
_this[api] = browser[api]
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
_this.api = browser.extension[api]
|
||||
} catch (e) {}
|
||||
|
@ -45,7 +45,11 @@ function IdentityStore (opts = {}) {
|
||||
|
||||
IdentityStore.prototype.createNewVault = function (password, entropy, cb) {
|
||||
delete this._keyStore
|
||||
var serializedKeystore = this.configManager.getWallet()
|
||||
|
||||
if (serializedKeystore) {
|
||||
this.configManager.setData({})
|
||||
}
|
||||
this._createIdmgmt(password, null, entropy, (err) => {
|
||||
if (err) return cb(err)
|
||||
|
||||
@ -100,6 +104,10 @@ IdentityStore.prototype.getState = function () {
|
||||
unconfMsgs: messageManager.unconfirmedMsgs(),
|
||||
messages: messageManager.getMsgList(),
|
||||
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'
|
||||
return this._didUpdate()
|
||||
}
|
||||
|
||||
console.log('web3.getNetwork returned ' + network)
|
||||
if (global.METAMASK_DEBUG) {
|
||||
console.log('web3.getNetwork returned ' + network)
|
||||
}
|
||||
this._currentState.network = network
|
||||
this._didUpdate()
|
||||
})
|
||||
@ -432,6 +441,7 @@ IdentityStore.prototype.tryPassword = function (password, cb) {
|
||||
|
||||
IdentityStore.prototype._createIdmgmt = function (password, seed, entropy, cb) {
|
||||
const configManager = this.configManager
|
||||
|
||||
var keyStore = null
|
||||
LightwalletKeyStore.deriveKeyFromPassword(password, (err, derivedKey) => {
|
||||
if (err) return cb(err)
|
||||
@ -475,7 +485,9 @@ IdentityStore.prototype._restoreFromSeed = function (password, seed, derivedKey)
|
||||
|
||||
keyStore.generateNewAddress(derivedKey, 3)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
const HttpProvider = require('web3/lib/web3/httpprovider')
|
||||
const Streams = require('mississippi')
|
||||
const ObjectMultiplex = require('./obj-multiplex')
|
||||
const StreamProvider = require('web3-stream-provider')
|
||||
const RemoteStore = require('./remote-store.js').RemoteStore
|
||||
const MetamaskConfig = require('../config.js')
|
||||
|
||||
module.exports = MetamaskInpageProvider
|
||||
|
||||
@ -27,13 +25,6 @@ function MetamaskInpageProvider (connectionStream) {
|
||||
})
|
||||
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
|
||||
var asyncProvider = new StreamProvider()
|
||||
Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) {
|
||||
@ -42,15 +33,23 @@ function MetamaskInpageProvider (connectionStream) {
|
||||
})
|
||||
asyncProvider.on('error', console.error.bind(console))
|
||||
self.asyncProvider = asyncProvider
|
||||
// overwrite own sendAsync method
|
||||
self.sendAsync = asyncProvider.sendAsync.bind(asyncProvider)
|
||||
// handle sendAsync requests via 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) {
|
||||
const self = this
|
||||
|
||||
let selectedAddress
|
||||
|
||||
var result = null
|
||||
let result = null
|
||||
switch (payload.method) {
|
||||
|
||||
case 'eth_accounts':
|
||||
@ -65,9 +64,10 @@ MetamaskInpageProvider.prototype.send = function (payload) {
|
||||
result = selectedAddress || '0x0000000000000000000000000000000000000000'
|
||||
break
|
||||
|
||||
// fallback to normal rpc
|
||||
// throw not-supported Error
|
||||
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
|
||||
|
||||
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) {
|
||||
// read local cache
|
||||
var initState = JSON.parse(localStorage[storageKey] || '{}')
|
||||
@ -129,3 +100,21 @@ function remoteStoreWithLocalStorageCache (storageKey) {
|
||||
|
||||
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')
|
||||
var notificationHandlers = {}
|
||||
|
||||
const notifications = {
|
||||
createUnlockRequestNotification: createUnlockRequestNotification,
|
||||
createTxNotification: createTxNotification,
|
||||
createMsgNotification: createMsgNotification,
|
||||
show,
|
||||
getPopup,
|
||||
closePopup,
|
||||
}
|
||||
module.exports = notifications
|
||||
window.METAMASK_NOTIFIER = notifications
|
||||
|
||||
setupListeners()
|
||||
|
||||
function setupListeners () {
|
||||
// guard for extension bug https://github.com/MetaMask/metamask-plugin/issues/236
|
||||
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()
|
||||
function show () {
|
||||
getPopup((popup) => {
|
||||
if (popup) {
|
||||
return extension.windows.update(popup.id, { focused: true })
|
||||
}
|
||||
extension.notifications.clear(notificationId)
|
||||
})
|
||||
|
||||
// notification teardown
|
||||
extension.notifications.onClosed.addListener(function (notificationId) {
|
||||
delete notificationHandlers[notificationId]
|
||||
extension.windows.create({
|
||||
url: 'notification.html',
|
||||
type: 'detached_panel',
|
||||
focused: true,
|
||||
width: 360,
|
||||
height: 500,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// creation helper
|
||||
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.'
|
||||
function getPopup(cb) {
|
||||
|
||||
var id = createId()
|
||||
extension.notifications.create(id, {
|
||||
type: 'basic',
|
||||
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,
|
||||
// Ignore in test environment
|
||||
if (!extension.windows) {
|
||||
return cb(null)
|
||||
}
|
||||
}
|
||||
|
||||
function renderTxNotificationSVG (state, cb) {
|
||||
var content = h(PendingTxDetails, state)
|
||||
renderNotificationSVG(content, cb)
|
||||
}
|
||||
extension.windows.getAll({}, (windows) => {
|
||||
let popup = windows.find((win) => {
|
||||
return win.type === 'popup'
|
||||
})
|
||||
|
||||
function renderMsgNotificationSVG (state, cb) {
|
||||
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)
|
||||
cb(popup)
|
||||
})
|
||||
}
|
||||
|
||||
function svgWrapper (content) {
|
||||
var wrapperSource = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="240">
|
||||
<foreignObject x="0" y="0" width="100%" height="100%">
|
||||
<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)
|
||||
function closePopup() {
|
||||
getPopup((popup) => {
|
||||
if (!popup) return
|
||||
extension.windows.remove(popup.id, console.error)
|
||||
})
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ module.exports = class MetamaskController {
|
||||
|
||||
constructor (opts) {
|
||||
this.opts = opts
|
||||
this.listeners = []
|
||||
this.configManager = new ConfigManager(opts)
|
||||
this.idStore = new IdentityStore({
|
||||
configManager: this.configManager,
|
||||
@ -21,6 +22,9 @@ module.exports = class MetamaskController {
|
||||
this.idStore.setStore(this.ethStore)
|
||||
this.messageManager = messageManager
|
||||
this.publicConfigStore = this.initPublicConfigStore()
|
||||
this.configManager.setCurrentFiat('USD')
|
||||
this.configManager.updateConversionRate()
|
||||
this.scheduleConversionInterval()
|
||||
}
|
||||
|
||||
getState () {
|
||||
@ -40,7 +44,9 @@ module.exports = class MetamaskController {
|
||||
setProviderType: this.setProviderType.bind(this),
|
||||
useEtherscanProvider: this.useEtherscanProvider.bind(this),
|
||||
agreeToDisclaimer: this.agreeToDisclaimer.bind(this),
|
||||
setCurrentFiat: this.setCurrentFiat.bind(this),
|
||||
agreeToEthWarning: this.agreeToEthWarning.bind(this),
|
||||
|
||||
// forward directly to idStore
|
||||
createNewVault: idStore.createNewVault.bind(idStore),
|
||||
recoverFromSeed: idStore.recoverFromSeed.bind(idStore),
|
||||
@ -59,6 +65,8 @@ module.exports = class MetamaskController {
|
||||
recoverSeed: idStore.recoverSeed.bind(idStore),
|
||||
// coinbase
|
||||
buyEth: this.buyEth.bind(this),
|
||||
// shapeshift
|
||||
createShapeShiftTx: this.createShapeShiftTx.bind(this),
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +102,9 @@ module.exports = class MetamaskController {
|
||||
function logger (err, request, response) {
|
||||
if (err) return console.error(err)
|
||||
if (!request.isMetamaskInternal) {
|
||||
console.log(`RPC (${originDomain}):`, request, '->', response)
|
||||
if (global.METAMASK_DEBUG) {
|
||||
console.log(`RPC (${originDomain}):`, request, '->', response)
|
||||
}
|
||||
if (response.error) {
|
||||
console.error('Error in RPC response:\n', response.error)
|
||||
}
|
||||
@ -103,9 +113,9 @@ module.exports = class MetamaskController {
|
||||
}
|
||||
|
||||
sendUpdate () {
|
||||
if (this.remote) {
|
||||
this.remote.sendUpdate(this.getState())
|
||||
}
|
||||
this.listeners.forEach((remote) => {
|
||||
remote.sendUpdate(this.getState())
|
||||
})
|
||||
}
|
||||
|
||||
initializeProvider (opts) {
|
||||
@ -121,10 +131,17 @@ module.exports = class MetamaskController {
|
||||
},
|
||||
// tx signing
|
||||
approveTransaction: this.newUnsignedTransaction.bind(this),
|
||||
signTransaction: idStore.signTransaction.bind(idStore),
|
||||
signTransaction: (...args) => {
|
||||
idStore.signTransaction(...args)
|
||||
this.sendUpdate()
|
||||
},
|
||||
|
||||
// msg signing
|
||||
approveMessage: this.newUnsignedMessage.bind(this),
|
||||
signMessage: idStore.signMessage.bind(idStore),
|
||||
signMessage: (...args) => {
|
||||
idStore.signMessage(...args)
|
||||
this.sendUpdate()
|
||||
},
|
||||
}
|
||||
|
||||
var provider = MetaMaskProvider(providerOpts)
|
||||
@ -184,6 +201,8 @@ module.exports = class MetamaskController {
|
||||
|
||||
// It's locked
|
||||
if (!state.isUnlocked) {
|
||||
|
||||
// Allow the environment to define an unlock message.
|
||||
this.opts.unlockAccountMessage()
|
||||
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, noop)
|
||||
|
||||
@ -191,6 +210,7 @@ module.exports = class MetamaskController {
|
||||
} else {
|
||||
idStore.addUnconfirmedTransaction(txParams, onTxDoneCb, (err, txData) => {
|
||||
if (err) return onTxDoneCb(err)
|
||||
this.sendUpdate()
|
||||
this.opts.showUnconfirmedTx(txParams, txData, onTxDoneCb)
|
||||
})
|
||||
}
|
||||
@ -199,9 +219,11 @@ module.exports = class MetamaskController {
|
||||
newUnsignedMessage (msgParams, cb) {
|
||||
var state = this.idStore.getState()
|
||||
if (!state.isUnlocked) {
|
||||
this.idStore.addUnconfirmedMessage(msgParams, cb)
|
||||
this.opts.unlockAccountMessage()
|
||||
} else {
|
||||
this.addUnconfirmedMessage(msgParams, cb)
|
||||
this.sendUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,7 +240,9 @@ module.exports = class MetamaskController {
|
||||
|
||||
// Log blocks
|
||||
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()
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
try {
|
||||
this.configManager.setShouldntShowWarning(true)
|
||||
this.configManager.setShouldntShowWarning()
|
||||
cb()
|
||||
} catch (e) {
|
||||
cb(e)
|
||||
@ -283,6 +332,9 @@ module.exports = class MetamaskController {
|
||||
})
|
||||
}
|
||||
|
||||
createShapeShiftTx (depositAddress, depositType) {
|
||||
this.configManager.createShapeShiftTx(depositAddress, depositType)
|
||||
}
|
||||
}
|
||||
|
||||
function noop () {}
|
||||
|
@ -9,7 +9,9 @@ const injectCss = require('inject-css')
|
||||
const PortStream = require('./lib/port-stream.js')
|
||||
const StreamProvider = require('web3-stream-provider')
|
||||
const setupMultiplex = require('./lib/stream-utils.js').setupMultiplex
|
||||
const isPopupOrNotification = require('./lib/is-popup-or-notification')
|
||||
const extension = require('./lib/extension')
|
||||
const notification = require('./lib/notifications')
|
||||
|
||||
// setup app
|
||||
var css = MetaMaskUiCss()
|
||||
@ -22,7 +24,11 @@ async.parallel({
|
||||
|
||||
function connectToAccountManager (cb) {
|
||||
// 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)
|
||||
// setup multiplexing
|
||||
var mx = setupMultiplex(portStream)
|
||||
@ -93,3 +99,9 @@ function setupApp (err, opts) {
|
||||
networkVersion: opts.networkVersion,
|
||||
})
|
||||
}
|
||||
|
||||
function closePopupIfOpen(name) {
|
||||
if (name !== 'notification') {
|
||||
notification.closePopup()
|
||||
}
|
||||
}
|
||||
|
@ -4,3 +4,10 @@ machine:
|
||||
dependencies:
|
||||
pre:
|
||||
- "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": {
|
||||
"currentFiat": "USD",
|
||||
"conversionRate": 11.06608791,
|
||||
"conversionDate": 1470421024,
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"currentDomain": "example.com",
|
||||
@ -72,7 +75,7 @@
|
||||
"txParams": {
|
||||
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
|
||||
"to": "0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761",
|
||||
"value": "0x99966c8104aa57038000",
|
||||
"value": "0x0",
|
||||
"origin": "thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com",
|
||||
"metamaskId": 1467923203344608,
|
||||
"metamaskNetworkId": "2"
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"metamask": {
|
||||
"currentFiat": "USD",
|
||||
"conversionRate": 11.06608791,
|
||||
"conversionDate": 1470421024,
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"currentDomain": "example.com",
|
||||
@ -81,4 +84,4 @@
|
||||
"warning": null
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"metamask": {
|
||||
"currentFiat": "USD",
|
||||
"conversionRate": 11.06608791,
|
||||
"conversionDate": 1470421024,
|
||||
"isInitialized": true,
|
||||
"isUnlocked": true,
|
||||
"currentDomain": "example.com",
|
||||
@ -68,7 +71,7 @@
|
||||
"appState": {
|
||||
"menuOpen": false,
|
||||
"currentView": {
|
||||
"name": "accounts"
|
||||
"name": "config"
|
||||
},
|
||||
"accountDetail": {
|
||||
"subview": "transactions",
|
||||
@ -82,4 +85,4 @@
|
||||
"scrollToBottom": true
|
||||
},
|
||||
"identities": {}
|
||||
}
|
||||
}
|
||||
|
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 actions = require('./ui/app/actions')
|
||||
const states = require('./development/states')
|
||||
const Selector = require('./development/selector')
|
||||
const MetamaskController = require('./app/scripts/metamask-controller')
|
||||
const extension = require('./development/mockExtension')
|
||||
|
||||
@ -139,6 +140,8 @@ render(
|
||||
},
|
||||
}, 'Reset State'),
|
||||
|
||||
h(Selector, { actions, selectedKey: selectedView, states, store }),
|
||||
|
||||
h('.mock-app-root', {
|
||||
style: {
|
||||
height: '500px',
|
||||
|
16
package.json
16
package.json
@ -5,6 +5,9 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "gulp dev",
|
||||
"lint": "gulp lint",
|
||||
"dev": "gulp dev",
|
||||
"dist": "gulp dist",
|
||||
"test": "npm run fastTest && npm run ci",
|
||||
"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\"",
|
||||
@ -35,6 +38,7 @@
|
||||
"debounce": "^1.0.0",
|
||||
"dnode": "^1.2.2",
|
||||
"end-of-stream": "^1.1.0",
|
||||
"ensnare": "^1.0.0",
|
||||
"eth-bin-to-ops": "^1.0.0",
|
||||
"eth-lightwallet": "^2.3.3",
|
||||
"eth-query": "^1.0.3",
|
||||
@ -47,12 +51,13 @@
|
||||
"inject-css": "^0.1.1",
|
||||
"jazzicon": "^1.1.3",
|
||||
"menu-droppo": "^1.1.0",
|
||||
"metamask-logo": "^1.1.5",
|
||||
"metamask-logo": "^1.3.1",
|
||||
"mississippi": "^1.2.0",
|
||||
"multiplex": "^6.7.0",
|
||||
"once": "^1.3.3",
|
||||
"pojo-migrator": "^2.1.0",
|
||||
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
||||
"post-message-stream": "^1.0.0",
|
||||
"pumpify": "^1.3.4",
|
||||
"react": "^15.0.2",
|
||||
"react-addons-css-transition-group": "^15.0.2",
|
||||
@ -65,14 +70,15 @@
|
||||
"redux": "^3.0.5",
|
||||
"redux-logger": "^2.3.1",
|
||||
"redux-thunk": "^1.0.2",
|
||||
"request-promise": "^4.1.1",
|
||||
"sandwich-expando": "^1.0.5",
|
||||
"textarea-caret": "^3.0.1",
|
||||
"three.js": "^0.73.2",
|
||||
"through2": "^2.0.1",
|
||||
"vreme": "^3.0.2",
|
||||
"web3": "ethereum/web3.js#0.16.0",
|
||||
"web3-provider-engine": "^7.8.3",
|
||||
"web3-stream-provider": "^2.0.5",
|
||||
"web3": "^0.17.0-alpha",
|
||||
"web3-provider-engine": "^8.0.2",
|
||||
"web3-stream-provider": "^2.0.6",
|
||||
"xtend": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -102,10 +108,12 @@
|
||||
"mocha-eslint": "^2.1.1",
|
||||
"mocha-jsdom": "^1.1.0",
|
||||
"mocha-sinon": "^1.1.5",
|
||||
"nock": "^8.0.0",
|
||||
"qs": "^6.2.0",
|
||||
"qunit": "^0.9.1",
|
||||
"sinon": "^1.17.3",
|
||||
"tape": "^4.5.1",
|
||||
"testem": "^1.10.3",
|
||||
"uglifyify": "^3.0.1",
|
||||
"vinyl-buffer": "^1.0.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 STORAGE_KEY = 'metamask-persistance-key'
|
||||
var configManagerGen = require('../lib/mock-config-manager')
|
||||
var configManager
|
||||
const rp = require('request-promise')
|
||||
const nock = require('nock')
|
||||
|
||||
describe('config-manager', function() {
|
||||
|
||||
@ -11,6 +13,91 @@ describe('config-manager', function() {
|
||||
configManager = configManagerGen()
|
||||
})
|
||||
|
||||
describe('currency conversions', function() {
|
||||
|
||||
describe('#getCurrentFiat', function() {
|
||||
it('should return false if no previous key exists', function() {
|
||||
var result = configManager.getCurrentFiat()
|
||||
assert.ok(!result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#setCurrentFiat', function() {
|
||||
it('should make getCurrentFiat return true once set', function() {
|
||||
assert.equal(configManager.getCurrentFiat(), false)
|
||||
configManager.setCurrentFiat('USD')
|
||||
var result = configManager.getCurrentFiat()
|
||||
assert.equal(result, 'USD')
|
||||
})
|
||||
|
||||
it('should work with other currencies as well', function() {
|
||||
assert.equal(configManager.getCurrentFiat(), false)
|
||||
configManager.setCurrentFiat('JPY')
|
||||
var result = configManager.getCurrentFiat()
|
||||
assert.equal(result, 'JPY')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getConversionRate', function() {
|
||||
it('should return false if non-existent', function() {
|
||||
var result = configManager.getConversionRate()
|
||||
assert.ok(!result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#updateConversionRate', function() {
|
||||
it('should retrieve an update for ETH to USD and set it in memory', function(done) {
|
||||
this.timeout(15000)
|
||||
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('#getConfirmed', function() {
|
||||
@ -157,6 +244,17 @@ describe('config-manager', function() {
|
||||
assert.equal(result.length, 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() {
|
||||
@ -215,4 +313,3 @@ describe('config-manager', function() {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -156,7 +156,12 @@ describe('util', function() {
|
||||
var result = util.formatBalance(input)
|
||||
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() {
|
||||
|
@ -4,22 +4,24 @@ const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const CopyButton = require('./components/copyButton')
|
||||
const AccountInfoLink = require('./components/account-info-link')
|
||||
const actions = require('./actions')
|
||||
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
|
||||
const valuesFor = require('./util').valuesFor
|
||||
|
||||
const Identicon = require('./components/identicon')
|
||||
const EtherBalance = require('./components/eth-balance')
|
||||
const AccountEtherBalance = require('./components/account-eth-balance')
|
||||
const TransactionList = require('./components/transaction-list')
|
||||
const ExportAccountView = require('./components/account-export')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
const EditableLabel = require('./components/editable-label')
|
||||
const Tooltip = require('./components/tooltip')
|
||||
|
||||
const BuyButtonSubview = require('./components/buy-button-subview')
|
||||
module.exports = connect(mapStateToProps)(AccountDetailScreen)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
metamask: state.metamask,
|
||||
identities: state.metamask.identities,
|
||||
accounts: state.metamask.accounts,
|
||||
address: state.metamask.selectedAccount,
|
||||
@ -29,6 +31,7 @@ function mapStateToProps (state) {
|
||||
unconfTxs: valuesFor(state.metamask.unconfTxs),
|
||||
unconfMsgs: valuesFor(state.metamask.unconfMsgs),
|
||||
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 identity = props.identities[selected]
|
||||
var account = props.accounts[selected]
|
||||
const { network } = props
|
||||
|
||||
return (
|
||||
|
||||
@ -125,6 +129,9 @@ AccountDetailScreen.prototype.render = function () {
|
||||
bottom: '15px',
|
||||
},
|
||||
}, [
|
||||
|
||||
h(AccountInfoLink, { selected, network }),
|
||||
|
||||
h(CopyButton, {
|
||||
value: ethUtil.toChecksumAddress(selected),
|
||||
}),
|
||||
@ -134,16 +141,15 @@ AccountDetailScreen.prototype.render = function () {
|
||||
}, [
|
||||
h('div', {
|
||||
style: {
|
||||
margin: '5px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
}, [
|
||||
h('img.cursor-pointer.color-orange', {
|
||||
src: 'images/key-32.png',
|
||||
onClick: () => this.requestAccountExport(selected),
|
||||
style: {
|
||||
margin: '0px 5px',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
height: '19px',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
@ -162,9 +168,8 @@ AccountDetailScreen.prototype.render = function () {
|
||||
},
|
||||
}, [
|
||||
|
||||
h(EtherBalance, {
|
||||
h(AccountEtherBalance, {
|
||||
value: account && account.balance,
|
||||
mainBalance: true,
|
||||
style: {
|
||||
lineHeight: '7px',
|
||||
marginTop: '10px',
|
||||
@ -172,7 +177,7 @@ AccountDetailScreen.prototype.render = function () {
|
||||
}),
|
||||
|
||||
h('button', {
|
||||
onClick: () => props.dispatch(actions.buyEth(selected)),
|
||||
onClick: () => props.dispatch(actions.buyEthView(selected)),
|
||||
style: {
|
||||
marginBottom: '20px',
|
||||
marginRight: '8px',
|
||||
@ -220,13 +225,15 @@ AccountDetailScreen.prototype.subview = function () {
|
||||
case 'export':
|
||||
var state = extend({key: 'export'}, this.props)
|
||||
return h(ExportAccountView, state)
|
||||
case 'buyForm':
|
||||
return h(BuyButtonSubview, extend({key: 'buyForm'}, this.props))
|
||||
default:
|
||||
return this.transactionList()
|
||||
}
|
||||
}
|
||||
|
||||
AccountDetailScreen.prototype.transactionList = function () {
|
||||
const { transactions, unconfTxs, unconfMsgs, address, network } = this.props
|
||||
const { transactions, unconfTxs, unconfMsgs, address, network, shapeShiftTxList } = this.props
|
||||
|
||||
var txsToRender = transactions
|
||||
// only transactions that are from the current address
|
||||
@ -241,6 +248,8 @@ AccountDetailScreen.prototype.transactionList = function () {
|
||||
network,
|
||||
unconfTxs,
|
||||
unconfMsgs,
|
||||
address,
|
||||
shapeShiftTxList,
|
||||
viewPendingTx: (txId) => {
|
||||
this.props.dispatch(actions.viewPendingTx(txId))
|
||||
},
|
||||
@ -251,3 +260,13 @@ AccountDetailScreen.prototype.requestAccountExport = function () {
|
||||
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 ethUtil = require('ethereumjs-util')
|
||||
|
||||
const EtherBalance = require('../components/eth-balance')
|
||||
const AccountEtherBalance = require('../components/account-eth-balance')
|
||||
const CopyButton = require('../components/copyButton')
|
||||
const Identicon = require('../components/identicon')
|
||||
|
||||
@ -50,8 +50,12 @@ NewComponent.prototype.render = function () {
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
}, ethUtil.toChecksumAddress(identity.address)),
|
||||
h(EtherBalance, {
|
||||
h(AccountEtherBalance, {
|
||||
value: account.balance,
|
||||
style: {
|
||||
lineHeight: '7px',
|
||||
marginTop: '10px',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
|
||||
|
@ -55,6 +55,8 @@ var actions = {
|
||||
SHOW_CONF_MSG_PAGE: 'SHOW_CONF_MSG_PAGE',
|
||||
REVEAL_ACCOUNT: 'REVEAL_ACCOUNT',
|
||||
revealAccount: revealAccount,
|
||||
SET_CURRENT_FIAT: 'SET_CURRENT_FIAT',
|
||||
setCurrentFiat: setCurrentFiat,
|
||||
// account detail screen
|
||||
SHOW_SEND_PAGE: 'SHOW_SEND_PAGE',
|
||||
showSendPage: showSendPage,
|
||||
@ -113,6 +115,34 @@ var actions = {
|
||||
// buy Eth with coinbase
|
||||
BUY_ETH: 'BUY_ETH',
|
||||
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
|
||||
@ -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) {
|
||||
return (dispatch) => {
|
||||
dispatch(actions.showLoadingIndication())
|
||||
@ -329,6 +376,12 @@ function showNewVaultSeed (seed) {
|
||||
}
|
||||
}
|
||||
|
||||
function backToUnlockView () {
|
||||
return {
|
||||
type: actions.BACK_TO_UNLOCK_VIEW,
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// unlock screen
|
||||
//
|
||||
@ -457,6 +510,12 @@ function showConfigPage (transitionForward = true) {
|
||||
}
|
||||
}
|
||||
|
||||
function goBackToInitView () {
|
||||
return {
|
||||
type: actions.BACK_TO_INIT_MENU,
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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) {
|
||||
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 Tooltip = require('./components/tooltip')
|
||||
const EthStoreWarning = require('./eth-store-warning')
|
||||
|
||||
const BuyView = require('./components/buy-button-subview')
|
||||
const QrView = require('./components/qr-code')
|
||||
module.exports = connect(mapStateToProps)(App)
|
||||
|
||||
inherits(App, Component)
|
||||
@ -50,6 +51,7 @@ function mapStateToProps (state) {
|
||||
menuOpen: state.appState.menuOpen,
|
||||
network: state.metamask.network,
|
||||
provider: state.metamask.provider,
|
||||
forgottenPassword: state.appState.forgottenPassword,
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,6 +90,7 @@ App.prototype.render = function () {
|
||||
transitionLeaveTimeout: 300,
|
||||
}, [
|
||||
this.renderPrimary(),
|
||||
this.renderBackToInitButton(),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
@ -95,6 +98,11 @@ App.prototype.render = function () {
|
||||
}
|
||||
|
||||
App.prototype.renderAppBar = function () {
|
||||
|
||||
if (window.METAMASK_UI_TYPE === 'notification') {
|
||||
return null
|
||||
}
|
||||
|
||||
const props = this.props
|
||||
const state = this.state || {}
|
||||
const isNetworkMenuOpen = state.isNetworkMenuOpen || false
|
||||
@ -225,15 +233,6 @@ App.prototype.renderNetworkDropdown = function () {
|
||||
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, {
|
||||
label: 'Morden Test Network',
|
||||
closeMenu: () => this.setState({ isNetworkMenuOpen: false }),
|
||||
@ -250,6 +249,13 @@ App.prototype.renderNetworkDropdown = function () {
|
||||
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),
|
||||
])
|
||||
}
|
||||
@ -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 () {
|
||||
var props = this.props
|
||||
@ -314,7 +406,7 @@ App.prototype.renderPrimary = function () {
|
||||
}
|
||||
|
||||
// show initialize screen
|
||||
if (!props.isInitialized) {
|
||||
if (!props.isInitialized || props.forgottenPassword) {
|
||||
// show current view
|
||||
switch (props.currentView.name) {
|
||||
|
||||
@ -366,6 +458,35 @@ App.prototype.renderPrimary = function () {
|
||||
|
||||
case '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:
|
||||
return h(AccountDetailScreen, {key: 'account-detail'})
|
||||
@ -387,12 +508,8 @@ App.prototype.toggleMetamaskActive = function () {
|
||||
App.prototype.renderCustomOption = function (rpcTarget) {
|
||||
switch (rpcTarget) {
|
||||
case undefined:
|
||||
return 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 }),
|
||||
})
|
||||
return null
|
||||
|
||||
case 'http://localhost:8545':
|
||||
return h(DropMenuItem, {
|
||||
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':
|
||||
if (providerType === 'mainnet') return h('.check', '✓')
|
||||
break
|
||||
case 'Ethereum Classic Network':
|
||||
if (providerType === 'classic') return h('.check', '✓')
|
||||
break
|
||||
case 'Morden Test Network':
|
||||
if (activeNetwork === '2') return h('.check', '✓')
|
||||
break
|
||||
|
@ -4,6 +4,7 @@ const inherits = require('util').inherits
|
||||
const formatBalance = require('../util').formatBalance
|
||||
const generateBalanceObject = require('../util').generateBalanceObject
|
||||
const Tooltip = require('./tooltip.js')
|
||||
|
||||
module.exports = EthBalanceComponent
|
||||
|
||||
inherits(EthBalanceComponent, Component)
|
||||
@ -14,29 +15,33 @@ function EthBalanceComponent () {
|
||||
EthBalanceComponent.prototype.render = function () {
|
||||
var state = this.props
|
||||
var style = state.style
|
||||
|
||||
const value = formatBalance(state.value, 6)
|
||||
var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
|
||||
const value = formatBalance(state.value, 6, needsParse)
|
||||
var width = state.width
|
||||
|
||||
return (
|
||||
|
||||
h('.ether-balance', {
|
||||
h('.ether-balance.ether-balance-amount', {
|
||||
style: style,
|
||||
}, [
|
||||
h('.ether-balance-amount', {
|
||||
h('div', {
|
||||
style: {
|
||||
display: 'inline',
|
||||
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
|
||||
var balanceObj = generateBalanceObject(value, state.shorten ? 1 : 3)
|
||||
var balance
|
||||
var splitBalance = value.split(' ')
|
||||
var ethNumber = splitBalance[0]
|
||||
var ethSuffix = splitBalance[1]
|
||||
|
||||
if (state.shorten) {
|
||||
balance = balanceObj.shortBalance
|
||||
@ -49,7 +54,7 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) {
|
||||
return (
|
||||
h(Tooltip, {
|
||||
position: 'bottom',
|
||||
title: value.split(' ')[0],
|
||||
title: `${ethNumber} ${ethSuffix}`,
|
||||
}, [
|
||||
h('.flex-column', {
|
||||
style: {
|
||||
@ -64,7 +69,7 @@ EthBalanceComponent.prototype.renderBalance = function (value, state) {
|
||||
width: '100%',
|
||||
textAlign: 'right',
|
||||
},
|
||||
}, balance),
|
||||
}, this.props.incoming ? `+${balance}` : balance),
|
||||
h('div', {
|
||||
style: {
|
||||
color: ' #AEAEAE',
|
||||
|
@ -14,8 +14,9 @@ function Mascot () {
|
||||
pxNotRatio: true,
|
||||
width: 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.unfollowMouse = this.logo.setFollowMouse.bind(this.logo, false)
|
||||
}
|
||||
@ -34,19 +35,24 @@ Mascot.prototype.render = function () {
|
||||
}
|
||||
|
||||
Mascot.prototype.componentDidMount = function () {
|
||||
if (!this.logo) return
|
||||
var targetDivId = 'metamask-mascot-container'
|
||||
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 () {
|
||||
if (!this.logo) return
|
||||
if (!this.logo.webGLSupport) return
|
||||
this.logo.canvas.remove()
|
||||
}
|
||||
|
||||
Mascot.prototype.handleAnimationEvents = function () {
|
||||
if (!this.logo) return
|
||||
if (!this.logo.webGLSupport) return
|
||||
// only setup listeners once
|
||||
if (this.animations) return
|
||||
this.animations = this.props.animationEventEmitter
|
||||
@ -55,7 +61,7 @@ Mascot.prototype.handleAnimationEvents = function () {
|
||||
}
|
||||
|
||||
Mascot.prototype.lookAt = function (target) {
|
||||
if (!this.logo) return
|
||||
if (!this.logo.webGLSupport) return
|
||||
this.unfollowMouse()
|
||||
this.logo.lookAt(target)
|
||||
this.refollowMouse()
|
||||
|
@ -23,7 +23,7 @@ Network.prototype.render = function () {
|
||||
|
||||
if (networkNumber === 'loading') {
|
||||
|
||||
return h('img', {
|
||||
return h('img.network-indicator', {
|
||||
title: 'Attempting to connect to blockchain.',
|
||||
onClick: (event) => this.props.onClick(event),
|
||||
style: {
|
||||
@ -36,9 +36,6 @@ Network.prototype.render = function () {
|
||||
} else if (providerName === 'mainnet') {
|
||||
hoverText = 'Main Ethereum Network'
|
||||
iconName = 'ethereum-network'
|
||||
} else if (providerName === 'classic') {
|
||||
hoverText = 'Ethereum Classic Network'
|
||||
iconName = 'classic-network'
|
||||
} else if (parseInt(networkNumber) === 2) {
|
||||
hoverText = 'Morden Test Network'
|
||||
iconName = 'morden-test-network'
|
||||
@ -64,16 +61,7 @@ Network.prototype.render = function () {
|
||||
style: {
|
||||
color: '#039396',
|
||||
}},
|
||||
'Etherum Main Net'),
|
||||
])
|
||||
case 'classic-network':
|
||||
return h('.network-indicator', [
|
||||
h('.menu-icon.hollow-diamond'),
|
||||
h('.network-name', {
|
||||
style: {
|
||||
color: '#039396',
|
||||
}},
|
||||
'Etherum Classic'),
|
||||
'Ethereum Main Net'),
|
||||
])
|
||||
case 'morden-test-network':
|
||||
return h('.network-indicator', [
|
||||
|
@ -3,7 +3,6 @@ const h = require('react-hyperscript')
|
||||
const inherits = require('util').inherits
|
||||
|
||||
const AccountPanel = require('./account-panel')
|
||||
const readableDate = require('../util').readableDate
|
||||
|
||||
module.exports = PendingMsgDetails
|
||||
|
||||
@ -24,6 +23,9 @@ PendingMsgDetails.prototype.render = function () {
|
||||
return (
|
||||
h('div', {
|
||||
key: msgData.id,
|
||||
style: {
|
||||
margin: '10px 20px',
|
||||
},
|
||||
}, [
|
||||
|
||||
// account that will sign
|
||||
@ -36,11 +38,6 @@ PendingMsgDetails.prototype.render = function () {
|
||||
|
||||
// message data
|
||||
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('label.font-small', 'MESSAGE'),
|
||||
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 TransactionIcon = require('./transaction-list-item-icon')
|
||||
|
||||
const ShiftListItem = require('./shift-list-item')
|
||||
module.exports = TransactionListItem
|
||||
|
||||
inherits(TransactionListItem, Component)
|
||||
@ -19,8 +19,10 @@ function TransactionListItem () {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
let isLinkable = false
|
||||
@ -42,7 +44,6 @@ TransactionListItem.prototype.render = function () {
|
||||
|
||||
return (
|
||||
h(`.transaction-list-item.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
|
||||
key: `tx-${transaction.id + i}`,
|
||||
onClick: (event) => {
|
||||
if (isPending) {
|
||||
this.props.showTx(transaction.id)
|
||||
|
@ -14,7 +14,11 @@ function TransactionList () {
|
||||
|
||||
TransactionList.prototype.render = function () {
|
||||
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)
|
||||
|
||||
return (
|
||||
@ -39,33 +43,46 @@ TransactionList.prototype.render = function () {
|
||||
paddingBottom: '4px',
|
||||
},
|
||||
}, [
|
||||
'Transactions',
|
||||
'History',
|
||||
]),
|
||||
|
||||
h('.tx-list', {
|
||||
style: {
|
||||
overflowY: 'auto',
|
||||
height: '305px',
|
||||
height: '300px',
|
||||
padding: '0 20px',
|
||||
textAlign: 'center',
|
||||
},
|
||||
}, (
|
||||
}, [
|
||||
|
||||
transactions.length
|
||||
? 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, {
|
||||
transaction, i, network,
|
||||
transaction, i, network, key,
|
||||
showTx: (txId) => {
|
||||
this.props.viewPendingTx(txId)
|
||||
},
|
||||
})
|
||||
})
|
||||
: [h('.flex-center', {
|
||||
: h('.flex-center', {
|
||||
style: {
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
}, 'No transaction history...')]
|
||||
)),
|
||||
}, [
|
||||
'No transaction history.',
|
||||
]),
|
||||
]),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('./actions')
|
||||
const txHelper = require('../lib/tx-helper')
|
||||
const isPopupOrNotification = require('../../app/scripts/lib/is-popup-or-notification')
|
||||
|
||||
const PendingTx = require('./components/pending-tx')
|
||||
const PendingMsg = require('./components/pending-msg')
|
||||
@ -35,7 +36,8 @@ ConfirmTxScreen.prototype.render = function () {
|
||||
var unconfMsgs = state.unconfMsgs
|
||||
var unconfTxList = txHelper(unconfTxs, unconfMsgs)
|
||||
var index = state.index !== undefined ? state.index : 0
|
||||
var txData = unconfTxList[index] || {}
|
||||
var txData = unconfTxList[index] || unconfTxList[0] || {}
|
||||
var isNotification = isPopupOrNotification() === 'notification'
|
||||
|
||||
return (
|
||||
|
||||
@ -43,9 +45,9 @@ ConfirmTxScreen.prototype.render = function () {
|
||||
|
||||
// subtitle and nav
|
||||
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),
|
||||
}),
|
||||
}) : null,
|
||||
h('h2.page-subtitle', 'Confirm Transaction'),
|
||||
]),
|
||||
|
||||
|
@ -3,7 +3,7 @@ const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('./actions')
|
||||
|
||||
const currencies = require('./conversion-util').availableCurrencies.rows
|
||||
module.exports = connect(mapStateToProps)(ConfigScreen)
|
||||
|
||||
function mapStateToProps (state) {
|
||||
@ -74,6 +74,8 @@ ConfigScreen.prototype.render = function () {
|
||||
}, 'Save'),
|
||||
]),
|
||||
h('hr.horizontal-line'),
|
||||
currentConversionInformation(metamaskState, state),
|
||||
h('hr.horizontal-line'),
|
||||
|
||||
h('div', {
|
||||
style: {
|
||||
@ -97,6 +99,27 @@ ConfigScreen.prototype.render = function () {
|
||||
)
|
||||
}
|
||||
|
||||
function currentConversionInformation (metamaskState, state) {
|
||||
var currentFiat = metamaskState.currentFiat
|
||||
var conversionDate = metamaskState.conversionDate
|
||||
return h('div', [
|
||||
h('span', {style: { fontWeight: 'bold', paddingRight: '10px'}}, 'Current Conversion'),
|
||||
h('span', {style: { fontWeight: 'bold', paddingRight: '10px', fontSize: '13px'}}, `Updated ${Date(conversionDate)}`),
|
||||
h('select#currentFiat', {
|
||||
onChange (event) {
|
||||
event.preventDefault()
|
||||
var element = document.getElementById('currentFiat')
|
||||
var newFiat = element.value
|
||||
state.dispatch(actions.setCurrentFiat(newFiat))
|
||||
},
|
||||
defaultValue: currentFiat,
|
||||
}, currencies.map((currency) => {
|
||||
return h('option', {key: currency.code, value: currency.code}, `${currency.code} - ${currency.name}`)
|
||||
})
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
function currentProviderDisplay (metamaskState) {
|
||||
var provider = metamaskState.provider
|
||||
var title, value
|
||||
|
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{
|
||||
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',
|
||||
},
|
||||
},
|
||||
`MetaMask is currently in beta -
|
||||
exercise caution while handling
|
||||
and storing your ether.
|
||||
`The MetaMask team would like to
|
||||
remind you that MetaMask is currently in beta - so
|
||||
don't store large
|
||||
amounts of ether in MetaMask.
|
||||
`),
|
||||
|
||||
h('i.fa.fa-exclamation-triangle.fa-4', {
|
||||
|
@ -73,9 +73,7 @@ InitializeMenuScreen.prototype.renderMenu = function () {
|
||||
margin: 12,
|
||||
},
|
||||
}, 'Restore Existing Vault'),
|
||||
|
||||
])
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
const inherits = require('util').inherits
|
||||
const Component = require('react').Component
|
||||
const PersistentForm = require('../../lib/persistent-form')
|
||||
const connect = require('react-redux').connect
|
||||
const h = require('react-hyperscript')
|
||||
const actions = require('../actions')
|
||||
|
||||
module.exports = connect(mapStateToProps)(RestoreVaultScreen)
|
||||
|
||||
inherits(RestoreVaultScreen, Component)
|
||||
inherits(RestoreVaultScreen, PersistentForm)
|
||||
function RestoreVaultScreen () {
|
||||
Component.call(this)
|
||||
PersistentForm.call(this)
|
||||
}
|
||||
|
||||
function mapStateToProps (state) {
|
||||
@ -19,6 +19,8 @@ function mapStateToProps (state) {
|
||||
|
||||
RestoreVaultScreen.prototype.render = function () {
|
||||
var state = this.props
|
||||
this.persistentFormParentId = 'restore-vault-form'
|
||||
|
||||
return (
|
||||
|
||||
h('.initialize-screen.flex-column.flex-center.flex-grow', [
|
||||
@ -39,6 +41,9 @@ RestoreVaultScreen.prototype.render = function () {
|
||||
// wallet seed entry
|
||||
h('h3', 'Wallet Seed'),
|
||||
h('textarea.twelve-word-phrase.letter-spacey', {
|
||||
dataset: {
|
||||
persistentFormId: 'wallet-seed',
|
||||
},
|
||||
placeholder: 'Enter your secret twelve word phrase here to restore your vault.',
|
||||
}),
|
||||
|
||||
@ -47,6 +52,9 @@ RestoreVaultScreen.prototype.render = function () {
|
||||
type: 'password',
|
||||
id: 'password-box',
|
||||
placeholder: 'New Password (min 8 chars)',
|
||||
dataset: {
|
||||
persistentFormId: 'password',
|
||||
},
|
||||
style: {
|
||||
width: 260,
|
||||
marginTop: 12,
|
||||
@ -59,6 +67,9 @@ RestoreVaultScreen.prototype.render = function () {
|
||||
id: 'password-box-confirm',
|
||||
placeholder: 'Confirm Password',
|
||||
onKeyPress: this.onMaybeCreate.bind(this),
|
||||
dataset: {
|
||||
persistentFormId: 'password-confirmation',
|
||||
},
|
||||
style: {
|
||||
width: 260,
|
||||
marginTop: 16,
|
||||
|
@ -1,6 +1,7 @@
|
||||
const extend = require('xtend')
|
||||
const actions = require('../actions')
|
||||
const txHelper = require('../../lib/tx-helper')
|
||||
const notification = require('../../../app/scripts/lib/notifications')
|
||||
|
||||
module.exports = reduceApp
|
||||
|
||||
@ -123,6 +124,7 @@ function reduceApp (state, action) {
|
||||
|
||||
case actions.UNLOCK_METAMASK:
|
||||
return extend(appState, {
|
||||
forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null,
|
||||
detailView: {},
|
||||
transForward: true,
|
||||
isLoading: false,
|
||||
@ -136,6 +138,25 @@ function reduceApp (state, action) {
|
||||
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
|
||||
|
||||
case actions.REVEAL_SEED_CONFIRMATION:
|
||||
@ -170,6 +191,7 @@ function reduceApp (state, action) {
|
||||
|
||||
case actions.SHOW_ACCOUNT_DETAIL:
|
||||
return extend(appState, {
|
||||
forgottenPassword: appState.forgottenPassword ? !appState.forgottenPassword : null,
|
||||
currentView: {
|
||||
name: 'accountDetail',
|
||||
context: action.value,
|
||||
@ -250,6 +272,9 @@ function reduceApp (state, action) {
|
||||
warning: null,
|
||||
})
|
||||
} else {
|
||||
|
||||
notification.closePopup()
|
||||
|
||||
return extend(appState, {
|
||||
transForward: false,
|
||||
warning: null,
|
||||
@ -317,6 +342,15 @@ function reduceApp (state, action) {
|
||||
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:
|
||||
return extend(appState, {
|
||||
transForward: true,
|
||||
@ -369,15 +403,116 @@ function reduceApp (state, action) {
|
||||
},
|
||||
})
|
||||
|
||||
case actions.SHOW_ETH_WARNING:
|
||||
case actions.BUY_ETH_VIEW:
|
||||
return extend(appState, {
|
||||
transForward: true,
|
||||
currentView: {
|
||||
name: 'accountDetail',
|
||||
name: 'buyEth',
|
||||
context: appState.currentView.context,
|
||||
},
|
||||
accountDetail: {
|
||||
subview: 'buy-eth-warning',
|
||||
buyView: {
|
||||
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:
|
||||
@ -405,4 +540,3 @@ function indexForPending (state, txId) {
|
||||
return idx
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,10 +10,14 @@ function reduceMetamask (state, action) {
|
||||
var metamaskState = extend({
|
||||
isInitialized: false,
|
||||
isUnlocked: false,
|
||||
isEthConfirmed: false,
|
||||
currentDomain: 'example.com',
|
||||
rpcTarget: 'https://rawtestrpc.metamask.io/',
|
||||
identities: {},
|
||||
unconfTxs: {},
|
||||
currentFiat: 'USD',
|
||||
conversionRate: 0,
|
||||
conversionDate: 'N/A',
|
||||
}, state.metamask)
|
||||
|
||||
switch (action.type) {
|
||||
@ -33,7 +37,7 @@ function reduceMetamask (state, action) {
|
||||
|
||||
case actions.AGREE_TO_ETH_WARNING:
|
||||
return extend(metamaskState, {
|
||||
isEthConfirmed: true,
|
||||
isEthConfirmed: !metamaskState.isEthConfirmed,
|
||||
})
|
||||
|
||||
case actions.UNLOCK_METAMASK:
|
||||
@ -114,6 +118,13 @@ function reduceMetamask (state, action) {
|
||||
var identities = extend(metamaskState.identities, id)
|
||||
return extend(metamaskState, { identities })
|
||||
|
||||
case actions.SET_CURRENT_FIAT:
|
||||
return extend(metamaskState, {
|
||||
currentFiat: action.value.currentFiat,
|
||||
conversionRate: action.value.conversionRate,
|
||||
conversionDate: action.value.conversionDate,
|
||||
})
|
||||
|
||||
default:
|
||||
return metamaskState
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
const inherits = require('util').inherits
|
||||
const Component = require('react').Component
|
||||
const PersistentForm = require('../lib/persistent-form')
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const Identicon = require('./components/identicon')
|
||||
@ -29,12 +29,14 @@ function mapStateToProps (state) {
|
||||
return result
|
||||
}
|
||||
|
||||
inherits(SendTransactionScreen, Component)
|
||||
inherits(SendTransactionScreen, PersistentForm)
|
||||
function SendTransactionScreen () {
|
||||
Component.call(this)
|
||||
PersistentForm.call(this)
|
||||
}
|
||||
|
||||
SendTransactionScreen.prototype.render = function () {
|
||||
this.persistentFormParentId = 'send-tx-form'
|
||||
|
||||
var state = this.props
|
||||
var address = state.address
|
||||
var account = state.account
|
||||
@ -137,6 +139,9 @@ SendTransactionScreen.prototype.render = function () {
|
||||
h('input.large-input', {
|
||||
name: 'address',
|
||||
placeholder: 'Recipient Address',
|
||||
dataset: {
|
||||
persistentFormId: 'recipient-address',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
|
||||
@ -150,6 +155,9 @@ SendTransactionScreen.prototype.render = function () {
|
||||
style: {
|
||||
marginRight: 6,
|
||||
},
|
||||
dataset: {
|
||||
persistentFormId: 'tx-amount',
|
||||
},
|
||||
}),
|
||||
|
||||
h('button.primary', {
|
||||
@ -185,11 +193,12 @@ SendTransactionScreen.prototype.render = function () {
|
||||
width: '100%',
|
||||
resize: 'none',
|
||||
},
|
||||
dataset: {
|
||||
persistentFormId: 'tx-data',
|
||||
},
|
||||
}),
|
||||
]),
|
||||
|
||||
])
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,20 @@
|
||||
const createStore = require('redux').createStore
|
||||
const applyMiddleware = require('redux').applyMiddleware
|
||||
const thunkMiddleware = require('redux-thunk')
|
||||
const createLogger = require('redux-logger')
|
||||
const rootReducer = require('./reducers')
|
||||
const createLogger = require('redux-logger')
|
||||
|
||||
global.METAMASK_DEBUG = false
|
||||
|
||||
module.exports = configureStore
|
||||
|
||||
const loggerMiddleware = createLogger()
|
||||
const loggerMiddleware = createLogger({
|
||||
predicate: () => global.METAMASK_DEBUG,
|
||||
})
|
||||
|
||||
const createStoreWithMiddleware = applyMiddleware(
|
||||
thunkMiddleware,
|
||||
loggerMiddleware
|
||||
)(createStore)
|
||||
const middlewares = [thunkMiddleware, loggerMiddleware]
|
||||
|
||||
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore)
|
||||
|
||||
function configureStore (initialState) {
|
||||
return createStoreWithMiddleware(rootReducer, initialState)
|
||||
|
@ -3,10 +3,11 @@ const Component = require('react').Component
|
||||
const h = require('react-hyperscript')
|
||||
const connect = require('react-redux').connect
|
||||
const actions = require('./actions')
|
||||
const Mascot = require('./components/mascot')
|
||||
const getCaretCoordinates = require('textarea-caret')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
|
||||
const Mascot = require('./components/mascot')
|
||||
|
||||
module.exports = connect(mapStateToProps)(UnlockScreen)
|
||||
|
||||
inherits(UnlockScreen, Component)
|
||||
@ -25,47 +26,46 @@ UnlockScreen.prototype.render = function () {
|
||||
const state = this.props
|
||||
const warning = state.warning
|
||||
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, {
|
||||
animationEventEmitter: this.animationEventEmitter,
|
||||
}),
|
||||
h('h1', {
|
||||
style: {
|
||||
fontSize: '1.4em',
|
||||
textTransform: 'uppercase',
|
||||
color: '#7F8082',
|
||||
},
|
||||
}, 'MetaMask'),
|
||||
|
||||
h('h1', {
|
||||
style: {
|
||||
fontSize: '1.4em',
|
||||
textTransform: 'uppercase',
|
||||
color: '#7F8082',
|
||||
},
|
||||
}, 'MetaMask'),
|
||||
h('input.large-input', {
|
||||
type: 'password',
|
||||
id: 'password-box',
|
||||
placeholder: 'enter password',
|
||||
style: {
|
||||
|
||||
h('input.large-input', {
|
||||
type: 'password',
|
||||
id: 'password-box',
|
||||
placeholder: 'enter password',
|
||||
style: {
|
||||
},
|
||||
onKeyPress: this.onKeyPress.bind(this),
|
||||
onInput: this.inputChanged.bind(this),
|
||||
}),
|
||||
|
||||
},
|
||||
onKeyPress: this.onKeyPress.bind(this),
|
||||
onInput: this.inputChanged.bind(this),
|
||||
}),
|
||||
|
||||
h('.error', {
|
||||
style: {
|
||||
display: warning ? 'block' : 'none',
|
||||
},
|
||||
}, warning),
|
||||
|
||||
h('button.primary.cursor-pointer', {
|
||||
onClick: this.onSubmit.bind(this),
|
||||
style: {
|
||||
margin: 10,
|
||||
},
|
||||
}, 'Unlock'),
|
||||
h('.error', {
|
||||
style: {
|
||||
display: warning ? 'block' : 'none',
|
||||
},
|
||||
}, warning),
|
||||
|
||||
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.
|
||||
// Its "formatted" property is what we generally use to render values.
|
||||
function formatBalance (balance, decimalsToKeep) {
|
||||
var parsed = parseBalance(balance)
|
||||
function formatBalance (balance, decimalsToKeep, needsParse = true) {
|
||||
var parsed = needsParse ? parseBalance(balance) : balance.split('.')
|
||||
var beforeDecimal = parsed[0]
|
||||
var afterDecimal = parsed[1]
|
||||
var formatted = 'None'
|
||||
@ -141,14 +141,21 @@ function shortenBalance (balance, decimalsToKeep = 1) {
|
||||
var convertedBalance = parseFloat(balance)
|
||||
if (convertedBalance > 1000000) {
|
||||
truncatedValue = (balance / 1000000).toFixed(decimalsToKeep)
|
||||
return `>${truncatedValue}m`
|
||||
return `${truncatedValue}m`
|
||||
} else if (convertedBalance > 1000) {
|
||||
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) {
|
||||
var exponent = balance.match(/\.0*/)[0].length
|
||||
truncatedValue = (convertedBalance * Math.pow(10, exponent)).toFixed(decimalsToKeep)
|
||||
return `<${truncatedValue}e-${exponent}`
|
||||
var stringBalance = convertedBalance.toString()
|
||||
if (stringBalance.split('.')[1].length > 3) {
|
||||
return convertedBalance.toFixed(3)
|
||||
} else {
|
||||
return stringBalance
|
||||
}
|
||||
} else {
|
||||
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