mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 01:47:00 +01:00
ens-ipfs - refactor for readability (#5568)
* ens-ipfs - refactor for readability * ens-ipfs - use official ipfs gateway for better performance * lint - remove unused code * ens-ipfs - support path and search * lint - gotta love that linter * ens-ipfs - improve loading page formatting * ens-ipfs - loading - redirect to 404 after 1 min timeout * ens-ipfs - destructure for cleaner code
This commit is contained in:
parent
b0c649a4e3
commit
6d09f60bbf
@ -1,5 +1,9 @@
|
||||
<html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>MetaMask Loading</title>
|
||||
<style>
|
||||
#div-logo {
|
||||
@ -31,5 +35,11 @@
|
||||
<div id="div-logo">
|
||||
<img id="logo" src="./images/loginglogo.svg">
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// redirect to 404 after one minute
|
||||
setTimeout(() => {
|
||||
location.href = './404.html'
|
||||
}, 60000)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -29,7 +29,7 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
|
||||
const EdgeEncryptor = require('./edge-encryptor')
|
||||
const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code')
|
||||
const getObjStructure = require('./lib/getObjStructure')
|
||||
const ipfsContent = require('./lib/ipfsContent.js')
|
||||
const setupEnsIpfsResolver = require('./lib/ens-ipfs/setup')
|
||||
|
||||
const {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
@ -58,7 +58,6 @@ const isIE = !!document.documentMode
|
||||
// Edge 20+
|
||||
const isEdge = !isIE && !!window.StyleMedia
|
||||
|
||||
let ipfsHandle
|
||||
let popupIsOpen = false
|
||||
let notificationIsOpen = false
|
||||
const openMetamaskTabsIDs = {}
|
||||
@ -164,7 +163,6 @@ async function initialize () {
|
||||
const initLangCode = await getFirstPreferredLangCode()
|
||||
await setupController(initState, initLangCode)
|
||||
log.debug('MetaMask initialization complete.')
|
||||
ipfsHandle = ipfsContent(initState.NetworkController.provider)
|
||||
}
|
||||
|
||||
//
|
||||
@ -269,10 +267,8 @@ function setupController (initState, initLangCode) {
|
||||
})
|
||||
global.metamaskController = controller
|
||||
|
||||
controller.networkController.on('networkDidChange', () => {
|
||||
ipfsHandle && ipfsHandle.remove()
|
||||
ipfsHandle = ipfsContent(controller.networkController.providerStore.getState())
|
||||
})
|
||||
const provider = controller.provider
|
||||
setupEnsIpfsResolver({ provider })
|
||||
|
||||
// report failed transactions to Sentry
|
||||
controller.txController.on(`tx:status-update`, (txId, status) => {
|
||||
|
54
app/scripts/lib/ens-ipfs/resolver.js
Normal file
54
app/scripts/lib/ens-ipfs/resolver.js
Normal file
@ -0,0 +1,54 @@
|
||||
const namehash = require('eth-ens-namehash')
|
||||
const multihash = require('multihashes')
|
||||
const Eth = require('ethjs-query')
|
||||
const EthContract = require('ethjs-contract')
|
||||
const registrarAbi = require('./contracts/registrar')
|
||||
const resolverAbi = require('./contracts/resolver')
|
||||
|
||||
module.exports = resolveEnsToIpfsContentId
|
||||
|
||||
|
||||
async function resolveEnsToIpfsContentId ({ provider, name }) {
|
||||
const eth = new Eth(provider)
|
||||
const hash = namehash.hash(name)
|
||||
const contract = new EthContract(eth)
|
||||
// lookup registrar
|
||||
const chainId = Number.parseInt(await eth.net_version(), 10)
|
||||
const registrarAddress = getRegistrarForChainId(chainId)
|
||||
if (!registrarAddress) {
|
||||
throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`)
|
||||
}
|
||||
const Registrar = contract(registrarAbi).at(registrarAddress)
|
||||
// lookup resolver
|
||||
const resolverLookupResult = await Registrar.resolver(hash)
|
||||
const resolverAddress = resolverLookupResult[0]
|
||||
if (hexValueIsEmpty(resolverAddress)) {
|
||||
throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`)
|
||||
}
|
||||
const Resolver = contract(resolverAbi).at(resolverAddress)
|
||||
// lookup content id
|
||||
const contentLookupResult = await Resolver.content(hash)
|
||||
const contentHash = contentLookupResult[0]
|
||||
if (hexValueIsEmpty(contentHash)) {
|
||||
throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`)
|
||||
}
|
||||
const nonPrefixedHex = contentHash.slice(2)
|
||||
const buffer = multihash.fromHexString(nonPrefixedHex)
|
||||
const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256'))
|
||||
return contentId
|
||||
}
|
||||
|
||||
function hexValueIsEmpty(value) {
|
||||
return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value)
|
||||
}
|
||||
|
||||
function getRegistrarForChainId (chainId) {
|
||||
switch (chainId) {
|
||||
// mainnet
|
||||
case 1:
|
||||
return '0x314159265dd8dbb310642f98f50c066173c1259b'
|
||||
// ropsten
|
||||
case 3:
|
||||
return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
|
||||
}
|
||||
}
|
63
app/scripts/lib/ens-ipfs/setup.js
Normal file
63
app/scripts/lib/ens-ipfs/setup.js
Normal file
@ -0,0 +1,63 @@
|
||||
const urlUtil = require('url')
|
||||
const extension = require('extensionizer')
|
||||
const resolveEnsToIpfsContentId = require('./resolver.js')
|
||||
|
||||
const supportedTopLevelDomains = ['eth']
|
||||
|
||||
module.exports = setupEnsIpfsResolver
|
||||
|
||||
function setupEnsIpfsResolver({ provider }) {
|
||||
|
||||
// install listener
|
||||
const urlPatterns = supportedTopLevelDomains.map(tld => `*://*.${tld}/*`)
|
||||
extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, { urls: urlPatterns })
|
||||
|
||||
// return api object
|
||||
return {
|
||||
// uninstall listener
|
||||
remove () {
|
||||
extension.webRequest.onErrorOccurred.removeListener(webRequestDidFail)
|
||||
},
|
||||
}
|
||||
|
||||
async function webRequestDidFail (details) {
|
||||
const { tabId, url } = details
|
||||
// ignore requests that are not associated with tabs
|
||||
if (tabId === -1) return
|
||||
// parse ens name
|
||||
const urlData = urlUtil.parse(url)
|
||||
const { hostname: name, path, search } = urlData
|
||||
const domainParts = name.split('.')
|
||||
const topLevelDomain = domainParts[domainParts.length - 1]
|
||||
// if unsupported TLD, abort
|
||||
if (!supportedTopLevelDomains.includes(topLevelDomain)) return
|
||||
// otherwise attempt resolve
|
||||
attemptResolve({ tabId, name, path, search })
|
||||
}
|
||||
|
||||
async function attemptResolve({ tabId, name, path, search }) {
|
||||
extension.tabs.update(tabId, { url: `loading.html` })
|
||||
try {
|
||||
const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name })
|
||||
let url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}`
|
||||
try {
|
||||
// check if ipfs gateway has result
|
||||
const response = await fetch(url, { method: 'HEAD' })
|
||||
// if failure, redirect to 404 page
|
||||
if (response.status !== 200) {
|
||||
extension.tabs.update(tabId, { url: '404.html' })
|
||||
return
|
||||
}
|
||||
// otherwise redirect to the correct page
|
||||
extension.tabs.update(tabId, { url })
|
||||
} catch (err) {
|
||||
console.warn(err)
|
||||
// if HEAD fetch failed, redirect so user can see relevant error page
|
||||
extension.tabs.update(tabId, { url })
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err)
|
||||
extension.tabs.update(tabId, { url: `error.html?name=${name}` })
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
const extension = require('extensionizer')
|
||||
const resolver = require('./resolver.js')
|
||||
|
||||
module.exports = function (provider) {
|
||||
function ipfsContent (details) {
|
||||
const name = details.url.substring(7, details.url.length - 1)
|
||||
let clearTime = null
|
||||
if (/^.+\.eth$/.test(name) === false) return
|
||||
|
||||
extension.tabs.query({active: true}, tab => {
|
||||
extension.tabs.update(tab.id, { url: 'loading.html' })
|
||||
|
||||
clearTime = setTimeout(() => {
|
||||
return extension.tabs.update(tab.id, { url: '404.html' })
|
||||
}, 60000)
|
||||
|
||||
resolver.resolve(name, provider).then(ipfsHash => {
|
||||
clearTimeout(clearTime)
|
||||
let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash
|
||||
return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => {
|
||||
if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' })
|
||||
extension.tabs.update(tab.id, { url: url })
|
||||
})
|
||||
.catch(err => {
|
||||
url = 'https://ipfs.infura.io/ipfs/' + ipfsHash
|
||||
extension.tabs.update(tab.id, {url: url})
|
||||
return err
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
clearTimeout(clearTime)
|
||||
const url = err === 'unsupport' ? 'unsupport' : 'error'
|
||||
extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`})
|
||||
})
|
||||
})
|
||||
return { cancel: true }
|
||||
}
|
||||
|
||||
extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/'], types: ['main_frame']})
|
||||
|
||||
return {
|
||||
remove () {
|
||||
extension.webRequest.onErrorOccurred.removeListener(ipfsContent)
|
||||
},
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
const namehash = require('eth-ens-namehash')
|
||||
const multihash = require('multihashes')
|
||||
const HttpProvider = require('ethjs-provider-http')
|
||||
const Eth = require('ethjs-query')
|
||||
const EthContract = require('ethjs-contract')
|
||||
const registrarAbi = require('./contracts/registrar')
|
||||
const resolverAbi = require('./contracts/resolver')
|
||||
|
||||
function ens (name, provider) {
|
||||
const eth = new Eth(new HttpProvider(getProvider(provider.type)))
|
||||
const hash = namehash.hash(name)
|
||||
const contract = new EthContract(eth)
|
||||
const Registrar = contract(registrarAbi).at(getRegistrar(provider.type))
|
||||
return new Promise((resolve, reject) => {
|
||||
if (provider.type === 'mainnet' || provider.type === 'ropsten') {
|
||||
Registrar.resolver(hash).then((address) => {
|
||||
if (address === '0x0000000000000000000000000000000000000000') {
|
||||
reject(null)
|
||||
} else {
|
||||
const Resolver = contract(resolverAbi).at(address['0'])
|
||||
return Resolver.content(hash)
|
||||
}
|
||||
}).then((contentHash) => {
|
||||
if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null)
|
||||
if (contentHash.ret !== '0x') {
|
||||
const hex = contentHash['0'].substring(2)
|
||||
const buf = multihash.fromHexString(hex)
|
||||
resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256')))
|
||||
} else {
|
||||
reject(null)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return reject('unsupport')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getProvider (type) {
|
||||
switch (type) {
|
||||
case 'mainnet':
|
||||
return 'https://mainnet.infura.io/'
|
||||
case 'ropsten':
|
||||
return 'https://ropsten.infura.io/'
|
||||
default:
|
||||
return 'http://localhost:8545/'
|
||||
}
|
||||
}
|
||||
|
||||
function getRegistrar (type) {
|
||||
switch (type) {
|
||||
case 'mainnet':
|
||||
return '0x314159265dd8dbb310642f98f50c066173c1259b'
|
||||
case 'ropsten':
|
||||
return '0x112234455c3a32fd11230c42e7bccd4a84e02010'
|
||||
default:
|
||||
return '0x0000000000000000000000000000000000000000'
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.resolve = function (name, provider) {
|
||||
const path = name.split('.')
|
||||
const topLevelDomain = path[path.length - 1]
|
||||
if (topLevelDomain === 'eth' || topLevelDomain === 'test') {
|
||||
return ens(name, provider)
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
reject(null)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user