mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-23 02:10:12 +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 }, '*')
|
window.postMessage({ type: 'ethereumproviderlegacy', selectedAddress }, '*')
|
||||||
break
|
break
|
||||||
case 'reject-provider-request':
|
case 'reject-provider-request':
|
||||||
window.postMessage({ type: 'ethereumprovider', error: 'User rejected provider access' }, '*')
|
window.postMessage({ type: 'ethereumprovider', error: 'User denied account authorization' }, '*')
|
||||||
break
|
break
|
||||||
case 'answer-is-approved':
|
case 'answer-is-approved':
|
||||||
window.postMessage({ type: 'ethereumisapproved', isApproved, caching }, '*')
|
window.postMessage({ type: 'ethereumisapproved', isApproved, caching }, '*')
|
||||||
@ -170,6 +170,11 @@ function listenForProviderRequest () {
|
|||||||
isEnabled = false
|
isEnabled = false
|
||||||
window.postMessage({ type: 'metamasksetlocked' }, '*')
|
window.postMessage({ type: 'metamasksetlocked' }, '*')
|
||||||
break
|
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 createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||||
const createInfuraMiddleware = require('eth-json-rpc-infura')
|
const createInfuraMiddleware = require('eth-json-rpc-infura')
|
||||||
const BlockTracker = require('eth-block-tracker')
|
const createBlockTracker = require('./createBlockTracker')
|
||||||
|
|
||||||
module.exports = createInfuraClient
|
module.exports = createInfuraClient
|
||||||
|
|
||||||
function createInfuraClient ({ network }) {
|
function createInfuraClient ({ network, platform }) {
|
||||||
const infuraMiddleware = createInfuraMiddleware({ network, maxAttempts: 5, source: 'metamask' })
|
const infuraMiddleware = createInfuraMiddleware({ network, maxAttempts: 5, source: 'metamask' })
|
||||||
const infuraProvider = providerFromMiddleware(infuraMiddleware)
|
const infuraProvider = providerFromMiddleware(infuraMiddleware)
|
||||||
const blockTracker = new BlockTracker({ provider: infuraProvider })
|
const blockTracker = createBlockTracker({ provider: infuraProvider }, platform)
|
||||||
|
|
||||||
const networkMiddleware = mergeMiddleware([
|
const networkMiddleware = mergeMiddleware([
|
||||||
createNetworkAndChainIdMiddleware({ network }),
|
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 createInflightMiddleware = require('eth-json-rpc-middleware/inflight-cache')
|
||||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||||
const BlockTracker = require('eth-block-tracker')
|
const createBlockTracker = require('./createBlockTracker')
|
||||||
|
|
||||||
module.exports = createJsonRpcClient
|
module.exports = createJsonRpcClient
|
||||||
|
|
||||||
function createJsonRpcClient ({ rpcUrl }) {
|
function createJsonRpcClient ({ rpcUrl, platform }) {
|
||||||
const fetchMiddleware = createFetchMiddleware({ rpcUrl })
|
const fetchMiddleware = createFetchMiddleware({ rpcUrl })
|
||||||
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
||||||
const blockTracker = new BlockTracker({ provider: blockProvider })
|
const blockTracker = createBlockTracker({ provider: blockProvider }, platform)
|
||||||
|
|
||||||
const networkMiddleware = mergeMiddleware([
|
const networkMiddleware = mergeMiddleware([
|
||||||
createBlockRefRewriteMiddleware({ blockTracker }),
|
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 createBlockRefRewriteMiddleware = require('eth-json-rpc-middleware/block-ref-rewrite')
|
||||||
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
const createBlockTrackerInspectorMiddleware = require('eth-json-rpc-middleware/block-tracker-inspector')
|
||||||
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
const providerFromMiddleware = require('eth-json-rpc-middleware/providerFromMiddleware')
|
||||||
const BlockTracker = require('eth-block-tracker')
|
const createBlockTracker = require('./createBlockTracker')
|
||||||
|
|
||||||
module.exports = createLocalhostClient
|
module.exports = createLocalhostClient
|
||||||
|
|
||||||
function createLocalhostClient () {
|
function createLocalhostClient ({ platform }) {
|
||||||
const fetchMiddleware = createFetchMiddleware({ rpcUrl: 'http://localhost:8545/' })
|
const fetchMiddleware = createFetchMiddleware({ rpcUrl: 'http://localhost:8545/' })
|
||||||
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
const blockProvider = providerFromMiddleware(fetchMiddleware)
|
||||||
const blockTracker = new BlockTracker({ provider: blockProvider, pollingInterval: 1000 })
|
const blockTracker = createBlockTracker({ provider: blockProvider, pollingInterval: 1000 }, platform)
|
||||||
|
|
||||||
const networkMiddleware = mergeMiddleware([
|
const networkMiddleware = mergeMiddleware([
|
||||||
createBlockRefRewriteMiddleware({ blockTracker }),
|
createBlockRefRewriteMiddleware({ blockTracker }),
|
||||||
|
@ -37,8 +37,9 @@ const defaultNetworkConfig = {
|
|||||||
|
|
||||||
module.exports = class NetworkController extends EventEmitter {
|
module.exports = class NetworkController extends EventEmitter {
|
||||||
|
|
||||||
constructor (opts = {}) {
|
constructor (opts = {}, platform) {
|
||||||
super()
|
super()
|
||||||
|
this.platform = platform
|
||||||
|
|
||||||
// parse options
|
// parse options
|
||||||
const providerConfig = opts.provider || defaultProviderConfig
|
const providerConfig = opts.provider || defaultProviderConfig
|
||||||
@ -180,7 +181,7 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
|
|
||||||
_configureInfuraProvider ({ type }) {
|
_configureInfuraProvider ({ type }) {
|
||||||
log.info('NetworkController - configureInfuraProvider', type)
|
log.info('NetworkController - configureInfuraProvider', type)
|
||||||
const networkClient = createInfuraClient({ network: type })
|
const networkClient = createInfuraClient({ network: type, platform: this.platform })
|
||||||
this._setNetworkClient(networkClient)
|
this._setNetworkClient(networkClient)
|
||||||
// setup networkConfig
|
// setup networkConfig
|
||||||
var settings = {
|
var settings = {
|
||||||
@ -191,13 +192,13 @@ module.exports = class NetworkController extends EventEmitter {
|
|||||||
|
|
||||||
_configureLocalhostProvider () {
|
_configureLocalhostProvider () {
|
||||||
log.info('NetworkController - configureLocalhostProvider')
|
log.info('NetworkController - configureLocalhostProvider')
|
||||||
const networkClient = createLocalhostClient()
|
const networkClient = createLocalhostClient({ platform: this.platform })
|
||||||
this._setNetworkClient(networkClient)
|
this._setNetworkClient(networkClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
|
_configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) {
|
||||||
log.info('NetworkController - configureStandardProvider', rpcUrl)
|
log.info('NetworkController - configureStandardProvider', rpcUrl)
|
||||||
const networkClient = createJsonRpcClient({ rpcUrl })
|
const networkClient = createJsonRpcClient({ rpcUrl, platform: this.platform })
|
||||||
// hack to add a 'rpc' network with chainId
|
// hack to add a 'rpc' network with chainId
|
||||||
networks.networkList['rpc'] = {
|
networks.networkList['rpc'] = {
|
||||||
chainId: chainId,
|
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 LocalMessageDuplexStream = require('post-message-stream')
|
||||||
const setupDappAutoReload = require('./lib/auto-reload.js')
|
const setupDappAutoReload = require('./lib/auto-reload.js')
|
||||||
const MetamaskInpageProvider = require('metamask-inpage-provider')
|
const MetamaskInpageProvider = require('metamask-inpage-provider')
|
||||||
|
const createStandardProvider = require('./createStandardProvider').default
|
||||||
|
|
||||||
let isEnabled = false
|
let isEnabled = false
|
||||||
let warned = false
|
let warned = false
|
||||||
@ -16,12 +17,6 @@ restoreContextAfterImports()
|
|||||||
|
|
||||||
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
|
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
|
* Adds a postMessage listener for a specific message type
|
||||||
*
|
*
|
||||||
@ -70,7 +65,10 @@ inpageProvider.enable = function ({ force } = {}) {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
providerHandle = ({ data: { error, selectedAddress } }) => {
|
providerHandle = ({ data: { error, selectedAddress } }) => {
|
||||||
if (typeof error !== 'undefined') {
|
if (typeof error !== 'undefined') {
|
||||||
reject(error)
|
reject({
|
||||||
|
message: error,
|
||||||
|
code: 4001,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
window.removeEventListener('message', providerHandle)
|
window.removeEventListener('message', providerHandle)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -155,7 +153,7 @@ const proxiedInpageProvider = new Proxy(inpageProvider, {
|
|||||||
deleteProperty: () => true,
|
deleteProperty: () => true,
|
||||||
})
|
})
|
||||||
|
|
||||||
window.ethereum = proxiedInpageProvider
|
window.ethereum = createStandardProvider(proxiedInpageProvider)
|
||||||
|
|
||||||
// detect eth_requestAccounts and pipe to enable for now
|
// detect eth_requestAccounts and pipe to enable for now
|
||||||
function detectAccountRequest (method) {
|
function detectAccountRequest (method) {
|
||||||
|
@ -86,7 +86,7 @@ module.exports = class MetamaskController extends EventEmitter {
|
|||||||
this.createVaultMutex = new Mutex()
|
this.createVaultMutex = new Mutex()
|
||||||
|
|
||||||
// network store
|
// network store
|
||||||
this.networkController = new NetworkController(initState.NetworkController)
|
this.networkController = new NetworkController(initState.NetworkController, this.platform)
|
||||||
|
|
||||||
// preferences controller
|
// preferences controller
|
||||||
this.preferencesController = new PreferencesController({
|
this.preferencesController = new PreferencesController({
|
||||||
|
Loading…
Reference in New Issue
Block a user