You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
6.3 KiB
213 lines
6.3 KiB
const ObservableStore = require('obs-store')
|
|
const Box = process.env.IN_TEST
|
|
? require('../../../development/mock-3box')
|
|
: require('3box')
|
|
// const Box = require(process.env.IN_TEST ? '../lib/mock-3box' : '3box/dist/3box.min')
|
|
const log = require('loglevel')
|
|
|
|
const JsonRpcEngine = require('json-rpc-engine')
|
|
const providerFromEngine = require('eth-json-rpc-middleware/providerFromEngine')
|
|
const createMetamaskMiddleware = require('./network/createMetamaskMiddleware')
|
|
const createOriginMiddleware = require('../lib/createOriginMiddleware')
|
|
|
|
const SYNC_TIMEOUT = 60 * 1000 // one minute
|
|
|
|
class ThreeBoxController {
|
|
constructor (opts = {}) {
|
|
const {
|
|
preferencesController,
|
|
keyringController,
|
|
addressBookController,
|
|
version,
|
|
getKeyringControllerState,
|
|
getSelectedAddress,
|
|
} = opts
|
|
|
|
this.preferencesController = preferencesController
|
|
this.addressBookController = addressBookController
|
|
this.keyringController = keyringController
|
|
this.provider = this._createProvider({
|
|
version,
|
|
getAccounts: async ({ origin }) => {
|
|
if (origin !== '3Box') { return [] }
|
|
const isUnlocked = getKeyringControllerState().isUnlocked
|
|
|
|
const selectedAddress = getSelectedAddress()
|
|
|
|
if (isUnlocked && selectedAddress) {
|
|
return [selectedAddress]
|
|
} else {
|
|
return []
|
|
}
|
|
},
|
|
processPersonalMessage: (msgParams) => {
|
|
return Promise.resolve(keyringController.signPersonalMessage(msgParams, {
|
|
withAppKeyOrigin: 'wallet://3box.metamask.io',
|
|
}))
|
|
},
|
|
})
|
|
|
|
const initState = {
|
|
threeBoxSyncingAllowed: true,
|
|
restoredFromThreeBox: null,
|
|
...opts.initState,
|
|
threeBoxAddress: null,
|
|
threeBoxSynced: false,
|
|
threeBoxDisabled: false,
|
|
}
|
|
this.store = new ObservableStore(initState)
|
|
this.registeringUpdates = false
|
|
|
|
const threeBoxFeatureFlagTurnedOn = this.preferencesController.getFeatureFlags().threeBox
|
|
|
|
if (threeBoxFeatureFlagTurnedOn) {
|
|
this.init()
|
|
}
|
|
}
|
|
|
|
async init () {
|
|
const accounts = await this.keyringController.getAccounts()
|
|
this.address = accounts[0]
|
|
if (this.address && !(this.box && this.store.getState().threeBoxSynced)) {
|
|
await this.new3Box()
|
|
}
|
|
}
|
|
|
|
async _update3Box ({ type }, newState) {
|
|
try {
|
|
const { threeBoxSyncingAllowed, threeBoxSynced } = this.store.getState()
|
|
if (threeBoxSyncingAllowed && threeBoxSynced) {
|
|
await this.space.private.set('lastUpdated', Date.now())
|
|
await this.space.private.set(type, JSON.stringify(newState))
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
_createProvider (providerOpts) {
|
|
const metamaskMiddleware = createMetamaskMiddleware(providerOpts)
|
|
const engine = new JsonRpcEngine()
|
|
engine.push(createOriginMiddleware({ origin: '3Box' }))
|
|
engine.push(metamaskMiddleware)
|
|
const provider = providerFromEngine(engine)
|
|
return provider
|
|
}
|
|
|
|
_waitForOnSyncDone () {
|
|
return new Promise((resolve) => {
|
|
this.box.onSyncDone(() => {
|
|
log.debug('3Box box sync done')
|
|
return resolve()
|
|
})
|
|
})
|
|
}
|
|
|
|
async new3Box () {
|
|
const accounts = await this.keyringController.getAccounts()
|
|
const address = accounts[0]
|
|
|
|
if (this.getThreeBoxSyncingState()) {
|
|
this.store.updateState({ threeBoxSynced: false })
|
|
this.address = await this.keyringController.getAppKeyAddress(address, 'wallet://3box.metamask.io')
|
|
|
|
let timedOut = false
|
|
const syncTimeout = setTimeout(() => {
|
|
log.error(`3Box sync timed out after ${SYNC_TIMEOUT} ms`)
|
|
timedOut = true
|
|
this.store.updateState({
|
|
threeBoxDisabled: true,
|
|
threeBoxSyncingAllowed: false,
|
|
})
|
|
}, SYNC_TIMEOUT)
|
|
try {
|
|
this.box = await Box.openBox(address, this.provider)
|
|
await this._waitForOnSyncDone()
|
|
this.space = await this.box.openSpace('metamask', {
|
|
onSyncDone: async () => {
|
|
const stateUpdate = {
|
|
threeBoxSynced: true,
|
|
threeBoxAddress: address,
|
|
}
|
|
if (timedOut) {
|
|
log.info(`3Box sync completed after timeout; no longer disabled`)
|
|
stateUpdate.threeBoxDisabled = false
|
|
}
|
|
|
|
clearTimeout(syncTimeout)
|
|
this.store.updateState(stateUpdate)
|
|
log.debug('3Box space sync done')
|
|
},
|
|
})
|
|
} catch (e) {
|
|
console.error(e)
|
|
throw e
|
|
}
|
|
}
|
|
}
|
|
|
|
async getLastUpdated () {
|
|
return await this.space.private.get('lastUpdated')
|
|
}
|
|
|
|
async restoreFromThreeBox () {
|
|
this.setRestoredFromThreeBoxToTrue()
|
|
const backedUpPreferences = await this.space.private.get('preferences')
|
|
backedUpPreferences && this.preferencesController.store.updateState(JSON.parse(backedUpPreferences))
|
|
const backedUpAddressBook = await this.space.private.get('addressBook')
|
|
backedUpAddressBook && this.addressBookController.update(JSON.parse(backedUpAddressBook), true)
|
|
}
|
|
|
|
turnThreeBoxSyncingOn () {
|
|
this._registerUpdates()
|
|
}
|
|
|
|
turnThreeBoxSyncingOff () {
|
|
this.box.logout()
|
|
}
|
|
|
|
setRestoredFromThreeBoxToTrue () {
|
|
this.store.updateState({ restoredFromThreeBox: true })
|
|
}
|
|
|
|
setRestoredFromThreeBoxToFalse () {
|
|
this.store.updateState({ restoredFromThreeBox: false })
|
|
}
|
|
|
|
setThreeBoxSyncingPermission (newThreeboxSyncingState) {
|
|
if (this.store.getState().threeBoxDisabled) {
|
|
return
|
|
}
|
|
this.store.updateState({
|
|
threeBoxSyncingAllowed: newThreeboxSyncingState,
|
|
})
|
|
|
|
if (newThreeboxSyncingState && this.box) {
|
|
this.turnThreeBoxSyncingOn()
|
|
}
|
|
|
|
if (!newThreeboxSyncingState && this.box) {
|
|
this.turnThreeBoxSyncingOff()
|
|
}
|
|
}
|
|
|
|
getThreeBoxSyncingState () {
|
|
return this.store.getState().threeBoxSyncingAllowed
|
|
}
|
|
|
|
getThreeBoxAddress () {
|
|
return this.store.getState().threeBoxAddress
|
|
}
|
|
|
|
_registerUpdates () {
|
|
if (!this.registeringUpdates) {
|
|
const updatePreferences = this._update3Box.bind(this, { type: 'preferences' })
|
|
this.preferencesController.store.subscribe(updatePreferences)
|
|
const updateAddressBook = this._update3Box.bind(this, { type: 'addressBook' })
|
|
this.addressBookController.subscribe(updateAddressBook)
|
|
this.registeringUpdates = true
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = ThreeBoxController
|
|
|