diff --git a/apps/remix-ide/src/app/tabs/compile-tab.js b/apps/remix-ide/src/app/tabs/compile-tab.js index af0febab57..4882158e86 100644 --- a/apps/remix-ide/src/app/tabs/compile-tab.js +++ b/apps/remix-ide/src/app/tabs/compile-tab.js @@ -1,7 +1,7 @@ /* global */ import React from 'react' // eslint-disable-line import ReactDOM from 'react-dom' -import { SolidityCompiler, CompileTab as CompileTabLogic } from '@remix-ui/solidity-compiler' // eslint-disable-line +import { SolidityCompiler, CompileTab as CompileTabLogic, compile, parseContracts } from '@remix-ui/solidity-compiler' // eslint-disable-line import { ViewPlugin } from '@remixproject/engine-web' import * as packageJson from '../../../../../package.json' import publishToStorage from '../../publishToStorage' @@ -11,7 +11,6 @@ const EventEmitter = require('events') const $ = require('jquery') const yo = require('yo-yo') var QueryParams = require('../../lib/query-params') -const parseContracts = require('./compileTab/contractParser') const addTooltip = require('../ui/tooltip') const globalRegistry = require('../../global/registry') diff --git a/libs/remix-ui/solidity-compiler/src/index.ts b/libs/remix-ui/solidity-compiler/src/index.ts index 576801a333..317c79cefa 100644 --- a/libs/remix-ui/solidity-compiler/src/index.ts +++ b/libs/remix-ui/solidity-compiler/src/index.ts @@ -1,2 +1,2 @@ export * from './lib/solidity-compiler' -export * from './lib/compileTabLogic' +export * from './lib/logic' diff --git a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx index 060ede455a..04638c06e7 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef, useReducer } from 'react' // eslint import semver from 'semver' import { CompilerContainerProps } from './types' import * as helper from '../../../../../apps/remix-ide/src/lib/helper' -import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '../../../../../apps/remix-ide/src/app/compiler/compiler-utils' // eslint-disable-line +import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from './logic/compiler-utils' // eslint-disable-line import { compilerReducer, compilerInitialState } from './reducers/compiler' import { resetCompilerMode, resetEditorMode, listenToEvents } from './actions/compiler' @@ -120,9 +120,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => { let selectedVersion, allVersionsWasm, isURL let allVersions = [{ path: 'builtin', longVersion: 'latest local version - 0.7.4' }] // fetch normal builds - const binRes = await promisedMiniXhr(`${baseURLBin}/list.json`) + const binRes: any = await promisedMiniXhr(`${baseURLBin}/list.json`) // fetch wasm builds - const wasmRes = await promisedMiniXhr(`${baseURLWasm}/list.json`) + const wasmRes: any = await promisedMiniXhr(`${baseURLWasm}/list.json`) if (binRes.event.type === 'error' && wasmRes.event.type === 'error') { selectedVersion = 'builtin' return callback(allVersions, selectedVersion) diff --git a/libs/remix-ui/solidity-compiler/src/lib/compileTabLogic.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts similarity index 98% rename from libs/remix-ui/solidity-compiler/src/lib/compileTabLogic.ts rename to libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts index 1e4a7adb6c..5541654ac7 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compileTabLogic.ts +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts @@ -1,6 +1,6 @@ import { Plugin } from '@remixproject/engine' -const packageJson = require('../../../../../package.json') +const packageJson = require('../../../../../../package.json') const Compiler = require('@remix-project/remix-solidity').Compiler const EventEmitter = require('events') const profile = { diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-abstract.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-abstract.ts new file mode 100644 index 0000000000..f0c4fc3ad1 --- /dev/null +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-abstract.ts @@ -0,0 +1,51 @@ +'use strict' +import * as remixLib from '@remix-project/remix-lib' + +const txHelper = remixLib.execution.txHelper + +export class CompilerAbstract { + public languageversion: string + public data: Record + public source: Record + + constructor (languageversion, data, source) { + this.languageversion = languageversion + this.data = data + this.source = source // source code + } + + getContracts () { + return this.data.contracts + } + + getContract (name) { + return txHelper.getContract(name, this.data.contracts) + } + + visitContracts (calllback) { + return txHelper.visitContracts(this.data.contracts, calllback) + } + + getData () { + return this.data + } + + getAsts () { + return this.data.sources // ast + } + + getSourceName (fileIndex) { + if (this.data && this.data.sources) { + return Object.keys(this.data.sources)[fileIndex] + } else if (Object.keys(this.source.sources).length === 1) { + // if we don't have ast, we return the only one filename present. + const sourcesArray = Object.keys(this.source.sources) + return sourcesArray[0] + } + return null + } + + getSourceCode () { + return this.source + } +} diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-helpers.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-helpers.ts new file mode 100644 index 0000000000..757cf2b897 --- /dev/null +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-helpers.ts @@ -0,0 +1,22 @@ +'use strict' +import { canUseWorker, urlFromVersion } from './compiler-utils' +import { Compiler } from '@remix-project/remix-solidity' +import { CompilerAbstract } from './compiler-abstract' + +export const compile = async (compilationTargets, settings, contentResolverCallback) => { + const res = await (() => { + return new Promise((resolve, reject) => { + const compiler = new Compiler(contentResolverCallback) + compiler.set('evmVersion', settings.evmVersion) + compiler.set('optimize', settings.optimize) + compiler.set('language', settings.language) + compiler.set('runs', settings.runs) + compiler.loadVersion(canUseWorker(settings.version), urlFromVersion(settings.version)) + compiler.event.register('compilationFinished', (success, compilationData, source) => { + resolve(new CompilerAbstract(settings.version, compilationData, source)) + }) + compiler.event.register('compilerLoaded', _ => compiler.compile(compilationTargets, '')) + }) + })() + return res +} diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-utils.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-utils.ts new file mode 100644 index 0000000000..bd3fc3a016 --- /dev/null +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/compiler-utils.ts @@ -0,0 +1,46 @@ +const semver = require('semver') +const minixhr = require('minixhr') +/* global Worker */ + +export const baseURLBin = 'https://binaries.soliditylang.org/bin' +export const baseURLWasm = 'https://binaries.soliditylang.org/wasm' + +export const pathToURL = {} + +/** + * Retrieves the URL of the given compiler version + * @param version is the version of compiler with or without 'soljson-v' prefix and .js postfix + */ +export function urlFromVersion (version) { + if (!version.startsWith('soljson-v')) version = 'soljson-v' + version + if (!version.endsWith('.js')) version = version + '.js' + return `${pathToURL[version]}/${version}` +} + +/** + * Checks if the worker can be used to load a compiler. + * checks a compiler whitelist, browser support and OS. + */ +export function canUseWorker (selectedVersion) { + const version = semver.coerce(selectedVersion) + const isNightly = selectedVersion.includes('nightly') + return browserSupportWorker() && ( + // All compiler versions (including nightlies) after 0.6.3 are wasm compiled + semver.gt(version, '0.6.3') || + // Only releases are wasm compiled starting with 0.3.6 + (semver.gte(version, '0.3.6') && !isNightly) + ) +} + +function browserSupportWorker () { + return document.location.protocol !== 'file:' && Worker !== undefined +} + +// returns a promise for minixhr +export function promisedMiniXhr (url) { + return new Promise((resolve, reject) => { + minixhr(url, (json, event) => { + resolve({ json, event }) + }) + }) +} diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/contract-parser.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/contract-parser.ts new file mode 100644 index 0000000000..3894b01a9d --- /dev/null +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/contract-parser.ts @@ -0,0 +1,119 @@ +'use strict' +import * as solcTranslate from 'solc/translate' +import * as remixLib from '@remix-project/remix-lib' + +const txHelper = remixLib.execution.txHelper + +export function parseContracts (contractName, contract, source) { + const detail: Record = {} + + detail.name = contractName + detail.metadata = contract.metadata + if (contract.evm.bytecode.object) { + detail.bytecode = contract.evm.bytecode.object + } + + detail.abi = contract.abi + + if (contract.evm.bytecode.object) { + detail.bytecode = contract.evm.bytecode + detail.web3Deploy = gethDeploy(contractName.toLowerCase(), contract.abi, contract.evm.bytecode.object) + + detail.metadataHash = retrieveMetadataHash(contract.evm.bytecode.object) + if (detail.metadataHash) { + detail.swarmLocation = 'bzzr://' + detail.metadataHash + } + } + + detail.functionHashes = {} + for (const fun in contract.evm.methodIdentifiers) { + detail.functionHashes[contract.evm.methodIdentifiers[fun]] = fun + } + + detail.gasEstimates = formatGasEstimates(contract.evm.gasEstimates) + + detail.devdoc = contract.devdoc + detail.userdoc = contract.userdoc + + if (contract.evm.deployedBytecode && contract.evm.deployedBytecode.object.length > 0) { + detail['Runtime Bytecode'] = contract.evm.deployedBytecode + } + + if (source && contract.assembly !== null) { + detail.Assembly = solcTranslate.prettyPrintLegacyAssemblyJSON(contract.evm.legacyAssembly, source.content) + } + + return detail +} + +const retrieveMetadataHash = function (bytecode) { + var match = /a165627a7a72305820([0-9a-f]{64})0029$/.exec(bytecode) + if (!match) { + match = /a265627a7a72305820([0-9a-f]{64})6c6578706572696d656e74616cf50037$/.exec(bytecode) + } + if (match) { + return match[1] + } +} + +const gethDeploy = function (contractName, jsonInterface, bytecode) { + let code = '' + const funABI = txHelper.getConstructorInterface(jsonInterface) + + funABI.inputs.forEach(function (inp) { + code += 'var ' + inp.name + ' = /* var of type ' + inp.type + ' here */ ;\n' + }) + + contractName = contractName.replace(/[:./]/g, '_') + code += 'var ' + contractName + 'Contract = new web3.eth.Contract(' + JSON.stringify(jsonInterface).replace('\n', '') + ');' + + '\nvar ' + contractName + ' = ' + contractName + 'Contract.deploy({' + + "\n data: '0x" + bytecode + "', " + + '\n arguments: [' + + funABI.inputs.forEach(function (inp) { + code += '\n ' + inp.name + ',' + }) + + code += '\n ]' + + '\n}).send({' + + '\n from: web3.eth.accounts[0], ' + + "\n gas: '4700000'" + + '\n }, function (e, contract){' + + '\n console.log(e, contract);' + + "\n if (typeof contract.address !== 'undefined') {" + + "\n console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);" + + '\n }' + + '\n })' + + return code +} + +const formatGasEstimates = function (data) { + if (!data) return {} + if (data.creation === undefined && data.external === undefined && data.internal === undefined) return {} + + const gasToText = function (g) { + return g === null ? 'unknown' : g + } + + const ret: Record = {} + let fun + if ('creation' in data) { + ret.Creation = data.creation + } + + if ('external' in data) { + ret.External = {} + for (fun in data.external) { + ret.External[fun] = gasToText(data.external[fun]) + } + } + + if ('internal' in data) { + ret.Internal = {} + for (fun in data.internal) { + ret.Internal[fun] = gasToText(data.internal[fun]) + } + } + return ret +} diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/index.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/index.ts new file mode 100644 index 0000000000..8e8fb31572 --- /dev/null +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/index.ts @@ -0,0 +1,5 @@ +export * from './compileTabLogic' +export * from './compiler-abstract' +export * from './compiler-helpers' +export * from './compiler-utils' +export * from './contract-parser' \ No newline at end of file diff --git a/libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx b/libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx index ab2f4e167b..2794f7bd21 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' // eslint-disable-line +import React, { useState } from 'react' // eslint-disable-line import { SolidityCompilerProps } from './types' import { CompilerContainer } from './compiler-container' // eslint-disable-line import { ContractSelection } from './contract-selection'