From 8012ede12698477692b80769781096b559159a32 Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Jan 2017 19:04:19 -0800 Subject: background - introduce ObservableStore --- app/scripts/lib/config-manager.js | 10 ++- app/scripts/lib/inpage-provider.js | 18 ++++-- app/scripts/lib/observable/host.js | 50 ++++++++++++++ app/scripts/lib/observable/index.js | 33 ++++++++++ app/scripts/lib/observable/remote.js | 51 +++++++++++++++ app/scripts/lib/observable/util/transform.js | 13 ++++ app/scripts/lib/remote-store.js | 97 ---------------------------- 7 files changed, 163 insertions(+), 109 deletions(-) create mode 100644 app/scripts/lib/observable/host.js create mode 100644 app/scripts/lib/observable/index.js create mode 100644 app/scripts/lib/observable/remote.js create mode 100644 app/scripts/lib/observable/util/transform.js delete mode 100644 app/scripts/lib/remote-store.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 3a1f12ac0..01e6ccc3c 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -19,6 +19,7 @@ module.exports = ConfigManager function ConfigManager (opts) { // ConfigManager is observable and will emit updates this._subs = [] + this.store = opts.store /* The migrator exported on the config-manager * has two methods the user should be concerned with: @@ -36,12 +37,9 @@ function ConfigManager (opts) { // config data format, and returns the new one. migrations: migrations, - // How to load initial config. - // Includes step on migrating pre-pojo-migrator data. - loadData: opts.loadData, - - // How to persist migrated config. - setData: opts.setData, + // Data persistence methods + loadData: () => this.store.get(), + setData: (value) => this.store.put(value), }) } diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 11bd5cc3a..64301be78 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -1,7 +1,7 @@ const Streams = require('mississippi') const StreamProvider = require('web3-stream-provider') const ObjectMultiplex = require('./obj-multiplex') -const RemoteStore = require('./remote-store.js').RemoteStore +const RemoteStore = require('./observable/remote') const createRandomId = require('./random-id') module.exports = MetamaskInpageProvider @@ -72,13 +72,13 @@ MetamaskInpageProvider.prototype.send = function (payload) { case 'eth_accounts': // read from localStorage - selectedAccount = self.publicConfigStore.get('selectedAccount') + selectedAccount = self.publicConfigStore.get().selectedAccount result = selectedAccount ? [selectedAccount] : [] break case 'eth_coinbase': // read from localStorage - selectedAccount = self.publicConfigStore.get('selectedAccount') + selectedAccount = self.publicConfigStore.get().selectedAccount result = selectedAccount || '0x0000000000000000000000000000000000000000' break @@ -117,9 +117,15 @@ MetamaskInpageProvider.prototype.isMetaMask = true function remoteStoreWithLocalStorageCache (storageKey) { // read local cache - var initState = JSON.parse(localStorage[storageKey] || '{}') - var store = new RemoteStore(initState) - // cache the latest state locally + let initState + try { + initState = JSON.parse(localStorage[storageKey] || '{}') + } catch (err) { + initState = {} + } + // intialize store + const store = new RemoteStore(initState) + // write local cache store.subscribe(function (state) { localStorage[storageKey] = JSON.stringify(state) }) diff --git a/app/scripts/lib/observable/host.js b/app/scripts/lib/observable/host.js new file mode 100644 index 000000000..69f674be8 --- /dev/null +++ b/app/scripts/lib/observable/host.js @@ -0,0 +1,50 @@ +const Dnode = require('dnode') +const ObservableStore = require('./index') +const endOfStream = require('end-of-stream') + +// +// HostStore +// +// plays host to many RemoteStores and sends its state over a stream +// + +class HostStore extends ObservableStore { + + constructor (initState, opts) { + super(initState) + this.opts = opts || {} + } + + createStream () { + const self = this + // setup remotely exposed api + let remoteApi = {} + if (!self.opts.readOnly) { + remoteApi.put = (newState) => self.put(newState) + } + // listen for connection to remote + const dnode = Dnode(remoteApi) + dnode.on('remote', (remote) => { + // setup update subscription lifecycle + const updateHandler = (state) => remote.put(state) + self._onConnect(updateHandler) + endOfStream(dnode, () => self._onDisconnect(updateHandler)) + }) + return dnode + } + + _onConnect (updateHandler) { + // subscribe to updates + this.subscribe(updateHandler) + // send state immediately + updateHandler(this.get()) + } + + _onDisconnect (updateHandler) { + // unsubscribe to updates + this.unsubscribe(updateHandler) + } + +} + +module.exports = HostStore diff --git a/app/scripts/lib/observable/index.js b/app/scripts/lib/observable/index.js new file mode 100644 index 000000000..d193e5554 --- /dev/null +++ b/app/scripts/lib/observable/index.js @@ -0,0 +1,33 @@ +const EventEmitter = require('events').EventEmitter + +class ObservableStore extends EventEmitter { + + constructor (initialState) { + super() + this._state = initialState + } + + get () { + return this._state + } + + put (newState) { + this._put(newState) + } + + subscribe (handler) { + this.on('update', handler) + } + + unsubscribe (handler) { + this.removeListener('update', handler) + } + + _put (newState) { + this._state = newState + this.emit('update', newState) + } + +} + +module.exports = ObservableStore diff --git a/app/scripts/lib/observable/remote.js b/app/scripts/lib/observable/remote.js new file mode 100644 index 000000000..b5a3254a2 --- /dev/null +++ b/app/scripts/lib/observable/remote.js @@ -0,0 +1,51 @@ +const Dnode = require('dnode') +const ObservableStore = require('./index') +const endOfStream = require('end-of-stream') + +// +// RemoteStore +// +// connects to a HostStore and receives its latest state +// + +class RemoteStore extends ObservableStore { + + constructor (initState, opts) { + super(initState) + this.opts = opts || {} + this._remote = null + } + + put (newState) { + if (!this._remote) throw new Error('RemoteStore - "put" called before connection to HostStore') + this._put(newState) + this._remote.put(newState) + } + + createStream () { + const self = this + const dnode = Dnode({ + put: (newState) => self._put(newState), + }) + // listen for connection to remote + dnode.once('remote', (remote) => { + // setup connection lifecycle + self._onConnect(remote) + endOfStream(dnode, () => self._onDisconnect()) + }) + return dnode + } + + _onConnect (remote) { + this._remote = remote + this.emit('connected') + } + + _onDisconnect () { + this._remote = null + this.emit('disconnected') + } + +} + +module.exports = RemoteStore \ No newline at end of file diff --git a/app/scripts/lib/observable/util/transform.js b/app/scripts/lib/observable/util/transform.js new file mode 100644 index 000000000..87946f402 --- /dev/null +++ b/app/scripts/lib/observable/util/transform.js @@ -0,0 +1,13 @@ + +module.exports = transformStore + + +function transformStore(inStore, outStore, stateTransform) { + const initState = stateTransform(inStore.get()) + outStore.put(initState) + inStore.subscribe((inState) => { + const outState = stateTransform(inState) + outStore.put(outState) + }) + return outStore +} \ No newline at end of file diff --git a/app/scripts/lib/remote-store.js b/app/scripts/lib/remote-store.js deleted file mode 100644 index fbfab7bad..000000000 --- a/app/scripts/lib/remote-store.js +++ /dev/null @@ -1,97 +0,0 @@ -const Dnode = require('dnode') -const inherits = require('util').inherits - -module.exports = { - HostStore: HostStore, - RemoteStore: RemoteStore, -} - -function BaseStore (initState) { - this._state = initState || {} - this._subs = [] -} - -BaseStore.prototype.set = function (key, value) { - throw Error('Not implemented.') -} - -BaseStore.prototype.get = function (key) { - return this._state[key] -} - -BaseStore.prototype.subscribe = function (fn) { - this._subs.push(fn) - var unsubscribe = this.unsubscribe.bind(this, fn) - return unsubscribe -} - -BaseStore.prototype.unsubscribe = function (fn) { - var index = this._subs.indexOf(fn) - if (index !== -1) this._subs.splice(index, 1) -} - -BaseStore.prototype._emitUpdates = function (state) { - this._subs.forEach(function (handler) { - handler(state) - }) -} - -// -// host -// - -inherits(HostStore, BaseStore) -function HostStore (initState, opts) { - BaseStore.call(this, initState) -} - -HostStore.prototype.set = function (key, value) { - this._state[key] = value - process.nextTick(this._emitUpdates.bind(this, this._state)) -} - -HostStore.prototype.createStream = function () { - var dnode = Dnode({ - // update: this._didUpdate.bind(this), - }) - dnode.on('remote', this._didConnect.bind(this)) - return dnode -} - -HostStore.prototype._didConnect = function (remote) { - this.subscribe(function (state) { - remote.update(state) - }) - remote.update(this._state) -} - -// -// remote -// - -inherits(RemoteStore, BaseStore) -function RemoteStore (initState, opts) { - BaseStore.call(this, initState) - this._remote = null -} - -RemoteStore.prototype.set = function (key, value) { - this._remote.set(key, value) -} - -RemoteStore.prototype.createStream = function () { - var dnode = Dnode({ - update: this._didUpdate.bind(this), - }) - dnode.once('remote', this._didConnect.bind(this)) - return dnode -} - -RemoteStore.prototype._didConnect = function (remote) { - this._remote = remote -} - -RemoteStore.prototype._didUpdate = function (state) { - this._state = state - this._emitUpdates(state) -} -- cgit From 3bc996878b467e1fa5fd63656bd465377daa137d Mon Sep 17 00:00:00 2001 From: kumavis Date: Wed, 11 Jan 2017 22:47:56 -0800 Subject: background - move pojo migrator to outside of metamask controller --- app/scripts/lib/config-manager.js | 49 ++++++++-------------------- app/scripts/lib/migrations.js | 13 ++++++++ app/scripts/lib/observable/host.js | 4 +-- app/scripts/lib/observable/index.js | 10 +++++- app/scripts/lib/observable/local-storage.js | 37 +++++++++++++++++++++ app/scripts/lib/observable/remote.js | 2 +- app/scripts/lib/observable/util/sync.js | 24 ++++++++++++++ app/scripts/lib/observable/util/transform.js | 13 -------- 8 files changed, 99 insertions(+), 53 deletions(-) create mode 100644 app/scripts/lib/observable/local-storage.js create mode 100644 app/scripts/lib/observable/util/sync.js delete mode 100644 app/scripts/lib/observable/util/transform.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 01e6ccc3c..6d7305377 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -1,6 +1,4 @@ -const Migrator = require('pojo-migrator') const MetamaskConfig = require('../config.js') -const migrations = require('./migrations') const ethUtil = require('ethereumjs-util') const normalize = require('./sig-util').normalize @@ -20,38 +18,17 @@ function ConfigManager (opts) { // ConfigManager is observable and will emit updates this._subs = [] this.store = opts.store - - /* The migrator exported on the config-manager - * has two methods the user should be concerned with: - * - * getData(), which returns the app-consumable data object - * saveData(), which persists the app-consumable data object. - */ - this.migrator = new Migrator({ - - // Migrations must start at version 1 or later. - // They are objects with a `version` number - // and a `migrate` function. - // - // The `migrate` function receives the previous - // config data format, and returns the new one. - migrations: migrations, - - // Data persistence methods - loadData: () => this.store.get(), - setData: (value) => this.store.put(value), - }) } ConfigManager.prototype.setConfig = function (config) { - var data = this.migrator.getData() + var data = this.store.get() data.config = config this.setData(data) this._emitUpdates(config) } ConfigManager.prototype.getConfig = function () { - var data = this.migrator.getData() + var data = this.store.get() if ('config' in data) { return data.config } else { @@ -94,15 +71,15 @@ ConfigManager.prototype.getProvider = function () { } ConfigManager.prototype.setData = function (data) { - this.migrator.saveData(data) + this.store.put(data) } ConfigManager.prototype.getData = function () { - return this.migrator.getData() + return this.store.get() } ConfigManager.prototype.setWallet = function (wallet) { - var data = this.migrator.getData() + var data = this.store.get() data.wallet = wallet this.setData(data) } @@ -119,11 +96,11 @@ ConfigManager.prototype.getVault = function () { } ConfigManager.prototype.getKeychains = function () { - return this.migrator.getData().keychains || [] + return this.store.get().keychains || [] } ConfigManager.prototype.setKeychains = function (keychains) { - var data = this.migrator.getData() + var data = this.store.get() data.keychains = keychains this.setData(data) } @@ -140,19 +117,19 @@ ConfigManager.prototype.setSelectedAccount = function (address) { } ConfigManager.prototype.getWallet = function () { - return this.migrator.getData().wallet + return this.store.get().wallet } // Takes a boolean ConfigManager.prototype.setShowSeedWords = function (should) { - var data = this.migrator.getData() + var data = this.store.get() data.showSeedWords = should this.setData(data) } ConfigManager.prototype.getShouldShowSeedWords = function () { - var data = this.migrator.getData() + var data = this.store.get() return data.showSeedWords } @@ -187,7 +164,7 @@ ConfigManager.prototype.getCurrentRpcAddress = function () { } ConfigManager.prototype.setData = function (data) { - this.migrator.saveData(data) + this.store.put(data) } // @@ -195,7 +172,7 @@ ConfigManager.prototype.setData = function (data) { // ConfigManager.prototype.getTxList = function () { - var data = this.migrator.getData() + var data = this.store.get() if (data.transactions !== undefined) { return data.transactions } else { @@ -204,7 +181,7 @@ ConfigManager.prototype.getTxList = function () { } ConfigManager.prototype.setTxList = function (txList) { - var data = this.migrator.getData() + var data = this.store.get() data.transactions = txList this.setData(data) } diff --git a/app/scripts/lib/migrations.js b/app/scripts/lib/migrations.js index f026cbe53..12f60def1 100644 --- a/app/scripts/lib/migrations.js +++ b/app/scripts/lib/migrations.js @@ -1,3 +1,16 @@ +/* The migrator has two methods the user should be concerned with: + * + * getData(), which returns the app-consumable data object + * saveData(), which persists the app-consumable data object. + */ + +// Migrations must start at version 1 or later. +// They are objects with a `version` number +// and a `migrate` function. +// +// The `migrate` function receives the previous +// config data format, and returns the new one. + module.exports = [ require('../migrations/002'), require('../migrations/003'), diff --git a/app/scripts/lib/observable/host.js b/app/scripts/lib/observable/host.js index 69f674be8..d1b110503 100644 --- a/app/scripts/lib/observable/host.js +++ b/app/scripts/lib/observable/host.js @@ -12,14 +12,14 @@ class HostStore extends ObservableStore { constructor (initState, opts) { super(initState) - this.opts = opts || {} + this._opts = opts || {} } createStream () { const self = this // setup remotely exposed api let remoteApi = {} - if (!self.opts.readOnly) { + if (!self._opts.readOnly) { remoteApi.put = (newState) => self.put(newState) } // listen for connection to remote diff --git a/app/scripts/lib/observable/index.js b/app/scripts/lib/observable/index.js index d193e5554..1ff112e95 100644 --- a/app/scripts/lib/observable/index.js +++ b/app/scripts/lib/observable/index.js @@ -7,22 +7,30 @@ class ObservableStore extends EventEmitter { this._state = initialState } + // wrapper around internal get get () { return this._state } - + + // wrapper around internal put put (newState) { this._put(newState) } + // subscribe to changes subscribe (handler) { this.on('update', handler) } + // unsubscribe to changes unsubscribe (handler) { this.removeListener('update', handler) } + // + // private + // + _put (newState) { this._state = newState this.emit('update', newState) diff --git a/app/scripts/lib/observable/local-storage.js b/app/scripts/lib/observable/local-storage.js new file mode 100644 index 000000000..6ed3860f6 --- /dev/null +++ b/app/scripts/lib/observable/local-storage.js @@ -0,0 +1,37 @@ +const ObservableStore = require('./index') + +// +// LocalStorageStore +// +// uses localStorage instead of a cache +// + +class LocalStorageStore extends ObservableStore { + + constructor (opts) { + super() + delete this._state + + this._opts = opts || {} + if (!this._opts.storageKey) { + throw new Error('LocalStorageStore - no "storageKey" specified') + } + this._storageKey = this._opts.storageKey + } + + get() { + try { + return JSON.parse(global.localStorage[this._storageKey]) + } catch (err) { + return undefined + } + } + + _put(newState) { + global.localStorage[this._storageKey] = JSON.stringify(newState) + this.emit('update', newState) + } + +} + +module.exports = LocalStorageStore diff --git a/app/scripts/lib/observable/remote.js b/app/scripts/lib/observable/remote.js index b5a3254a2..603f6f0b8 100644 --- a/app/scripts/lib/observable/remote.js +++ b/app/scripts/lib/observable/remote.js @@ -12,7 +12,7 @@ class RemoteStore extends ObservableStore { constructor (initState, opts) { super(initState) - this.opts = opts || {} + this._opts = opts || {} this._remote = null } diff --git a/app/scripts/lib/observable/util/sync.js b/app/scripts/lib/observable/util/sync.js new file mode 100644 index 000000000..c61feb02e --- /dev/null +++ b/app/scripts/lib/observable/util/sync.js @@ -0,0 +1,24 @@ + +// +// synchronizeStore(inStore, outStore, stateTransform) +// +// keeps outStore synchronized with inStore, via an optional stateTransform +// + +module.exports = synchronizeStore + + +function synchronizeStore(inStore, outStore, stateTransform) { + stateTransform = stateTransform || transformNoop + const initState = stateTransform(inStore.get()) + outStore.put(initState) + inStore.subscribe((inState) => { + const outState = stateTransform(inState) + outStore.put(outState) + }) + return outStore +} + +function transformNoop(state) { + return state +} \ No newline at end of file diff --git a/app/scripts/lib/observable/util/transform.js b/app/scripts/lib/observable/util/transform.js deleted file mode 100644 index 87946f402..000000000 --- a/app/scripts/lib/observable/util/transform.js +++ /dev/null @@ -1,13 +0,0 @@ - -module.exports = transformStore - - -function transformStore(inStore, outStore, stateTransform) { - const initState = stateTransform(inStore.get()) - outStore.put(initState) - inStore.subscribe((inState) => { - const outState = stateTransform(inState) - outStore.put(outState) - }) - return outStore -} \ No newline at end of file -- cgit From b33c51c0a6c7c8a7b0c0a9a6ca101f874f2db3d1 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 12 Jan 2017 01:17:05 -0800 Subject: migrations - introduce promise-based migrator --- app/scripts/lib/migrator/index.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/scripts/lib/migrator/index.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js new file mode 100644 index 000000000..02d8c2335 --- /dev/null +++ b/app/scripts/lib/migrator/index.js @@ -0,0 +1,31 @@ +const asyncQ = require('async-q') + +class Migrator { + + constructor (opts = {}) { + let migrations = opts.migrations || [] + this.migrations = migrations.sort((a, b) => a.version - b.version) + let lastMigration = this.migrations.slice(-1)[0] + // use specified defaultVersion or highest migration version + this.defaultVersion = opts.defaultVersion || lastMigration && lastMigration.version || 0 + } + + // run all pending migrations on meta in place + migrateData (meta = { version: this.defaultVersion }) { + let remaining = this.migrations.filter(migrationIsPending) + + return ( + asyncQ.eachSeries(remaining, (migration) => migration.migrate(meta)) + .then(() => meta) + ) + + // migration is "pending" if hit has a higher + // version number than currentVersion + function migrationIsPending(migration) { + return migration.version > meta.version + } + } + +} + +module.exports = Migrator -- cgit From 80514d73b5bc6887cea877194091c941cfb9a8e6 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 12 Jan 2017 02:24:33 -0800 Subject: migrations - wip - 005 multivault migration --- app/scripts/lib/migrations.js | 18 ------------------ app/scripts/lib/migrator/index.js | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 23 deletions(-) delete mode 100644 app/scripts/lib/migrations.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/migrations.js b/app/scripts/lib/migrations.js deleted file mode 100644 index 12f60def1..000000000 --- a/app/scripts/lib/migrations.js +++ /dev/null @@ -1,18 +0,0 @@ -/* The migrator has two methods the user should be concerned with: - * - * getData(), which returns the app-consumable data object - * saveData(), which persists the app-consumable data object. - */ - -// Migrations must start at version 1 or later. -// They are objects with a `version` number -// and a `migrate` function. -// -// The `migrate` function receives the previous -// config data format, and returns the new one. - -module.exports = [ - require('../migrations/002'), - require('../migrations/003'), - require('../migrations/004'), -] diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js index 02d8c2335..ab5a757b3 100644 --- a/app/scripts/lib/migrator/index.js +++ b/app/scripts/lib/migrator/index.js @@ -7,22 +7,31 @@ class Migrator { this.migrations = migrations.sort((a, b) => a.version - b.version) let lastMigration = this.migrations.slice(-1)[0] // use specified defaultVersion or highest migration version - this.defaultVersion = opts.defaultVersion || lastMigration && lastMigration.version || 0 + this.defaultVersion = opts.defaultVersion || (lastMigration && lastMigration.version) || 0 } // run all pending migrations on meta in place - migrateData (meta = { version: this.defaultVersion }) { + migrateData (versionedData = this.generateInitialState()) { let remaining = this.migrations.filter(migrationIsPending) return ( - asyncQ.eachSeries(remaining, (migration) => migration.migrate(meta)) - .then(() => meta) + asyncQ.eachSeries(remaining, (migration) => migration.migrate(versionedData)) + .then(() => versionedData) ) // migration is "pending" if hit has a higher // version number than currentVersion function migrationIsPending(migration) { - return migration.version > meta.version + return migration.version > versionedData.meta.version + } + } + + generateInitialState (initState) { + return { + meta: { + version: this.defaultVersion, + }, + data: initState, } } -- cgit From 76ce348a04b83693eda0e8a40f9888c1f5fe7ef5 Mon Sep 17 00:00:00 2001 From: kumavis Date: Tue, 24 Jan 2017 19:47:00 -0800 Subject: obs-store - use published module --- app/scripts/lib/config-manager.js | 44 ++++++++-------- app/scripts/lib/inpage-provider.js | 79 ++++++++++++----------------- app/scripts/lib/observable/host.js | 50 ------------------ app/scripts/lib/observable/index.js | 41 --------------- app/scripts/lib/observable/local-storage.js | 37 -------------- app/scripts/lib/observable/remote.js | 51 ------------------- app/scripts/lib/observable/util/sync.js | 24 --------- 7 files changed, 52 insertions(+), 274 deletions(-) delete mode 100644 app/scripts/lib/observable/host.js delete mode 100644 app/scripts/lib/observable/index.js delete mode 100644 app/scripts/lib/observable/local-storage.js delete mode 100644 app/scripts/lib/observable/remote.js delete mode 100644 app/scripts/lib/observable/util/sync.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index daba8bc7b..6d088906c 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -21,14 +21,14 @@ function ConfigManager (opts) { } ConfigManager.prototype.setConfig = function (config) { - var data = this.store.get() + var data = this.getData() data.config = config this.setData(data) this._emitUpdates(config) } ConfigManager.prototype.getConfig = function () { - var data = this.store.get() + var data = this.getData() if ('config' in data) { return data.config } else { @@ -71,15 +71,15 @@ ConfigManager.prototype.getProvider = function () { } ConfigManager.prototype.setData = function (data) { - this.store.put(data) + this.store.putState(data) } ConfigManager.prototype.getData = function () { - return this.store.get() + return this.store.getState() } ConfigManager.prototype.setWallet = function (wallet) { - var data = this.store.get() + var data = this.getData() data.wallet = wallet this.setData(data) } @@ -96,11 +96,11 @@ ConfigManager.prototype.getVault = function () { } ConfigManager.prototype.getKeychains = function () { - return this.store.get().keychains || [] + return this.getData().keychains || [] } ConfigManager.prototype.setKeychains = function (keychains) { - var data = this.store.get() + var data = this.getData() data.keychains = keychains this.setData(data) } @@ -117,19 +117,19 @@ ConfigManager.prototype.setSelectedAccount = function (address) { } ConfigManager.prototype.getWallet = function () { - return this.store.get().wallet + return this.getData().wallet } // Takes a boolean ConfigManager.prototype.setShowSeedWords = function (should) { - var data = this.store.get() + var data = this.getData() data.showSeedWords = should this.setData(data) } ConfigManager.prototype.getShouldShowSeedWords = function () { - var data = this.store.get() + var data = this.getData() return data.showSeedWords } @@ -141,7 +141,7 @@ ConfigManager.prototype.setSeedWords = function (words) { ConfigManager.prototype.getSeedWords = function () { var data = this.getData() - return ('seedWords' in data) && data.seedWords + return data.seedWords } ConfigManager.prototype.getCurrentRpcAddress = function () { @@ -163,16 +163,12 @@ ConfigManager.prototype.getCurrentRpcAddress = function () { } } -ConfigManager.prototype.setData = function (data) { - this.store.put(data) -} - // // Tx // ConfigManager.prototype.getTxList = function () { - var data = this.store.get() + var data = this.getData() if (data.transactions !== undefined) { return data.transactions } else { @@ -181,7 +177,7 @@ ConfigManager.prototype.getTxList = function () { } ConfigManager.prototype.setTxList = function (txList) { - var data = this.store.get() + var data = this.getData() data.transactions = txList this.setData(data) } @@ -214,7 +210,7 @@ ConfigManager.prototype.setNicknameForWallet = function (account, nickname) { ConfigManager.prototype.getSalt = function () { var data = this.getData() - return ('salt' in data) && data.salt + return data.salt } ConfigManager.prototype.setSalt = function (salt) { @@ -248,7 +244,7 @@ ConfigManager.prototype.setConfirmedDisclaimer = function (confirmed) { ConfigManager.prototype.getConfirmedDisclaimer = function () { var data = this.getData() - return ('isDisclaimerConfirmed' in data) && data.isDisclaimerConfirmed + return data.isDisclaimerConfirmed } ConfigManager.prototype.setTOSHash = function (hash) { @@ -259,7 +255,7 @@ ConfigManager.prototype.setTOSHash = function (hash) { ConfigManager.prototype.getTOSHash = function () { var data = this.getData() - return ('TOSHash' in data) && data.TOSHash + return data.TOSHash } ConfigManager.prototype.setCurrentFiat = function (currency) { @@ -270,7 +266,7 @@ ConfigManager.prototype.setCurrentFiat = function (currency) { ConfigManager.prototype.getCurrentFiat = function () { var data = this.getData() - return ('fiatCurrency' in data) && data.fiatCurrency + return data.fiatCurrency } ConfigManager.prototype.updateConversionRate = function () { @@ -301,12 +297,12 @@ ConfigManager.prototype.setConversionDate = function (datestring) { ConfigManager.prototype.getConversionRate = function () { var data = this.getData() - return (('conversionRate' in data) && data.conversionRate) || 0 + return (data.conversionRate) || 0 } ConfigManager.prototype.getConversionDate = function () { var data = this.getData() - return (('conversionDate' in data) && data.conversionDate) || 'N/A' + return (data.conversionDate) || 'N/A' } ConfigManager.prototype.getShapeShiftTxList = function () { @@ -345,7 +341,7 @@ ConfigManager.prototype.createShapeShiftTx = function (depositAddress, depositTy ConfigManager.prototype.getGasMultiplier = function () { var data = this.getData() - return ('gasMultiplier' in data) && data.gasMultiplier + return data.gasMultiplier } ConfigManager.prototype.setGasMultiplier = function (gasMultiplier) { diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 64301be78..066916b4d 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -1,7 +1,7 @@ -const Streams = require('mississippi') +const pipe = require('pump') const StreamProvider = require('web3-stream-provider') +const LocalStorageStore = require('obs-store') const ObjectMultiplex = require('./obj-multiplex') -const RemoteStore = require('./observable/remote') const createRandomId = require('./random-id') module.exports = MetamaskInpageProvider @@ -10,33 +10,30 @@ function MetamaskInpageProvider (connectionStream) { const self = this // setup connectionStream multiplexing - var multiStream = ObjectMultiplex() - Streams.pipe(connectionStream, multiStream, connectionStream, function (err) { - let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask' - if (err) warningMsg += '\n' + err.stack - console.warn(warningMsg) - }) - 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) { - let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask publicConfig' - if (err) warningMsg += '\n' + err.stack - console.warn(warningMsg) - }) - self.publicConfigStore = publicConfigStore + var multiStream = self.multiStream = ObjectMultiplex() + pipe( + connectionStream, + multiStream, + connectionStream, + (err) => logStreamDisconnectWarning('MetaMask', err) + ) + + // subscribe to metamask public config (one-way) + self.publicConfigStore = new LocalStorageStore({ storageKey: 'MetaMask-Config' }) + pipe( + multiStream.createStream('publicConfig'), + self.publicConfigStore, + (err) => logStreamDisconnectWarning('MetaMask PublicConfigStore', err) + ) // connect to async provider - var asyncProvider = new StreamProvider() - Streams.pipe(asyncProvider, multiStream.createStream('provider'), asyncProvider, function (err) { - let warningMsg = 'MetamaskInpageProvider - lost connection to MetaMask provider' - if (err) warningMsg += '\n' + err.stack - console.warn(warningMsg) - }) - asyncProvider.on('error', console.error.bind(console)) - self.asyncProvider = asyncProvider + const asyncProvider = self.asyncProvider = new StreamProvider() + pipe( + asyncProvider, + multiStream.createStream('provider'), + asyncProvider, + (err) => logStreamDisconnectWarning('MetaMask RpcProvider', err) + ) self.idMap = {} // handle sendAsync requests via asyncProvider @@ -72,13 +69,13 @@ MetamaskInpageProvider.prototype.send = function (payload) { case 'eth_accounts': // read from localStorage - selectedAccount = self.publicConfigStore.get().selectedAccount + selectedAccount = self.publicConfigStore.getState().selectedAccount result = selectedAccount ? [selectedAccount] : [] break case 'eth_coinbase': // read from localStorage - selectedAccount = self.publicConfigStore.get().selectedAccount + selectedAccount = self.publicConfigStore.getState().selectedAccount result = selectedAccount || '0x0000000000000000000000000000000000000000' break @@ -115,24 +112,6 @@ MetamaskInpageProvider.prototype.isMetaMask = true // util -function remoteStoreWithLocalStorageCache (storageKey) { - // read local cache - let initState - try { - initState = JSON.parse(localStorage[storageKey] || '{}') - } catch (err) { - initState = {} - } - // intialize store - const store = new RemoteStore(initState) - // write local cache - store.subscribe(function (state) { - localStorage[storageKey] = JSON.stringify(state) - }) - - return store -} - function eachJsonMessage (payload, transformFn) { if (Array.isArray(payload)) { return payload.map(transformFn) @@ -141,4 +120,10 @@ function eachJsonMessage (payload, transformFn) { } } +function logStreamDisconnectWarning(remoteLabel, err){ + let warningMsg = `MetamaskInpageProvider - lost connection to ${remoteLabel}` + if (err) warningMsg += '\n' + err.stack + console.warn(warningMsg) +} + function noop () {} diff --git a/app/scripts/lib/observable/host.js b/app/scripts/lib/observable/host.js deleted file mode 100644 index d1b110503..000000000 --- a/app/scripts/lib/observable/host.js +++ /dev/null @@ -1,50 +0,0 @@ -const Dnode = require('dnode') -const ObservableStore = require('./index') -const endOfStream = require('end-of-stream') - -// -// HostStore -// -// plays host to many RemoteStores and sends its state over a stream -// - -class HostStore extends ObservableStore { - - constructor (initState, opts) { - super(initState) - this._opts = opts || {} - } - - createStream () { - const self = this - // setup remotely exposed api - let remoteApi = {} - if (!self._opts.readOnly) { - remoteApi.put = (newState) => self.put(newState) - } - // listen for connection to remote - const dnode = Dnode(remoteApi) - dnode.on('remote', (remote) => { - // setup update subscription lifecycle - const updateHandler = (state) => remote.put(state) - self._onConnect(updateHandler) - endOfStream(dnode, () => self._onDisconnect(updateHandler)) - }) - return dnode - } - - _onConnect (updateHandler) { - // subscribe to updates - this.subscribe(updateHandler) - // send state immediately - updateHandler(this.get()) - } - - _onDisconnect (updateHandler) { - // unsubscribe to updates - this.unsubscribe(updateHandler) - } - -} - -module.exports = HostStore diff --git a/app/scripts/lib/observable/index.js b/app/scripts/lib/observable/index.js deleted file mode 100644 index 1ff112e95..000000000 --- a/app/scripts/lib/observable/index.js +++ /dev/null @@ -1,41 +0,0 @@ -const EventEmitter = require('events').EventEmitter - -class ObservableStore extends EventEmitter { - - constructor (initialState) { - super() - this._state = initialState - } - - // wrapper around internal get - get () { - return this._state - } - - // wrapper around internal put - put (newState) { - this._put(newState) - } - - // subscribe to changes - subscribe (handler) { - this.on('update', handler) - } - - // unsubscribe to changes - unsubscribe (handler) { - this.removeListener('update', handler) - } - - // - // private - // - - _put (newState) { - this._state = newState - this.emit('update', newState) - } - -} - -module.exports = ObservableStore diff --git a/app/scripts/lib/observable/local-storage.js b/app/scripts/lib/observable/local-storage.js deleted file mode 100644 index 6ed3860f6..000000000 --- a/app/scripts/lib/observable/local-storage.js +++ /dev/null @@ -1,37 +0,0 @@ -const ObservableStore = require('./index') - -// -// LocalStorageStore -// -// uses localStorage instead of a cache -// - -class LocalStorageStore extends ObservableStore { - - constructor (opts) { - super() - delete this._state - - this._opts = opts || {} - if (!this._opts.storageKey) { - throw new Error('LocalStorageStore - no "storageKey" specified') - } - this._storageKey = this._opts.storageKey - } - - get() { - try { - return JSON.parse(global.localStorage[this._storageKey]) - } catch (err) { - return undefined - } - } - - _put(newState) { - global.localStorage[this._storageKey] = JSON.stringify(newState) - this.emit('update', newState) - } - -} - -module.exports = LocalStorageStore diff --git a/app/scripts/lib/observable/remote.js b/app/scripts/lib/observable/remote.js deleted file mode 100644 index 603f6f0b8..000000000 --- a/app/scripts/lib/observable/remote.js +++ /dev/null @@ -1,51 +0,0 @@ -const Dnode = require('dnode') -const ObservableStore = require('./index') -const endOfStream = require('end-of-stream') - -// -// RemoteStore -// -// connects to a HostStore and receives its latest state -// - -class RemoteStore extends ObservableStore { - - constructor (initState, opts) { - super(initState) - this._opts = opts || {} - this._remote = null - } - - put (newState) { - if (!this._remote) throw new Error('RemoteStore - "put" called before connection to HostStore') - this._put(newState) - this._remote.put(newState) - } - - createStream () { - const self = this - const dnode = Dnode({ - put: (newState) => self._put(newState), - }) - // listen for connection to remote - dnode.once('remote', (remote) => { - // setup connection lifecycle - self._onConnect(remote) - endOfStream(dnode, () => self._onDisconnect()) - }) - return dnode - } - - _onConnect (remote) { - this._remote = remote - this.emit('connected') - } - - _onDisconnect () { - this._remote = null - this.emit('disconnected') - } - -} - -module.exports = RemoteStore \ No newline at end of file diff --git a/app/scripts/lib/observable/util/sync.js b/app/scripts/lib/observable/util/sync.js deleted file mode 100644 index c61feb02e..000000000 --- a/app/scripts/lib/observable/util/sync.js +++ /dev/null @@ -1,24 +0,0 @@ - -// -// synchronizeStore(inStore, outStore, stateTransform) -// -// keeps outStore synchronized with inStore, via an optional stateTransform -// - -module.exports = synchronizeStore - - -function synchronizeStore(inStore, outStore, stateTransform) { - stateTransform = stateTransform || transformNoop - const initState = stateTransform(inStore.get()) - outStore.put(initState) - inStore.subscribe((inState) => { - const outState = stateTransform(inState) - outStore.put(outState) - }) - return outStore -} - -function transformNoop(state) { - return state -} \ No newline at end of file -- cgit From af115aa4c12849b147224cd0d167f5a57565ed6d Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 26 Jan 2017 15:09:31 -0800 Subject: background - organize metamask instantiation --- 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 6d088906c..fd4ac511a 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -266,7 +266,7 @@ ConfigManager.prototype.setCurrentFiat = function (currency) { ConfigManager.prototype.getCurrentFiat = function () { var data = this.getData() - return data.fiatCurrency + return data.fiatCurrency || 'USD' } ConfigManager.prototype.updateConversionRate = function () { -- cgit From fc3a53ec7b2af538d5e9748173fb11b5a09a4e84 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 26 Jan 2017 20:18:28 -0800 Subject: background - stream disconnection fix --- app/scripts/lib/stream-utils.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/stream-utils.js b/app/scripts/lib/stream-utils.js index 1b7b89d14..ba79990cc 100644 --- a/app/scripts/lib/stream-utils.js +++ b/app/scripts/lib/stream-utils.js @@ -1,4 +1,5 @@ const Through = require('through2') +const endOfStream = require('end-of-stream') const ObjectMultiplex = require('./obj-multiplex') module.exports = { @@ -24,11 +25,11 @@ function jsonStringifyStream () { function setupMultiplex (connectionStream) { var mx = ObjectMultiplex() connectionStream.pipe(mx).pipe(connectionStream) - mx.on('error', function (err) { - console.error(err) + endOfStream(mx, function (err) { + if (err) console.error(err) }) - connectionStream.on('error', function (err) { - console.error(err) + endOfStream(connectionStream, function (err) { + if (err) console.error(err) mx.destroy() }) return mx -- cgit From 451845142ed40d1e10bd993cedca1b28e59baba1 Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 27 Jan 2017 14:16:35 -0800 Subject: Rewrite message controller to fit controller pattern --- app/scripts/lib/message-manager.js | 93 ++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 48 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index b609b820e..379f38917 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -1,61 +1,58 @@ -module.exports = new MessageManager() +const EventEmitter = require('events') -function MessageManager (opts) { - this.messages = [] -} +module.exports = class MessageManager extends EventEmitter{ + constructor (opts) { + super() + this.messages = [] + } -MessageManager.prototype.getMsgList = function () { - return this.messages -} + getMsgList () { + return this.messages + } -MessageManager.prototype.unconfirmedMsgs = function () { - var messages = this.getMsgList() - return messages.filter(msg => msg.status === 'unconfirmed') - .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) -} + unconfirmedMsgs () { + let messages = this.getMsgList() + return messages.filter(msg => msg.status === 'unconfirmed') + .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) + } -MessageManager.prototype._saveMsgList = function (msgList) { - this.messages = msgList -} + _saveMsgList (msgList) { + this.messages = msgList + } -MessageManager.prototype.addMsg = function (msg) { - var messages = this.getMsgList() - messages.push(msg) - this._saveMsgList(messages) -} + addMsg (msg) { + let messages = this.getMsgList() + messages.push(msg) + this._saveMsgList(messages) + } -MessageManager.prototype.getMsg = function (msgId) { - var messages = this.getMsgList() - var matching = messages.filter(msg => msg.id === msgId) - return matching.length > 0 ? matching[0] : null -} + getMsg (msgId) { + let messages = this.getMsgList() + let matching = messages.filter(msg => msg.id === msgId) + return matching.length > 0 ? matching[0] : null + } -MessageManager.prototype.confirmMsg = function (msgId) { - this._setMsgStatus(msgId, 'confirmed') -} + confirmMsg (msgId) { + this._setMsgStatus(msgId, 'confirmed') + } -MessageManager.prototype.rejectMsg = function (msgId) { - this._setMsgStatus(msgId, 'rejected') -} + rejectMsg (msgId) { + this._setMsgStatus(msgId, 'rejected') + } -MessageManager.prototype._setMsgStatus = function (msgId, status) { - var msg = this.getMsg(msgId) - if (msg) msg.status = status - this.updateMsg(msg) -} + _setMsgStatus (msgId, status) { + let msg = this.getMsg(msgId) + if (msg) msg.status = status + this.updateMsg(msg) + } -MessageManager.prototype.updateMsg = function (msg) { - var messages = this.getMsgList() - var found, index - messages.forEach((otherMsg, i) => { - if (otherMsg.id === msg.id) { - found = true - index = i + updateMsg (msg) { + let messages = this.getMsgList() + let index = messages.findIndex((message) => message.id === msg.id) + if (index !== -1) { + this.emit('update', msg.id) + messages[index] = msg } - }) - if (found) { - messages[index] = msg + this._saveMsgList(messages) } - this._saveMsgList(messages) } - -- cgit From 3afd0ef27d69ed5d130f4ea37e3e856cadba34d9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 27 Jan 2017 23:04:34 -0800 Subject: tests - remove persistence and start with initial state --- app/scripts/lib/config-manager.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index fd4ac511a..357e081b1 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -29,15 +29,7 @@ ConfigManager.prototype.setConfig = function (config) { ConfigManager.prototype.getConfig = function () { var data = this.getData() - if ('config' in data) { - return data.config - } else { - return { - provider: { - type: 'testnet', - }, - } - } + return data.config } ConfigManager.prototype.setRpcTarget = function (rpcUrl) { -- cgit From 8be68575bbef1dcc89b51355abaee90dbf018387 Mon Sep 17 00:00:00 2001 From: Frankie Date: Fri, 27 Jan 2017 16:11:59 -0800 Subject: Clean up message manger includes: Provider egine bump Remove presence of message manger in keyring controller Change the status wording fom conf to approved make Message manager a class fix messages not being apart of the badge re write message manger to better reflect controller pattern --- app/scripts/lib/message-manager.js | 77 +++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 10 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 379f38917..bc9a9e6c8 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -1,23 +1,61 @@ const EventEmitter = require('events') +const ObservableStore = require('obs-store') +const createId = require('./random-id') + module.exports = class MessageManager extends EventEmitter{ constructor (opts) { super() - this.messages = [] + this.memStore = new ObservableStore({ messages: [] }) + } + + getState() { + return { + unapprovedMsgs: this.unapprovedMsgs(), + messages: this.getMsgList(), + } } getMsgList () { - return this.messages + return this.memStore.getState().messages } - unconfirmedMsgs () { + get unapprovedMsgCount () { + return Object.keys(this.unapprovedMsgs()).length + } + + unapprovedMsgs () { let messages = this.getMsgList() - return messages.filter(msg => msg.status === 'unconfirmed') + return messages.filter(msg => msg.status === 'unapproved') .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) } _saveMsgList (msgList) { - this.messages = msgList + this.emit('updateBadge') + let state = this.memStore.getState() + state.messages = msgList + this.memStore.putState(state) + } + + addUnapprovedMessage (msgParams) { + // create txData obj with parameters and meta data + var time = (new Date()).getTime() + var msgId = createId() + var msgData = { + id: msgId, + msgParams: msgParams, + time: time, + status: 'unapproved', + } + this.addMsg(msgData) + console.log('addUnapprovedMessage:', msgData) + + // keep the cb around for after approval (requires user interaction) + // This cb fires completion to the Dapp's write operation. + + // signal update + this.emit('update') + return msgId } addMsg (msg) { @@ -32,8 +70,28 @@ module.exports = class MessageManager extends EventEmitter{ return matching.length > 0 ? matching[0] : null } - confirmMsg (msgId) { - this._setMsgStatus(msgId, 'confirmed') + brodcastMessage (rawSig, msgId, status) { + this.emit(`${msgId}:finished`, {status, rawSig}) + } + + approveMessage (msgParams) { + this.setMessageApproved(msgParams.metamaskId) + return this.prepMsgForSigning(msgParams) + } + + setMessageApproved (msgId) { + this._setMsgStatus(msgId, 'approved') + } + prepMsgForSigning (msgParams) { + delete msgParams.metamaskId + return Promise.resolve(msgParams) + } + + cancelMessage (msgId) { + // reject tx + // clean up + this.brodcastMessage(null, msgId, 'rejected') + this.rejectMsg(msgId) } rejectMsg (msgId) { @@ -43,14 +101,13 @@ module.exports = class MessageManager extends EventEmitter{ _setMsgStatus (msgId, status) { let msg = this.getMsg(msgId) if (msg) msg.status = status - this.updateMsg(msg) + this._updateMsg(msg) } - updateMsg (msg) { + _updateMsg (msg) { let messages = this.getMsgList() let index = messages.findIndex((message) => message.id === msg.id) if (index !== -1) { - this.emit('update', msg.id) messages[index] = msg } this._saveMsgList(messages) -- cgit From 0a1918f71a0a5768d7501e3ee799a3ed2e6d9f49 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 30 Jan 2017 12:42:24 -0800 Subject: preferences - introduce preferences controller --- app/scripts/lib/controllers/preferences.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 app/scripts/lib/controllers/preferences.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/controllers/preferences.js b/app/scripts/lib/controllers/preferences.js new file mode 100644 index 000000000..e338f5b5e --- /dev/null +++ b/app/scripts/lib/controllers/preferences.js @@ -0,0 +1,30 @@ +const ObservableStore = require('obs-store') +const normalizeAddress = require('../sig-util').normalize + +class PreferencesController { + + constructor (opts = {}) { + const initState = opts.initState || {} + this.store = new ObservableStore(initState) + } + + // + // PUBLIC METHODS + // + + setSelectedAddress(_address) { + const address = normalizeAddress(_address) + this.store.updateState({ selectedAddress: address }) + } + + getSelectedAddress(_address) { + return this.store.getState().selectedAddress + } + + // + // PRIVATE METHODS + // + +} + +module.exports = PreferencesController -- cgit From f9b31fe2c3a43339a4519761e4c8b2f3813e85e0 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 30 Jan 2017 15:08:31 -0800 Subject: rename selectedAccount to selectedAddress --- app/scripts/lib/controllers/preferences.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/controllers/preferences.js b/app/scripts/lib/controllers/preferences.js index e338f5b5e..dc9464c4e 100644 --- a/app/scripts/lib/controllers/preferences.js +++ b/app/scripts/lib/controllers/preferences.js @@ -13,8 +13,11 @@ class PreferencesController { // setSelectedAddress(_address) { - const address = normalizeAddress(_address) - this.store.updateState({ selectedAddress: address }) + return new Promise((resolve, reject) => { + const address = normalizeAddress(_address) + this.store.updateState({ selectedAddress: address }) + resolve() + }) } getSelectedAddress(_address) { -- cgit From 2fa60cfcbfdeb43599007f0dd26fd44a4beec8f1 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 30 Jan 2017 15:25:12 -0800 Subject: continue rename selectedAccount to selectedAddress --- app/scripts/lib/inpage-provider.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js index 066916b4d..faecac137 100644 --- a/app/scripts/lib/inpage-provider.js +++ b/app/scripts/lib/inpage-provider.js @@ -63,20 +63,20 @@ function MetamaskInpageProvider (connectionStream) { MetamaskInpageProvider.prototype.send = function (payload) { const self = this - let selectedAccount + let selectedAddress let result = null switch (payload.method) { case 'eth_accounts': // read from localStorage - selectedAccount = self.publicConfigStore.getState().selectedAccount - result = selectedAccount ? [selectedAccount] : [] + selectedAddress = self.publicConfigStore.getState().selectedAddress + result = selectedAddress ? [selectedAddress] : [] break case 'eth_coinbase': // read from localStorage - selectedAccount = self.publicConfigStore.getState().selectedAccount - result = selectedAccount || '0x0000000000000000000000000000000000000000' + selectedAddress = self.publicConfigStore.getState().selectedAddress + result = selectedAddress break case 'eth_uninstallFilter': -- cgit From 1b16b4624186265ccbb6f8106e1bf9ff997e2528 Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 1 Feb 2017 11:54:01 -0800 Subject: code clan up and tests --- app/scripts/lib/message-manager.js | 42 ++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 22 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index bc9a9e6c8..490cd4d1c 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -11,7 +11,7 @@ module.exports = class MessageManager extends EventEmitter{ getState() { return { - unapprovedMsgs: this.unapprovedMsgs(), + unapprovedMsgs: this.getUnapprovedMsgs(), messages: this.getMsgList(), } } @@ -21,22 +21,15 @@ module.exports = class MessageManager extends EventEmitter{ } get unapprovedMsgCount () { - return Object.keys(this.unapprovedMsgs()).length + return Object.keys(this.getUnapprovedMsgs()).length } - unapprovedMsgs () { + getUnapprovedMsgs () { let messages = this.getMsgList() return messages.filter(msg => msg.status === 'unapproved') .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) } - _saveMsgList (msgList) { - this.emit('updateBadge') - let state = this.memStore.getState() - state.messages = msgList - this.memStore.putState(state) - } - addUnapprovedMessage (msgParams) { // create txData obj with parameters and meta data var time = (new Date()).getTime() @@ -70,33 +63,29 @@ module.exports = class MessageManager extends EventEmitter{ return matching.length > 0 ? matching[0] : null } - brodcastMessage (rawSig, msgId, status) { - this.emit(`${msgId}:finished`, {status, rawSig}) - } - approveMessage (msgParams) { - this.setMessageApproved(msgParams.metamaskId) + this.setMsgStatusApproved(msgParams.metamaskId) return this.prepMsgForSigning(msgParams) } - setMessageApproved (msgId) { + setMsgStatusApproved (msgId) { this._setMsgStatus(msgId, 'approved') } + prepMsgForSigning (msgParams) { delete msgParams.metamaskId return Promise.resolve(msgParams) } - cancelMessage (msgId) { - // reject tx - // clean up + rejectMsg (msgId) { this.brodcastMessage(null, msgId, 'rejected') - this.rejectMsg(msgId) + this._setMsgStatus(msgId, 'rejected') } - rejectMsg (msgId) { - this._setMsgStatus(msgId, 'rejected') + brodcastMessage (rawSig, msgId, status) { + this.emit(`${msgId}:finished`, {status, rawSig}) } +// PRIVATE METHODS _setMsgStatus (msgId, status) { let msg = this.getMsg(msgId) @@ -112,4 +101,13 @@ module.exports = class MessageManager extends EventEmitter{ } this._saveMsgList(messages) } + + _saveMsgList (msgList) { + this.emit('updateBadge') + let state = this.memStore.getState() + state.messages = msgList + this.memStore.putState(state) + } + + } -- cgit From c7b9adbfcbaa51082f193581b80bec4f86996f81 Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 1 Feb 2017 15:00:41 -0800 Subject: swap out set state for updateState --- app/scripts/lib/message-manager.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 490cd4d1c..4a0017342 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -103,10 +103,7 @@ module.exports = class MessageManager extends EventEmitter{ } _saveMsgList (msgList) { - this.emit('updateBadge') - let state = this.memStore.getState() - state.messages = msgList - this.memStore.putState(state) + this.memStore.updateState({ messages: msgList }) } -- cgit From d93708b55394b1408ecf5ff4a5e90f37b666205d Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 1 Feb 2017 15:08:06 -0800 Subject: put updateBadge back in --- app/scripts/lib/message-manager.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 4a0017342..30eff12d3 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -91,6 +91,7 @@ module.exports = class MessageManager extends EventEmitter{ let msg = this.getMsg(msgId) if (msg) msg.status = status this._updateMsg(msg) + this.emit('updateBadge') } _updateMsg (msg) { -- cgit From 10ce2a781f0e1ea85996630b7925c7de4b162e16 Mon Sep 17 00:00:00 2001 From: Frankie Date: Wed, 1 Feb 2017 15:12:15 -0800 Subject: emit a update badge --- app/scripts/lib/message-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 30eff12d3..18bf54ae1 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -91,7 +91,6 @@ module.exports = class MessageManager extends EventEmitter{ let msg = this.getMsg(msgId) if (msg) msg.status = status this._updateMsg(msg) - this.emit('updateBadge') } _updateMsg (msg) { @@ -104,6 +103,7 @@ module.exports = class MessageManager extends EventEmitter{ } _saveMsgList (msgList) { + this.emit('updateBadge') this.memStore.updateState({ messages: msgList }) } -- cgit From bcb86f38cbe91fb814d30b3129de76dba45bff66 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 2 Feb 2017 18:21:22 -0800 Subject: messageManager - move view state to obs-store --- app/scripts/lib/message-manager.js | 52 +++++++++++++++----------------------- 1 file changed, 20 insertions(+), 32 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 18bf54ae1..38fa42017 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -6,18 +6,11 @@ const createId = require('./random-id') module.exports = class MessageManager extends EventEmitter{ constructor (opts) { super() - this.memStore = new ObservableStore({ messages: [] }) - } - - getState() { - return { - unapprovedMsgs: this.getUnapprovedMsgs(), - messages: this.getMsgList(), - } - } - - getMsgList () { - return this.memStore.getState().messages + this.memStore = new ObservableStore({ + unapprovedMsgs: {}, + unapprovedMsgCount: 0, + }) + this.messages = [] } get unapprovedMsgCount () { @@ -25,8 +18,7 @@ module.exports = class MessageManager extends EventEmitter{ } getUnapprovedMsgs () { - let messages = this.getMsgList() - return messages.filter(msg => msg.status === 'unapproved') + return this.messages.filter(msg => msg.status === 'unapproved') .reduce((result, msg) => { result[msg.id] = msg; return result }, {}) } @@ -41,10 +33,6 @@ module.exports = class MessageManager extends EventEmitter{ status: 'unapproved', } this.addMsg(msgData) - console.log('addUnapprovedMessage:', msgData) - - // keep the cb around for after approval (requires user interaction) - // This cb fires completion to the Dapp's write operation. // signal update this.emit('update') @@ -52,15 +40,12 @@ module.exports = class MessageManager extends EventEmitter{ } addMsg (msg) { - let messages = this.getMsgList() - messages.push(msg) - this._saveMsgList(messages) + this.messages.push(msg) + this._saveMsgList() } getMsg (msgId) { - let messages = this.getMsgList() - let matching = messages.filter(msg => msg.id === msgId) - return matching.length > 0 ? matching[0] : null + return this.messages.find(msg => msg.id === msgId) } approveMessage (msgParams) { @@ -85,7 +70,10 @@ module.exports = class MessageManager extends EventEmitter{ brodcastMessage (rawSig, msgId, status) { this.emit(`${msgId}:finished`, {status, rawSig}) } -// PRIVATE METHODS + + // + // PRIVATE METHODS + // _setMsgStatus (msgId, status) { let msg = this.getMsg(msgId) @@ -94,18 +82,18 @@ module.exports = class MessageManager extends EventEmitter{ } _updateMsg (msg) { - let messages = this.getMsgList() - let index = messages.findIndex((message) => message.id === msg.id) + let index = this.messages.findIndex((message) => message.id === msg.id) if (index !== -1) { - messages[index] = msg + this.messages[index] = msg } - this._saveMsgList(messages) + this._saveMsgList() } - _saveMsgList (msgList) { + _saveMsgList () { + const unapprovedMsgs = this.getUnapprovedMsgs() + const unapprovedMsgCount = Object.keys(unapprovedMsgs).length + this.memStore.updateState({ unapprovedMsgs, unapprovedMsgCount }) this.emit('updateBadge') - this.memStore.updateState({ messages: msgList }) } - } -- cgit From 73edfc9f31b1cbd44ae8b5372e7bef5d1d5959ad Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 2 Feb 2017 22:05:06 -0800 Subject: eth-store - convert to obs-store subclass --- app/scripts/lib/eth-store.js | 214 ++++++++++++++++++++----------------------- 1 file changed, 98 insertions(+), 116 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js index 7e2caf884..64a3c6b59 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/eth-store.js @@ -7,140 +7,122 @@ * on each new block. */ -const EventEmitter = require('events').EventEmitter -const inherits = require('util').inherits const async = require('async') -const clone = require('clone') const EthQuery = require('eth-query') +const ObservableStore = require('obs-store') +function noop() {} -module.exports = EthereumStore +class EthereumStore extends ObservableStore { -inherits(EthereumStore, EventEmitter) -function EthereumStore(engine) { - const self = this - EventEmitter.call(self) - self._currentState = { - accounts: {}, - transactions: {}, + constructor (opts = {}) { + super({ + accounts: {}, + transactions: {}, + }) + this._provider = opts.provider + this._query = new EthQuery(this._provider) + this._blockTracker = opts.blockTracker + // subscribe to latest block + this._blockTracker.on('block', this._updateForBlock.bind(this)) } - self._query = new EthQuery(engine) - - engine.on('block', self._updateForBlock.bind(self)) -} - -// -// public -// -EthereumStore.prototype.getState = function () { - const self = this - return clone(self._currentState) -} - -EthereumStore.prototype.addAccount = function (address) { - const self = this - self._currentState.accounts[address] = {} - self._didUpdate() - if (!self.currentBlockNumber) return - self._updateAccount(address, () => { - self._didUpdate() - }) -} + // + // public + // -EthereumStore.prototype.removeAccount = function (address) { - const self = this - delete self._currentState.accounts[address] - self._didUpdate() -} + addAccount (address) { + const accounts = this.getState().accounts + accounts[address] = {} + this.updateState({ accounts }) + if (!this._currentBlockNumber) return + this._updateAccount(address) + } -EthereumStore.prototype.addTransaction = function (txHash) { - const self = this - self._currentState.transactions[txHash] = {} - self._didUpdate() - if (!self.currentBlockNumber) return - self._updateTransaction(self.currentBlockNumber, txHash, noop) -} + removeAccount (address) { + const accounts = this.getState().accounts + delete accounts[address] + this.updateState({ accounts }) + } -EthereumStore.prototype.removeTransaction = function (address) { - const self = this - delete self._currentState.transactions[address] - self._didUpdate() -} + addTransaction (txHash) { + const transactions = this.getState().transactions + transactions[txHash] = {} + this.updateState({ transactions }) + if (!this._currentBlockNumber) return + this._updateTransaction(this._currentBlockNumber, txHash, noop) + } + removeTransaction (txHash) { + const transactions = this.getState().transactions + delete transactions[txHash] + this.updateState({ transactions }) + } -// -// private -// -EthereumStore.prototype._didUpdate = function () { - const self = this - var state = self.getState() - self.emit('update', state) -} + // + // private + // + + _updateForBlock (block) { + const blockNumber = '0x' + block.number.toString('hex') + this._currentBlockNumber = blockNumber + async.parallel([ + this._updateAccounts.bind(this), + this._updateTransactions.bind(this, blockNumber), + ], (err) => { + if (err) return console.error(err) + this.emit('block', this.getState()) + }) + } -EthereumStore.prototype._updateForBlock = function (block) { - const self = this - var blockNumber = '0x' + block.number.toString('hex') - self.currentBlockNumber = blockNumber - async.parallel([ - self._updateAccounts.bind(self), - self._updateTransactions.bind(self, blockNumber), - ], function (err) { - if (err) return console.error(err) - self.emit('block', self.getState()) - self._didUpdate() - }) -} + _updateAccounts (cb) { + const accounts = this.getState().accounts + const addresses = Object.keys(accounts) + async.each(addresses, this._updateAccount.bind(this), cb) + } -EthereumStore.prototype._updateAccounts = function (cb) { - var accountsState = this._currentState.accounts - var addresses = Object.keys(accountsState) - async.each(addresses, this._updateAccount.bind(this), cb) -} + _updateAccount (address, cb) { + const accounts = this.getState().accounts + this._getAccount(address, (err, result) => { + if (err) return cb(err) + result.address = address + // only populate if the entry is still present + if (accounts[address]) { + accounts[address] = result + } + cb(null, result) + }) + } -EthereumStore.prototype._updateAccount = function (address, cb) { - var accountsState = this._currentState.accounts - this.getAccount(address, function (err, result) { - if (err) return cb(err) - result.address = address - // only populate if the entry is still present - if (accountsState[address]) { - accountsState[address] = result - } - cb(null, result) - }) -} + _updateTransactions (block, cb) { + const transactions = this.getState().transactions + const txHashes = Object.keys(transactions) + async.each(txHashes, this._updateTransaction.bind(this, block), cb) + } -EthereumStore.prototype.getAccount = function (address, cb) { - const query = this._query - async.parallel({ - balance: query.getBalance.bind(query, address), - nonce: query.getTransactionCount.bind(query, address), - code: query.getCode.bind(query, address), - }, cb) -} + _updateTransaction (block, txHash, cb) { + // would use the block here to determine how many confirmations the tx has + const transactions = this.getState().transactions + this._query.getTransaction(txHash, (err, result) => { + if (err) return cb(err) + // only populate if the entry is still present + if (transactions[txHash]) { + transactions[txHash] = result + } + cb(null, result) + }) + } -EthereumStore.prototype._updateTransactions = function (block, cb) { - const self = this - var transactionsState = self._currentState.transactions - var txHashes = Object.keys(transactionsState) - async.each(txHashes, self._updateTransaction.bind(self, block), cb) -} + _getAccount (address, cb) { + const query = this._query + async.parallel({ + balance: query.getBalance.bind(query, address), + nonce: query.getTransactionCount.bind(query, address), + code: query.getCode.bind(query, address), + }, cb) + } -EthereumStore.prototype._updateTransaction = function (block, txHash, cb) { - const self = this - // would use the block here to determine how many confirmations the tx has - var transactionsState = self._currentState.transactions - self._query.getTransaction(txHash, function (err, result) { - if (err) return cb(err) - // only populate if the entry is still present - if (transactionsState[txHash]) { - transactionsState[txHash] = result - self._didUpdate() - } - cb(null, result) - }) } -function noop() {} +module.exports = EthereumStore \ No newline at end of file -- cgit From b233e7e37c56852080bdc7b1843eb68ba96b5382 Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 2 Feb 2017 22:32:00 -0800 Subject: eth-store - cbs default to noop --- app/scripts/lib/eth-store.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js index 64a3c6b59..96b4a60f2 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/eth-store.js @@ -76,13 +76,13 @@ class EthereumStore extends ObservableStore { }) } - _updateAccounts (cb) { + _updateAccounts (cb = noop) { const accounts = this.getState().accounts const addresses = Object.keys(accounts) async.each(addresses, this._updateAccount.bind(this), cb) } - _updateAccount (address, cb) { + _updateAccount (address, cb = noop) { const accounts = this.getState().accounts this._getAccount(address, (err, result) => { if (err) return cb(err) @@ -95,13 +95,13 @@ class EthereumStore extends ObservableStore { }) } - _updateTransactions (block, cb) { + _updateTransactions (block, cb = noop) { const transactions = this.getState().transactions const txHashes = Object.keys(transactions) async.each(txHashes, this._updateTransaction.bind(this, block), cb) } - _updateTransaction (block, txHash, cb) { + _updateTransaction (block, txHash, cb = noop) { // would use the block here to determine how many confirmations the tx has const transactions = this.getState().transactions this._query.getTransaction(txHash, (err, result) => { @@ -114,7 +114,7 @@ class EthereumStore extends ObservableStore { }) } - _getAccount (address, cb) { + _getAccount (address, cb = noop) { const query = this._query async.parallel({ balance: query.getBalance.bind(query, address), -- cgit From 9e4ef45b6ac460e6539e0f79ad5c78959fa1c4cb Mon Sep 17 00:00:00 2001 From: kumavis Date: Thu, 2 Feb 2017 23:32:24 -0800 Subject: migration #9 - break out CurrencyController substate --- app/scripts/lib/config-manager.js | 47 ---------------------- app/scripts/lib/controllers/currency.js | 70 +++++++++++++++++++++++++++++++++ app/scripts/lib/idStore.js | 3 -- 3 files changed, 70 insertions(+), 50 deletions(-) create mode 100644 app/scripts/lib/controllers/currency.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index 357e081b1..a9b86ca8c 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -250,53 +250,6 @@ ConfigManager.prototype.getTOSHash = function () { return data.TOSHash } -ConfigManager.prototype.setCurrentFiat = function (currency) { - var data = this.getData() - data.fiatCurrency = currency - this.setData(data) -} - -ConfigManager.prototype.getCurrentFiat = function () { - var data = this.getData() - return data.fiatCurrency || 'USD' -} - -ConfigManager.prototype.updateConversionRate = function () { - var data = this.getData() - return fetch(`https://www.cryptonator.com/api/ticker/eth-${data.fiatCurrency}`) - .then(response => response.json()) - .then((parsedResponse) => { - this.setConversionPrice(parsedResponse.ticker.price) - this.setConversionDate(parsedResponse.timestamp) - }).catch((err) => { - console.warn('MetaMask - Failed to query currency conversion.') - 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 (data.conversionRate) || 0 -} - -ConfigManager.prototype.getConversionDate = function () { - var data = this.getData() - return (data.conversionDate) || 'N/A' -} - ConfigManager.prototype.getShapeShiftTxList = function () { var data = this.getData() var shapeShiftTxList = data.shapeShiftTxList ? data.shapeShiftTxList : [] diff --git a/app/scripts/lib/controllers/currency.js b/app/scripts/lib/controllers/currency.js new file mode 100644 index 000000000..c4904f8ac --- /dev/null +++ b/app/scripts/lib/controllers/currency.js @@ -0,0 +1,70 @@ +const ObservableStore = require('obs-store') +const extend = require('xtend') + +// every ten minutes +const POLLING_INTERVAL = 600000 + +class CurrencyController { + + constructor (opts = {}) { + const initState = extend({ + currentCurrency: 'USD', + conversionRate: 0, + conversionDate: 'N/A', + }, opts.initState) + this.store = new ObservableStore(initState) + } + + // + // PUBLIC METHODS + // + + getCurrentCurrency () { + return this.store.getState().currentCurrency + } + + setCurrentCurrency (currentCurrency) { + this.store.updateState({ currentCurrency }) + } + + getConversionRate () { + return this.store.getState().conversionRate + } + + setConversionRate (conversionRate) { + this.store.updateState({ conversionRate }) + } + + getConversionDate () { + return this.store.getState().conversionDate + } + + setConversionDate (conversionDate) { + this.store.updateState({ conversionDate }) + } + + updateConversionRate () { + const currentCurrency = this.getCurrentCurrency() + return fetch(`https://www.cryptonator.com/api/ticker/eth-${currentCurrency}`) + .then(response => response.json()) + .then((parsedResponse) => { + this.setConversionRate(Number(parsedResponse.ticker.price)) + this.setConversionDate(Number(parsedResponse.timestamp)) + }).catch((err) => { + console.warn('MetaMask - Failed to query currency conversion.') + this.setConversionRate(0) + this.setConversionDate('N/A') + }) + } + + scheduleConversionInterval () { + if (this.conversionInterval) { + clearInterval(this.conversionInterval) + } + this.conversionInterval = setInterval(() => { + this.updateConversionRate() + }, POLLING_INTERVAL) + } +} + +module.exports = CurrencyController diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index e4cbca456..ac395440d 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -97,9 +97,6 @@ IdentityStore.prototype.getState = function () { isDisclaimerConfirmed: configManager.getConfirmedDisclaimer(), selectedAddress: configManager.getSelectedAccount(), shapeShiftTxList: configManager.getShapeShiftTxList(), - currentFiat: configManager.getCurrentFiat(), - conversionRate: configManager.getConversionRate(), - conversionDate: configManager.getConversionDate(), gasMultiplier: configManager.getGasMultiplier(), })) } -- cgit From 99d6a329a2e8358b85ff8a82c7f17e2ebf71a399 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 3 Feb 2017 12:35:01 -0800 Subject: eths-store - eagerly set current block --- app/scripts/lib/eth-store.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js index 96b4a60f2..773c81d1b 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/eth-store.js @@ -25,6 +25,8 @@ class EthereumStore extends ObservableStore { this._blockTracker = opts.blockTracker // subscribe to latest block this._blockTracker.on('block', this._updateForBlock.bind(this)) + // blockTracker.currentBlock may be null + this._currentBlockNumber = this._blockTracker.currentBlock } // -- cgit From 77f8995568d64aa2d9acd878b43fdeb1e7c3bafb Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 3 Feb 2017 14:00:30 -0800 Subject: migrations - fix migration #9 and add safety checks to migrator --- app/scripts/lib/migrator/index.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/migrator/index.js b/app/scripts/lib/migrator/index.js index ab5a757b3..312345263 100644 --- a/app/scripts/lib/migrator/index.js +++ b/app/scripts/lib/migrator/index.js @@ -15,7 +15,7 @@ class Migrator { let remaining = this.migrations.filter(migrationIsPending) return ( - asyncQ.eachSeries(remaining, (migration) => migration.migrate(versionedData)) + asyncQ.eachSeries(remaining, (migration) => this.runMigration(versionedData, migration)) .then(() => versionedData) ) @@ -26,6 +26,17 @@ class Migrator { } } + runMigration(versionedData, migration) { + return ( + migration.migrate(versionedData) + .then((versionedData) => { + if (!versionedData.data) return Promise.reject(new Error('Migrator - Migration returned empty data')) + if (migration.version !== undefined && versionedData.meta.version !== migration.version) return Promise.reject(new Error('Migrator - Migration did not update version number correctly')) + return Promise.resolve(versionedData) + }) + ) + } + generateInitialState (initState) { return { meta: { -- cgit From ff87b9dc7aa3a3bd0e6ca75ca76d538c5ecaf44a Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 3 Feb 2017 14:59:07 -0800 Subject: id mgmt - update to latest eth_sign spec --- app/scripts/lib/message-manager.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/message-manager.js b/app/scripts/lib/message-manager.js index 38fa42017..ceaf8ee2f 100644 --- a/app/scripts/lib/message-manager.js +++ b/app/scripts/lib/message-manager.js @@ -1,5 +1,6 @@ const EventEmitter = require('events') const ObservableStore = require('obs-store') +const ethUtil = require('ethereumjs-util') const createId = require('./random-id') @@ -23,6 +24,7 @@ module.exports = class MessageManager extends EventEmitter{ } addUnapprovedMessage (msgParams) { + msgParams.data = normalizeMsgData(msgParams.data) // create txData obj with parameters and meta data var time = (new Date()).getTime() var msgId = createId() @@ -57,32 +59,39 @@ module.exports = class MessageManager extends EventEmitter{ this._setMsgStatus(msgId, 'approved') } + setMsgStatusSigned (msgId, rawSig) { + const msg = this.getMsg(msgId) + msg.rawSig = rawSig + this._updateMsg(msg) + this._setMsgStatus(msgId, 'signed') + } + prepMsgForSigning (msgParams) { delete msgParams.metamaskId return Promise.resolve(msgParams) } rejectMsg (msgId) { - this.brodcastMessage(null, msgId, 'rejected') this._setMsgStatus(msgId, 'rejected') } - brodcastMessage (rawSig, msgId, status) { - this.emit(`${msgId}:finished`, {status, rawSig}) - } - // // PRIVATE METHODS // _setMsgStatus (msgId, status) { - let msg = this.getMsg(msgId) - if (msg) msg.status = status + const msg = this.getMsg(msgId) + if (!msg) throw new Error('MessageManager - Message not found for id: "${msgId}".') + msg.status = status this._updateMsg(msg) + this.emit(`${msgId}:${status}`, msg) + if (status === 'rejected' || status === 'signed') { + this.emit(`${msgId}:finished`, msg) + } } _updateMsg (msg) { - let index = this.messages.findIndex((message) => message.id === msg.id) + const index = this.messages.findIndex((message) => message.id === msg.id) if (index !== -1) { this.messages[index] = msg } @@ -97,3 +106,13 @@ module.exports = class MessageManager extends EventEmitter{ } } + +function normalizeMsgData(data) { + if (data.slice(0, 2) === '0x') { + // data is already hex + return data + } else { + // data is unicode, convert to hex + return ethUtil.bufferToHex(new Buffer(data, 'utf8')) + } +} \ No newline at end of file -- cgit From bc4efa18072997fea7f20051171f11a36cf006c8 Mon Sep 17 00:00:00 2001 From: kumavis Date: Fri, 3 Feb 2017 16:07:58 -0800 Subject: eth-store - update store state after manipulating --- app/scripts/lib/eth-store.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/eth-store.js b/app/scripts/lib/eth-store.js index 773c81d1b..8812a507b 100644 --- a/app/scripts/lib/eth-store.js +++ b/app/scripts/lib/eth-store.js @@ -92,6 +92,7 @@ class EthereumStore extends ObservableStore { // only populate if the entry is still present if (accounts[address]) { accounts[address] = result + this.updateState({ accounts }) } cb(null, result) }) @@ -111,6 +112,7 @@ class EthereumStore extends ObservableStore { // only populate if the entry is still present if (transactions[txHash]) { transactions[txHash] = result + this.updateState({ transactions }) } cb(null, result) }) -- cgit From 13ee92909cce93b37eec2092757e4aab174a970e Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Feb 2017 20:45:20 -0800 Subject: Mostly got shapeshift tx management into its own controller Rendering the list is still having issues, so this isn't done yet. --- app/scripts/lib/config-manager.js | 34 ---------- app/scripts/lib/controllers/shapeshift.js | 100 ++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 34 deletions(-) create mode 100644 app/scripts/lib/controllers/shapeshift.js (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/config-manager.js b/app/scripts/lib/config-manager.js index a9b86ca8c..7ae2d4400 100644 --- a/app/scripts/lib/config-manager.js +++ b/app/scripts/lib/config-manager.js @@ -250,40 +250,6 @@ ConfigManager.prototype.getTOSHash = function () { return data.TOSHash } -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) -} - ConfigManager.prototype.getGasMultiplier = function () { var data = this.getData() return data.gasMultiplier diff --git a/app/scripts/lib/controllers/shapeshift.js b/app/scripts/lib/controllers/shapeshift.js new file mode 100644 index 000000000..6c02dc3eb --- /dev/null +++ b/app/scripts/lib/controllers/shapeshift.js @@ -0,0 +1,100 @@ +const ObservableStore = require('obs-store') +const extend = require('xtend') + +// every three seconds when an incomplete tx is waiting +const POLLING_INTERVAL = 3000 + +class ShapeshiftController { + + constructor (opts = {}) { + const initState = extend({ + shapeShiftTxList: [], + }, opts) + this.store = new ObservableStore(initState) + } + + // + // PUBLIC METHODS + // + + getShapeShiftTxList () { + const shapeShiftTxList = this.store.getState().shapeShiftTxList + + shapeShiftTxList.forEach((tx) => { + if (tx.response.status === 'no_deposits') { + this.updateTx(tx) + } + }) + console.dir({shapeShiftTxList}) + return shapeShiftTxList + } + + getPendingTxs () { + const txs = this.getShapeShiftTxList() + const pending = txs.filter(tx => tx.response.status !== 'complete') + return pending + } + + pollForUpdates () { + const pendingTxs = this.getPendingTxs() + + if (pendingTxs.length === 0) { + return + } + + Promise.all(pendingTxs.map((tx) => { + return this.updateTx(tx) + })) + .then((results) => { + results.forEach(tx => this.saveTx(tx)) + setTimeout(this.pollForUpdates.bind(this), POLLING_INTERVAL) + }) + } + + updateTx (tx) { + const url = `https://shapeshift.io/txStat/${tx.depositAddress}` + return fetch(url) + .then((response) => { + tx.response = response.json() + if (tx.response.status === 'complete') { + tx.time = new Date().getTime() + } + return tx + }) + } + + saveTx (tx) { + const { shapeShiftTxList } = this.store.getState() + const index = shapeShiftTxList.indexOf(tx) + if (index !== -1) { + shapeShiftTxList[index] = tx + this.store.updateState({ shapeShiftTxList }) + } + } + + createShapeShiftTx (depositAddress, depositType) { + const state = this.store.getState() + let { shapeShiftTxList } = state + + var shapeShiftTx = { + depositAddress, + depositType, + key: 'shapeshift', + time: new Date().getTime(), + response: {}, + } + + if (!shapeShiftTxList) { + shapeShiftTxList = [shapeShiftTx] + } else { + shapeShiftTxList.push(shapeShiftTx) + } + console.dir({ shapeShiftTxList }) + + this.store.updateState({ shapeShiftTxList }) + this.pollForUpdates() + } + +} + +module.exports = ShapeshiftController -- cgit From 4dc71ed57bab3e8310d37e2fb2a4c495ed3ca5d0 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Feb 2017 21:12:18 -0800 Subject: Got ShapeShiftController back to working --- app/scripts/lib/controllers/shapeshift.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/controllers/shapeshift.js b/app/scripts/lib/controllers/shapeshift.js index 6c02dc3eb..bcfe3e14c 100644 --- a/app/scripts/lib/controllers/shapeshift.js +++ b/app/scripts/lib/controllers/shapeshift.js @@ -9,8 +9,9 @@ class ShapeshiftController { constructor (opts = {}) { const initState = extend({ shapeShiftTxList: [], - }, opts) + }, opts.initState) this.store = new ObservableStore(initState) + this.pollForUpdates() } // @@ -19,19 +20,12 @@ class ShapeshiftController { getShapeShiftTxList () { const shapeShiftTxList = this.store.getState().shapeShiftTxList - - shapeShiftTxList.forEach((tx) => { - if (tx.response.status === 'no_deposits') { - this.updateTx(tx) - } - }) - console.dir({shapeShiftTxList}) return shapeShiftTxList } getPendingTxs () { const txs = this.getShapeShiftTxList() - const pending = txs.filter(tx => tx.response.status !== 'complete') + const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete') return pending } @@ -47,7 +41,7 @@ class ShapeshiftController { })) .then((results) => { results.forEach(tx => this.saveTx(tx)) - setTimeout(this.pollForUpdates.bind(this), POLLING_INTERVAL) + this.timeout = setTimeout(this.pollForUpdates.bind(this), POLLING_INTERVAL) }) } @@ -55,7 +49,9 @@ class ShapeshiftController { const url = `https://shapeshift.io/txStat/${tx.depositAddress}` return fetch(url) .then((response) => { - tx.response = response.json() + return response.json() + }).then((json) => { + tx.response = json if (tx.response.status === 'complete') { tx.time = new Date().getTime() } @@ -89,7 +85,6 @@ class ShapeshiftController { } else { shapeShiftTxList.push(shapeShiftTx) } - console.dir({ shapeShiftTxList }) this.store.updateState({ shapeShiftTxList }) this.pollForUpdates() -- cgit From 5d37f90787cdeec130537e61626f92d6f8a7b5e3 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Feb 2017 21:36:04 -0800 Subject: Automatically remove shapeshift txs over 11 minutes old with no payment --- app/scripts/lib/controllers/shapeshift.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/controllers/shapeshift.js b/app/scripts/lib/controllers/shapeshift.js index bcfe3e14c..6d1c95323 100644 --- a/app/scripts/lib/controllers/shapeshift.js +++ b/app/scripts/lib/controllers/shapeshift.js @@ -4,6 +4,9 @@ const extend = require('xtend') // every three seconds when an incomplete tx is waiting const POLLING_INTERVAL = 3000 +// drop txs that haven't been paid to in 11 mins +const TIMEOUT_LIMIT = 660000 + class ShapeshiftController { constructor (opts = {}) { @@ -24,11 +27,21 @@ class ShapeshiftController { } getPendingTxs () { + this.removeOldTxs() const txs = this.getShapeShiftTxList() const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete') return pending } + removeOldTxs() { + const { shapeShiftTxList } = this.store.getState() + const now = new Date().getTime() + const old = shapeShiftTxList.find((tx) => { + return tx.time + TIMEOUT_LIMIT < now + }) + old.forEach(tx => this.removeShapeShiftTx(tx)) + } + pollForUpdates () { const pendingTxs = this.getPendingTxs() @@ -68,6 +81,15 @@ class ShapeshiftController { } } + removeShapeShiftTx (tx) { + const { shapeShiftTxList } = this.store.getState() + const index = shapeShiftTxList.indexOf(index) + if (index !== -1) { + shapeShiftTxList.splice(index, 1) + } + this.updateState({ shapeShiftTxList }) + } + createShapeShiftTx (depositAddress, depositType) { const state = this.store.getState() let { shapeShiftTxList } = state -- cgit From 901eeb5c102dbd8e42d5835e4d35c10fe0301086 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Feb 2017 21:39:22 -0800 Subject: Fix bug when clearing old shapeshift txs --- app/scripts/lib/controllers/shapeshift.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/controllers/shapeshift.js b/app/scripts/lib/controllers/shapeshift.js index 6d1c95323..ec27b37a0 100644 --- a/app/scripts/lib/controllers/shapeshift.js +++ b/app/scripts/lib/controllers/shapeshift.js @@ -39,7 +39,9 @@ class ShapeshiftController { const old = shapeShiftTxList.find((tx) => { return tx.time + TIMEOUT_LIMIT < now }) - old.forEach(tx => this.removeShapeShiftTx(tx)) + if (old) { + old.forEach(tx => this.removeShapeShiftTx(tx)) + } } pollForUpdates () { -- cgit From af439cc6cf10fa0387f7e2196d5fefaf84f2f0a2 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Fri, 3 Feb 2017 21:40:27 -0800 Subject: Do not remove completed shapeshift deposits --- app/scripts/lib/controllers/shapeshift.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/controllers/shapeshift.js b/app/scripts/lib/controllers/shapeshift.js index ec27b37a0..fbdc2c180 100644 --- a/app/scripts/lib/controllers/shapeshift.js +++ b/app/scripts/lib/controllers/shapeshift.js @@ -37,7 +37,8 @@ class ShapeshiftController { const { shapeShiftTxList } = this.store.getState() const now = new Date().getTime() const old = shapeShiftTxList.find((tx) => { - return tx.time + TIMEOUT_LIMIT < now + return tx.time + TIMEOUT_LIMIT < now && + tx.response && tx.response.status === 'no_deposits' }) if (old) { old.forEach(tx => this.removeShapeShiftTx(tx)) -- cgit From 89bbccb09cbb29dd4b1f6a8c7ef3be137da4b243 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sat, 4 Feb 2017 15:15:50 -0800 Subject: Stop removing old shapeshift txs --- app/scripts/lib/controllers/shapeshift.js | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/controllers/shapeshift.js b/app/scripts/lib/controllers/shapeshift.js index fbdc2c180..3d955c01f 100644 --- a/app/scripts/lib/controllers/shapeshift.js +++ b/app/scripts/lib/controllers/shapeshift.js @@ -4,9 +4,6 @@ const extend = require('xtend') // every three seconds when an incomplete tx is waiting const POLLING_INTERVAL = 3000 -// drop txs that haven't been paid to in 11 mins -const TIMEOUT_LIMIT = 660000 - class ShapeshiftController { constructor (opts = {}) { @@ -27,24 +24,11 @@ class ShapeshiftController { } getPendingTxs () { - this.removeOldTxs() const txs = this.getShapeShiftTxList() const pending = txs.filter(tx => tx.response && tx.response.status !== 'complete') return pending } - removeOldTxs() { - const { shapeShiftTxList } = this.store.getState() - const now = new Date().getTime() - const old = shapeShiftTxList.find((tx) => { - return tx.time + TIMEOUT_LIMIT < now && - tx.response && tx.response.status === 'no_deposits' - }) - if (old) { - old.forEach(tx => this.removeShapeShiftTx(tx)) - } - } - pollForUpdates () { const pendingTxs = this.getPendingTxs() -- cgit From 0c0c0051e4cd351c2ffc5bd05a527364fd1445aa Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sat, 4 Feb 2017 15:20:31 -0800 Subject: Remove shapeshift tx list from idStore --- app/scripts/lib/idStore.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index ac395440d..c320a46e9 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -95,8 +95,7 @@ IdentityStore.prototype.getState = function () { isUnlocked: this._isUnlocked(), seedWords: seedWords, isDisclaimerConfirmed: configManager.getConfirmedDisclaimer(), - selectedAddress: configManager.getSelectedAccount(), - shapeShiftTxList: configManager.getShapeShiftTxList(), + tselectedAddress: configManager.getSelectedAccount(), gasMultiplier: configManager.getGasMultiplier(), })) } -- cgit From c0637f8d6ad969f16b7e8b582f462a7f9c480537 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Sat, 4 Feb 2017 16:32:09 -0800 Subject: Fix typo --- app/scripts/lib/idStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/scripts/lib') diff --git a/app/scripts/lib/idStore.js b/app/scripts/lib/idStore.js index c320a46e9..1afe5f651 100644 --- a/app/scripts/lib/idStore.js +++ b/app/scripts/lib/idStore.js @@ -95,7 +95,7 @@ IdentityStore.prototype.getState = function () { isUnlocked: this._isUnlocked(), seedWords: seedWords, isDisclaimerConfirmed: configManager.getConfirmedDisclaimer(), - tselectedAddress: configManager.getSelectedAccount(), + selectedAddress: configManager.getSelectedAccount(), gasMultiplier: configManager.getGasMultiplier(), })) } -- cgit