From 041b5493dc43c9f8b69dc5a1dde4b319638618a7 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 13 May 2016 01:13:14 -0700 Subject: Streamlined some transition logic Fixes #122 Had used multiple actions for some transitions, which would lead to brief intermediary states. Now making a few actions much more explicit about what they route to, so there is less intermediary logic, and we can transition confidently to the correct view. --- app/scripts/lib/idStore.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index b8d825d8b..6d3d0c0aa 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -105,14 +105,14 @@ IdentityStore.prototype.getSelectedAddress = function(){ return configManager.getSelectedAccount() } -IdentityStore.prototype.setSelectedAddress = function(address){ +IdentityStore.prototype.setSelectedAddress = function(address, cb){ if (!address) { var addresses = this._getAddresses() address = addresses[0] } configManager.setSelectedAccount(address) - this._didUpdate() + if (cb) return cb(null, address) } IdentityStore.prototype.getNetwork = function(tries) { -- cgit v1.2.3 From a703706cb115c17d072f277045a2ef7d838d9762 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 19 May 2016 16:53:16 -0700 Subject: sync rpc fix --- app/scripts/lib/config-manager.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 3c9326db9..847d85a20 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -1,11 +1,12 @@ const Migrator = require('pojo-migrator') const extend = require('xtend') +const MetamaskConfig = require('./config.js') +const migrations = require('./migrations') const STORAGE_KEY = 'metamask-config' -const TESTNET_RPC = 'https://morden.infura.io' -const MAINNET_RPC = 'https://mainnet.infura.io/' +const TESTNET_RPC = MetamaskConfig.network.testnet +const MAINNET_RPC = MetamaskConfig.network.mainnet -const migrations = require('./migrations') /* The config-manager is a convenience object * wrapping a pojo-migrator. -- cgit v1.2.3 From 21dd806b270ede40c848ef97fea27139d22597ca Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 19 May 2016 17:13:33 -0700 Subject: Corrected config path --- app/scripts/lib/config-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 847d85a20..7b2f2f1f8 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -1,6 +1,6 @@ const Migrator = require('pojo-migrator') const extend = require('xtend') -const MetamaskConfig = require('./config.js') +const MetamaskConfig = require('../config.js') const migrations = require('./migrations') const STORAGE_KEY = 'metamask-config' -- cgit v1.2.3 From 7d5aaaa5bd8a0f34694eb3e8ce5ba6bbecf03d71 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 May 2016 12:40:44 -0700 Subject: Add ability to add account to vault Scrolling to the bottom of the accounts page now reveals a downward-facing chevron button. Pressing this button shows loading indication, adds a new account to the identity vault, displays it in the list, and scrolls the list to the bottom of the page. Any number of accounts can be generated in this way, and the UX feels intuitive without having to overly explain how HD paths work. --- app/scripts/lib/idStore.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index 6d3d0c0aa..0604c4bca 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -115,6 +115,21 @@ IdentityStore.prototype.setSelectedAddress = function(address, cb){ if (cb) return cb(null, address) } +IdentityStore.prototype.revealAccount = function(cb) { + let addresses = this._getAddresses() + const derivedKey = this._idmgmt.derivedKey + const keyStore = this._keyStore + + keyStore.setDefaultHdDerivationPath(this.hdPathString) + keyStore.generateNewAddress(derivedKey, 1) + configManager.setWallet(keyStore.serialize()) + + addresses = this._getAddresses() + this._loadIdentities() + this._didUpdate() + cb(null) +} + IdentityStore.prototype.getNetwork = function(tries) { if (tries === 0) return this.web3.version.getNetwork((err, network) => { -- cgit v1.2.3 From 95a3cfe3fcffee2ffabd4cf71e568ae94693b10f Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 20 May 2016 16:18:54 -0700 Subject: Added ability to nickname wallets locally The changes are persisted to localstorage, so they cannot be restored on a new computer, but for right now it's a nice organizational feature. --- app/scripts/lib/config-manager.js | 20 ++++++++++++++++++++ app/scripts/lib/idStore.js | 12 ++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 7b2f2f1f8..f5e1cf38d 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -230,6 +230,26 @@ ConfigManager.prototype.updateTx = function(tx) { this._saveTxList(transactions) } +// wallet nickname methods + +ConfigManager.prototype.getWalletNicknames = function() { + var data = this.getData() + let nicknames = ('walletNicknames' in data) ? data.walletNicknames : {} + return nicknames +} + +ConfigManager.prototype.nicknameForWallet = function(account) { + let nicknames = this.getWalletNicknames() + return nicknames[account] +} + +ConfigManager.prototype.setNicknameForWallet = function(account, nickname) { + let nicknames = this.getWalletNicknames() + nicknames[account] = nickname + var data = this.getData() + data.walletNicknames = nicknames + this.setData(data) +} // observable diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index 0604c4bca..9d2552e8b 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -325,9 +325,10 @@ IdentityStore.prototype._loadIdentities = function(){ // // add to ethStore this._ethStore.addAccount(address) // add to identities + const defaultLabel = 'Wallet ' + (i+1) + const nickname = configManager.nicknameForWallet(address) var identity = { - name: 'Wallet ' + (i+1), - img: 'QmW6hcwYzXrNkuHrpvo58YeZvbZxUddv69ATSHY3BHpPdd', + name: nickname || defaultLabel, address: address, mayBeFauceting: this._mayBeFauceting(i), } @@ -336,6 +337,13 @@ IdentityStore.prototype._loadIdentities = function(){ this._didUpdate() } +IdentityStore.prototype.saveAccountLabel = function(account, label, cb) { + configManager.setNicknameForWallet(account, label) + this._loadIdentities() + cb(null, label) + this._didUpdate() +} + // mayBeFauceting // If on testnet, index 0 may be fauceting. // The UI will have to check the balance to know. -- cgit v1.2.3 From 00e9f3c6ae2d4092f0c9270d113d7e6dd47ddf0b Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 22 May 2016 15:23:16 -0700 Subject: inpage - refactor for modularity --- app/scripts/lib/auto-reload.js | 37 +++++++++++ app/scripts/lib/ensnare.js | 24 ++++++++ app/scripts/lib/inpage-provider.js | 123 +++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 app/scripts/lib/auto-reload.js create mode 100644 app/scripts/lib/ensnare.js create mode 100644 app/scripts/lib/inpage-provider.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js new file mode 100644 index 000000000..95a744b2c --- /dev/null +++ b/app/scripts/lib/auto-reload.js @@ -0,0 +1,37 @@ +const once = require('once') +const ensnare = require('./ensnare.js') + +module.exports = setupDappAutoReload + + +function setupDappAutoReload(web3, controlStream){ + + // export web3 as a global, checking for usage + var pageIsUsingWeb3 = false + var resetWasRequested = false + global.web3 = ensnare(web3, once(function(){ + // if web3 usage happened after a reset request, trigger reset late + if (resetWasRequested) return triggerReset() + // mark web3 as used + pageIsUsingWeb3 = true + // reset web3 reference + global.web3 = web3 + })) + + // listen for reset requests from metamask + controlStream.once('data', function(){ + resetWasRequested = true + // ignore if web3 was not used + if (!pageIsUsingWeb3) return + // reload after short timeout + triggerReset() + }) + + // reload the page + function triggerReset(){ + setTimeout(function(){ + global.location.reload() + }, 500) + } + +} \ No newline at end of file diff --git a/app/scripts/lib/ensnare.js b/app/scripts/lib/ensnare.js new file mode 100644 index 000000000..b70330a5a --- /dev/null +++ b/app/scripts/lib/ensnare.js @@ -0,0 +1,24 @@ +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(); return obj[key] = val }, + }) + return + } + }) + return proxy +} diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js new file mode 100644 index 000000000..66681c3a9 --- /dev/null +++ b/app/scripts/lib/inpage-provider.js @@ -0,0 +1,123 @@ +const HttpProvider = require('web3/lib/web3/httpprovider') +const Streams = require('mississippi') +const ObjectMultiplex = require('./obj-multiplex') +const StreamProvider = require('./stream-provider.js') +const RemoteStore = require('./remote-store.js').RemoteStore +const MetamaskConfig = require('../config.js') + +module.exports = MetamaskInpageProvider + + +function MetamaskInpageProvider(connectionStream){ + const self = this + + // setup connectionStream multiplexing + var multiStream = ObjectMultiplex() + Streams.pipe(connectionStream, multiStream, connectionStream, function(err){ + console.warn('MetamaskInpageProvider - lost connection to MetaMask') + if (err) throw err + }) + self.multiStream = multiStream + + // subscribe to metamask public config + var publicConfigStore = remoteStoreWithLocalStorageCache('MetaMask-Config') + var storeStream = publicConfigStore.createStream() + Streams.pipe(storeStream, multiStream.createStream('publicConfig'), storeStream, function(err){ + console.warn('MetamaskInpageProvider - lost connection to MetaMask publicConfig') + if (err) throw err + }) + 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){ + console.warn('MetamaskInpageProvider - lost connection to MetaMask provider') + if (err) throw err + }) + asyncProvider.on('error', console.error.bind(console)) + self.asyncProvider = asyncProvider + // overwrite own sendAsync method + self.sendAsync = asyncProvider.sendAsync.bind(asyncProvider) +} + +MetamaskInpageProvider.prototype.send = function(payload){ + const self = this + + var result = null + switch (payload.method) { + + case 'eth_accounts': + // read from localStorage + var selectedAddress = self.publicConfigStore.get('selectedAddress') + result = selectedAddress ? [selectedAddress] : [] + break + + case 'eth_coinbase': + // read from localStorage + var selectedAddress = self.publicConfigStore.get('selectedAddress') + result = selectedAddress || '0x0000000000000000000000000000000000000000' + break + + // fallback to normal rpc + default: + return self.syncProvider.send(payload) + + } + + // return the result + return { + id: payload.id, + jsonrpc: payload.jsonrpc, + result: result, + } +} + +MetamaskInpageProvider.prototype.sendAsync = function(){ + throw new Error('MetamaskInpageProvider - sendAsync not overwritten') +} + +MetamaskInpageProvider.prototype.isConnected = function(){ + return true +} + +// util + +function createSyncProvider(providerConfig){ + providerConfig = providerConfig || {} + var syncProviderUrl = undefined + + 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 + } + } + return new HttpProvider(syncProviderUrl) +} + +function remoteStoreWithLocalStorageCache(storageKey){ + // read local cache + var initState = JSON.parse(localStorage[storageKey] || '{}') + var store = new RemoteStore(initState) + // cache the latest state locally + store.subscribe(function(state){ + localStorage[storageKey] = JSON.stringify(state) + }) + + return store +} \ No newline at end of file -- cgit v1.2.3 From 2a62d63b4f70bea12126e4b15d78ebbf9dbddefb Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 22 May 2016 18:02:27 -0700 Subject: deps - use web3-stream-provider module --- app/scripts/lib/inpage-provider.js | 2 +- app/scripts/lib/stream-provider.js | 72 -------------------------------------- 2 files changed, 1 insertion(+), 73 deletions(-) delete mode 100644 app/scripts/lib/stream-provider.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 66681c3a9..70b0d80dd 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -1,7 +1,7 @@ const HttpProvider = require('web3/lib/web3/httpprovider') const Streams = require('mississippi') const ObjectMultiplex = require('./obj-multiplex') -const StreamProvider = require('./stream-provider.js') +const StreamProvider = require('web3-stream-provider') const RemoteStore = require('./remote-store.js').RemoteStore const MetamaskConfig = require('../config.js') diff --git a/app/scripts/lib/stream-provider.js b/app/scripts/lib/stream-provider.js deleted file mode 100644 index 505e45d1f..000000000 --- a/app/scripts/lib/stream-provider.js +++ /dev/null @@ -1,72 +0,0 @@ -const Duplex = require('readable-stream').Duplex -const inherits = require('util').inherits - -module.exports = StreamProvider - - -inherits(StreamProvider, Duplex) - -function StreamProvider(){ - Duplex.call(this, { - objectMode: true, - }) - - this._payloads = {} -} - -// public - -StreamProvider.prototype.send = function(payload){ - throw new Error('StreamProvider - does not support synchronous RPC calls. called: "'+payload.method+'"') -} - -StreamProvider.prototype.sendAsync = function(payload, callback){ - // console.log('StreamProvider - sending payload', payload) - var id = payload.id - if (Array.isArray(payload)) { - id = 'batch'+payload[0].id - } - this._payloads[id] = [payload, callback] - // console.log('payload for plugin:', payload) - this.push(payload) -} - -StreamProvider.prototype.isConnected = function(){ - return true -} - -// private - -StreamProvider.prototype._onResponse = function(response){ - // console.log('StreamProvider - got response', payload) - var id = response.id - if (Array.isArray(response)) { - id = 'batch'+response[0].id - } - var data = this._payloads[id] - if (!data) throw new Error('StreamProvider - Unknown response id') - delete this._payloads[id] - var payload = data[0] - var callback = data[1] - - // logging - var res = Array.isArray(response) ? response : [response] - // ;(Array.isArray(payload) ? payload : [payload]).forEach(function(payload, index){ - // console.log('plugin response:', payload.id, payload.method, payload.params, '->', res[index].result) - // }) - - callback(null, response) -} - -// stream plumbing - -StreamProvider.prototype._read = noop - -StreamProvider.prototype._write = function(msg, encoding, cb){ - this._onResponse(msg) - cb() -} - -// util - -function noop(){} \ No newline at end of file -- cgit v1.2.3