aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Bouchon <mail@bitpshr.net>2019-02-20 08:42:08 +0800
committerDan Finlay <542863+danfinlay@users.noreply.github.com>2019-02-20 08:42:08 +0800
commit2f7d4494278ad809c1cc9fcc0d9438182003b22d (patch)
treecf3a9a60c84ee8dd53bbb0fdc115f1285da40d01
parent1eebe54c6422845ec90e6bdb8bffc7c1a7da440c (diff)
downloadtangerine-wallet-browser-2f7d4494278ad809c1cc9fcc0d9438182003b22d.tar
tangerine-wallet-browser-2f7d4494278ad809c1cc9fcc0d9438182003b22d.tar.gz
tangerine-wallet-browser-2f7d4494278ad809c1cc9fcc0d9438182003b22d.tar.bz2
tangerine-wallet-browser-2f7d4494278ad809c1cc9fcc0d9438182003b22d.tar.lz
tangerine-wallet-browser-2f7d4494278ad809c1cc9fcc0d9438182003b22d.tar.xz
tangerine-wallet-browser-2f7d4494278ad809c1cc9fcc0d9438182003b22d.tar.zst
tangerine-wallet-browser-2f7d4494278ad809c1cc9fcc0d9438182003b22d.zip
EIP-1193: standard provider API (#6170)
* EIP-1193: Implement new provider API * EIP-1193: Updated implementation * Remove test file * Fix tests * Update ping check * Update logic * PR feedback
-rw-r--r--app/scripts/contentscript.js7
-rw-r--r--app/scripts/controllers/network/createBlockTracker.js19
-rw-r--r--app/scripts/controllers/network/createInfuraClient.js6
-rw-r--r--app/scripts/controllers/network/createJsonRpcClient.js6
-rw-r--r--app/scripts/controllers/network/createLocalhostClient.js6
-rw-r--r--app/scripts/controllers/network/network.js9
-rw-r--r--app/scripts/createStandardProvider.js92
-rw-r--r--app/scripts/inpage.js14
-rw-r--r--app/scripts/metamask-controller.js2
9 files changed, 138 insertions, 23 deletions
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 65e2ec523..68b6117e5 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -158,7 +158,7 @@ function listenForProviderRequest () {
window.postMessage({ type: 'ethereumproviderlegacy', selectedAddress }, '*')
break
case 'reject-provider-request':
- window.postMessage({ type: 'ethereumprovider', error: 'User rejected provider access' }, '*')
+ window.postMessage({ type: 'ethereumprovider', error: 'User denied account authorization' }, '*')
break
case 'answer-is-approved':
window.postMessage({ type: 'ethereumisapproved', isApproved, caching }, '*')
@@ -170,6 +170,11 @@ function listenForProviderRequest () {
isEnabled = false
window.postMessage({ type: 'metamasksetlocked' }, '*')
break
+ case 'ethereum-ping-success':
+ window.postMessage({ type: 'ethereumpingsuccess' }, '*')
+ break
+ case 'ethereum-ping-error':
+ window.postMessage({ type: 'ethereumpingerror' }, '*')
}
})
}
diff --git a/app/scripts/controllers/network/createBlockTracker.js b/app/scripts/controllers/network/createBlockTracker.js
new file mode 100644
index 000000000..6573b18a1
--- /dev/null
+++ b/app/scripts/controllers/network/createBlockTracker.js
@@ -0,0 +1,19 @@
+const BlockTracker = require('eth-block-tracker')
+
+/**
+ * Creates a block tracker that sends platform events on success and failure
+ */
+module.exports = function createBlockTracker (args, platform) {
+ const blockTracker = new BlockTracker(args)
+ blockTracker.on('latest', () => {
+ if (platform && platform.sendMessage) {
+ platform.sendMessage({ action: 'ethereum-ping-success' })
+ }
+ })
+ blockTracker.on('error', () => {
+ if (platform && platform.sendMessage) {
+ platform.sendMessage({ action: 'ethereum-ping-error' })
+ }
+ })
+ return blockTracker
+}
diff --git a/app/scripts/controllers/network/createInfuraClient.js b/app/scripts/controllers/network/createInfuraClient.js
index 5281dc4c1..884b94db3 100644
--- a/app/scripts/controllers/network/createInfuraClient.js
+++ b/app/scripts/controllers/network/createInfuraClient.js
@@ -7,14 +7,14 @@ const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
const createInfuraMiddleware = require('eth-json-rpc-infura')
-const BlockTracker = require('eth-block-tracker')
+const createBlockTracker = require('./createBlockTracker')
module.exports = createInfuraClient
-function createInfuraClient ({ network }) {
+function createInfuraClient ({ network, platform }) {
const infuraMiddleware = createInfuraMiddleware({ network, maxAttempts: 5, source: 'metamask' })
const infuraProvider = providerFromMiddleware(infuraMiddleware)
- const blockTracker = new BlockTracker({ provider: infuraProvider })
+ const blockTracker = createBlockTracker({ provider: infuraProvider }, platform)
const networkMiddleware = mergeMiddleware([
createNetworkAndChainIdMiddleware({ network }),
diff --git a/app/scripts/controllers/network/createJsonRpcClient.js b/app/scripts/controllers/network/createJsonRpcClient.js
index a8cbf2aaf..369dcd299 100644
--- a/app/scripts/controllers/network/createJsonRpcClient.js
+++ b/app/scripts/controllers/network/createJsonRpcClient.js
@@ -5,14 +5,14 @@ const createBlockCacheMiddleware = require('eth-json-rpc-middleware/block-cache'
const createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache')
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
-const BlockTracker = require('eth-block-tracker')
+const createBlockTracker = require('./createBlockTracker')
module.exports = createJsonRpcClient
-function createJsonRpcClient ({ rpcUrl }) {
+function createJsonRpcClient ({ rpcUrl, platform }) {
const fetchMiddleware = createFetchMiddleware({ rpcUrl })
const blockProvider = providerFromMiddleware(fetchMiddleware)
- const blockTracker = new BlockTracker({ provider: blockProvider })
+ const blockTracker = createBlockTracker({ provider: blockProvider }, platform)
const networkMiddleware = mergeMiddleware([
createBlockRefRewriteMiddleware({ blockTracker }),
diff --git a/app/scripts/controllers/network/createLocalhostClient.js b/app/scripts/controllers/network/createLocalhostClient.js
index 09b1d3c1c..36593dc70 100644
--- a/app/scripts/controllers/network/createLocalhostClient.js
+++ b/app/scripts/controllers/network/createLocalhostClient.js
@@ -3,14 +3,14 @@ const createFetchMiddleware = require('eth-json-rpc-middleware/fetch')
const createBlockRefRewriteMiddleware = require('eth-json-rpc-middleware/block-ref-rewrite')
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
-const BlockTracker = require('eth-block-tracker')
+const createBlockTracker = require('./createBlockTracker')
module.exports = createLocalhostClient
-function createLocalhostClient () {
+function createLocalhostClient ({ platform }) {
const fetchMiddleware = createFetchMiddleware({ rpcUrl: 'http://localhost:8545/' })
const blockProvider = providerFromMiddleware(fetchMiddleware)
- const blockTracker = new BlockTracker({ provider: blockProvider, pollingInterval: 1000 })
+ const blockTracker = createBlockTracker({ provider: blockProvider, pollingInterval: 1000 }, platform)
const networkMiddleware = mergeMiddleware([
createBlockRefRewriteMiddleware({ blockTracker }),
diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js
index 2958ba3b0..0c6327f6e 100644
--- a/app/scripts/controllers/network/network.js
+++ b/app/scripts/controllers/network/network.js
@@ -37,8 +37,9 @@ const defaultNetworkConfig = {
module.exports = class NetworkController extends EventEmitter {
- constructor (opts = {}) {
+ constructor (opts = {}, platform) {
super()
+ this.platform = platform
// parse options
const providerConfig = opts.provider || defaultProviderConfig
@@ -180,7 +181,7 @@ module.exports = class NetworkController extends EventEmitter {
_configureInfuraProvider ({ type }) {
log.info('NetworkController - configureInfuraProvider', type)
- const networkClient = createInfuraClient({ network: type })
+ const networkClient = createInfuraClient({ network: type, platform: this.platform })
this._setNetworkClient(networkClient)
// setup networkConfig
var settings = {
@@ -191,13 +192,13 @@ module.exports = class NetworkController extends EventEmitter {
_configureLocalhostProvider () {
log.info('NetworkController - configureLocalhostProvider')
- const networkClient = createLocalhostClient()
+ const networkClient = createLocalhostClient({ platform: this.platform })
this._setNetworkClient(networkClient)
}
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
log.info('NetworkController - configureStandardProvider', rpcUrl)
- const networkClient = createJsonRpcClient({ rpcUrl })
+ const networkClient = createJsonRpcClient({ rpcUrl, platform: this.platform })
// hack to add a 'rpc' network with chainId
networks.networkList['rpc'] = {
chainId: chainId,
diff --git a/app/scripts/createStandardProvider.js b/app/scripts/createStandardProvider.js
new file mode 100644
index 000000000..a5f9c5d03
--- /dev/null
+++ b/app/scripts/createStandardProvider.js
@@ -0,0 +1,92 @@
+class StandardProvider {
+ _isConnected
+ _provider
+
+ constructor (provider) {
+ this._provider = provider
+ this._onMessage('ethereumpingerror', this._onClose.bind(this))
+ this._onMessage('ethereumpingsuccess', this._onConnect.bind(this))
+ window.addEventListener('load', () => {
+ this._subscribe()
+ this._ping()
+ })
+ }
+
+ _onMessage (type, handler) {
+ window.addEventListener('message', function ({ data }) {
+ if (!data || data.type !== type) return
+ handler.apply(this, arguments)
+ })
+ }
+
+ _onClose () {
+ if (this._isConnected === undefined || this._isConnected) {
+ this._provider.emit('close', {
+ code: 1011,
+ reason: 'Network connection error',
+ })
+ }
+ this._isConnected = false
+ }
+
+ _onConnect () {
+ !this._isConnected && this._provider.emit('connect')
+ this._isConnected = true
+ }
+
+ async _ping () {
+ try {
+ await this.send('net_version')
+ window.postMessage({ type: 'ethereumpingsuccess' }, '*')
+ } catch (error) {
+ window.postMessage({ type: 'ethereumpingerror' }, '*')
+ }
+ }
+
+ _subscribe () {
+ this._provider.on('data', (error, { method, params }) => {
+ if (!error && method === 'eth_subscription') {
+ this._provider.emit('notification', params.result)
+ }
+ })
+ }
+
+ /**
+ * Initiate an RPC method call
+ *
+ * @param {string} method - RPC method name to call
+ * @param {string[]} params - Array of RPC method parameters
+ * @returns {Promise<*>} Promise resolving to the result if successful
+ */
+ send (method, params = []) {
+ if (method === 'eth_requestAccounts') return this._provider.enable()
+
+ return new Promise((resolve, reject) => {
+ try {
+ this._provider.sendAsync({ method, params, beta: true }, (error, response) => {
+ error = error || response.error
+ error ? reject(error) : resolve(response)
+ })
+ } catch (error) {
+ reject(error)
+ }
+ })
+ }
+}
+
+/**
+ * Converts a legacy provider into an EIP-1193-compliant standard provider
+ * @param {Object} provider - Legacy provider to convert
+ * @returns {Object} Standard provider
+ */
+export default function createStandardProvider (provider) {
+ const standardProvider = new StandardProvider(provider)
+ const sendLegacy = provider.send
+ provider.send = (methodOrPayload, callbackOrArgs) => {
+ if (typeof methodOrPayload === 'string' && !callbackOrArgs || Array.isArray(callbackOrArgs)) {
+ return standardProvider.send(methodOrPayload, callbackOrArgs)
+ }
+ return sendLegacy.call(provider, methodOrPayload, callbackOrArgs)
+ }
+ return provider
+}
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index ae5a375b0..c7f0c5669 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -5,6 +5,7 @@ const log = require('loglevel')
const LocalMessageDuplexStream = require('post-message-stream')
const setupDappAutoReload = require('./lib/auto-reload.js')
const MetamaskInpageProvider = require('metamask-inpage-provider')
+const createStandardProvider = require('./createStandardProvider').default
let isEnabled = false
let warned = false
@@ -16,12 +17,6 @@ restoreContextAfterImports()
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
-console.warn('ATTENTION: In an effort to improve user privacy, MetaMask ' +
-'stopped exposing user accounts to dapps if "privacy mode" is enabled on ' +
-'November 2nd, 2018. Dapps should now call provider.enable() in order to view and use ' +
-'accounts. Please see https://bit.ly/2QQHXvF for complete information and up-to-date ' +
-'example code.')
-
/**
* Adds a postMessage listener for a specific message type
*
@@ -70,7 +65,10 @@ inpageProvider.enable = function ({ force } = {}) {
return new Promise((resolve, reject) => {
providerHandle = ({ data: { error, selectedAddress } }) => {
if (typeof error !== 'undefined') {
- reject(error)
+ reject({
+ message: error,
+ code: 4001,
+ })
} else {
window.removeEventListener('message', providerHandle)
setTimeout(() => {
@@ -155,7 +153,7 @@ const proxiedInpageProvider = new Proxy(inpageProvider, {
deleteProperty: () => true,
})
-window.ethereum = proxiedInpageProvider
+window.ethereum = createStandardProvider(proxiedInpageProvider)
// detect eth_requestAccounts and pipe to enable for now
function detectAccountRequest (method) {
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 65cc2d3eb..b75f95d01 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -86,7 +86,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.createVaultMutex = new Mutex()
// network store
- this.networkController = new NetworkController(initState.NetworkController)
+ this.networkController = new NetworkController(initState.NetworkController, this.platform)
// preferences controller
this.preferencesController = new PreferencesController({