UNPKG

remix-ide

Version:
124 lines (110 loc) 5.01 kB
const ethutil = require('ethereumjs-util') import * as packageJson from '../../../package.json' import { Plugin } from '@remixproject/engine' import { compile } from './compiler-helpers' import globalRegistry from '../../global/registry' import remixLib from 'remix-lib' const profile = { name: 'fetchAndCompile', methods: ['resolve'], version: packageJson.version } export default class FetchAndCompile extends Plugin { constructor () { super(profile) this.unresolvedAddresses = [] this.sourceVerifierNetWork = ['Main', 'Rinkeby', 'Ropsten', 'Goerli'] } /** * Fetch compiliation metadata from source-Verify from a given @arg contractAddress - https://github.com/ethereum/source-verify * Put the artifacts in the file explorer * Compile the code using Solidity compiler * Returns compilation data * * @param {string} contractAddress - Address of the contrac to resolve * @param {string} compilersartefacts - Object containing a mapping of compilation results (byContractAddress and __last) * @return {CompilerAbstract} - compilation data targeting the given @arg contractAddress */ async resolve (contractAddress, targetPath, web3) { contractAddress = ethutil.toChecksumAddress(contractAddress) const compilersartefacts = globalRegistry.get('compilersartefacts').api const localCompilation = () => compilersartefacts.get('__last') ? compilersartefacts.get('__last') : null const resolved = compilersartefacts.get(contractAddress) if (resolved) return resolved if (this.unresolvedAddresses.includes(contractAddress)) return localCompilation() // sometimes when doing an internal call, the only available artifact is the Solidity interface. // resolving addresses of internal call would allow to step over the source code, even if the declaration was made using an Interface. let network try { network = await this.call('network', 'detectNetwork') } catch (e) { return localCompilation() } if (!network) return localCompilation() if (!this.sourceVerifierNetWork.includes(network.name)) return localCompilation() // check if the contract if part of the local compilation result const codeAtAddress = await web3.eth.getCode(contractAddress) const compilation = localCompilation() if (compilation) { let found = false compilation.visitContracts((contract) => { found = remixLib.util.compareByteCode('0x' + contract.object.evm.deployedBytecode.object, codeAtAddress) return found }) if (found) { compilersartefacts.addResolvedContract(contractAddress, compilation) setTimeout(_ => this.emit('usingLocalCompilation', contractAddress), 0) return compilation } } let name = network.name.toLowerCase() name === 'main' ? 'mainnet' : name // source-verifier api expect "mainnet" and not "main" let data try { data = await this.call('source-verification', 'fetchByNetwork', contractAddress, name.toLowerCase()) } catch (e) { setTimeout(_ => this.emit('notFound', contractAddress), 0) // plugin framework returns a time out error although it actually didn't find the source... this.unresolvedAddresses.push(contractAddress) return localCompilation() } if (!data || !data.metadata) { setTimeout(_ => this.emit('notFound', contractAddress), 0) this.unresolvedAddresses.push(contractAddress) return localCompilation() } // set the solidity contract code using metadata await this.call('fileManager', 'setFile', `${targetPath}/${name}/${contractAddress}/metadata.json`, JSON.stringify(data.metadata, null, '\t')) let compilationTargets = {} for (let file in data.metadata.sources) { const urls = data.metadata.sources[file].urls for (let url of urls) { if (url.includes('ipfs')) { let stdUrl = `ipfs://${url.split('/')[2]}` const source = await this.call('contentImport', 'resolve', stdUrl) file = file.replace('browser/', '') // should be fixed in the remix IDE end. const path = `${targetPath}/${name}/${contractAddress}/${file}` await this.call('fileManager', 'setFile', path, source.content) compilationTargets[path] = { content: source.content } break } } } // compile const settings = { version: data.metadata.compiler.version, language: data.metadata.language, evmVersion: data.metadata.settings.evmVersion, optimize: data.metadata.settings.optimizer.enabled } try { setTimeout(_ => this.emit('compiling', settings), 0) const compData = await compile(compilationTargets, settings) compilersartefacts.addResolvedContract(contractAddress, compData) return compData } catch (e) { this.unresolvedAddresses.push(contractAddress) setTimeout(_ => this.emit('compilationFailed'), 0) return localCompilation() } } }