mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
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
This commit is contained in:
parent
1eebe54c64
commit
2f7d449427
@ -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' }, '*')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
19
app/scripts/controllers/network/createBlockTracker.js
Normal file
19
app/scripts/controllers/network/createBlockTracker.js
Normal file
@ -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
|
||||
}
|
@ -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 }),
|
||||
|
@ -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 }),
|
||||
|
@ -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 }),
|
||||
|
@ -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,
|
||||
|
92
app/scripts/createStandardProvider.js
Normal file
92
app/scripts/createStandardProvider.js
Normal file
@ -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
|
||||
}
|
@ -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) {
|
||||
|
@ -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({
|
||||
|
Loading…
Reference in New Issue
Block a user