From 3c38fdcc50089c45da4e918378f5fc9d4f751738 Mon Sep 17 00:00:00 2001 From: ioedeveloper Date: Sat, 26 Jun 2021 12:01:36 +0100 Subject: [PATCH] Fixed publish to storage. --- apps/remix-ide/src/app/tabs/compile-tab.js | 154 +++++++++--------- .../src/lib/publish-to-storage.tsx | 97 ++++------- .../src/lib/publishOnSwarm.tsx | 45 ++--- .../src/lib/publishToIPFS.tsx | 35 ++-- .../publish-to-storage/src/lib/types/index.ts | 3 +- .../src/lib/compiler-container.tsx | 14 +- .../src/lib/contract-selection.tsx | 138 ++++++++++++---- .../src/lib/solidity-compiler.tsx | 6 +- .../solidity-compiler/src/lib/types/index.ts | 13 +- 9 files changed, 294 insertions(+), 211 deletions(-) diff --git a/apps/remix-ide/src/app/tabs/compile-tab.js b/apps/remix-ide/src/app/tabs/compile-tab.js index d277a02057..775b251872 100644 --- a/apps/remix-ide/src/app/tabs/compile-tab.js +++ b/apps/remix-ide/src/app/tabs/compile-tab.js @@ -13,9 +13,6 @@ const $ = require('jquery') const yo = require('yo-yo') const copy = require('copy-text-to-clipboard') var QueryParams = require('../../lib/query-params') -const TreeView = require('../ui/TreeView') -const modalDialog = require('../ui/modaldialog') -const copyToClipboard = require('../ui/copy-to-clipboard') const modalDialogCustom = require('../ui/modal-dialog-custom') const parseContracts = require('./compileTab/contractParser') const addTooltip = require('../ui/tooltip') @@ -76,6 +73,7 @@ class CompileTab extends ViewPlugin { ) this.compiler = this.compileTabLogic.compiler this.compileTabLogic.init() + this.contractMap = {} this.el = document.createElement('div') this.el.setAttribute('id', 'compileTabView') @@ -150,7 +148,7 @@ class CompileTab extends ViewPlugin { this.fileManager.events.on('noFileSelected', this.data.eventHandlers.onNoFileSelected) this.data.eventHandlers.onCompilationFinished = (success, data, source) => { - this._view.errorContainer.appendChild(yo``) + // this._view.errorContainer.appendChild(yo``) if (success) { // forwarding the event to the appManager infra this.emit('compilationFinished', source.target, source, 'soljson', data) @@ -175,10 +173,10 @@ class CompileTab extends ViewPlugin { this.emit('statusChanged', { key: count, title: `compilation failed with ${count} error${count.length > 1 ? 's' : ''}`, type: 'error' }) } // Update contract Selection - const contractMap = {} - if (success) this.compiler.visitContracts((contract) => { contractMap[contract.name] = contract }) - const contractSelection = this.contractSelection(contractMap) - yo.update(this._view.contractSelection, contractSelection) + this.contractMap = {} + if (success) this.compiler.visitContracts((contract) => { this.contractMap[contract.name] = contract }) + // const contractSelection = this.contractSelection(contractMap) + // yo.update(this._view.contractSelection, contractSelection) if (data.error) { // this.renderer.error( @@ -224,11 +222,15 @@ class CompileTab extends ViewPlugin { // ctrl+s or command+s if ((e.metaKey || e.ctrlKey) && e.keyCode === 83) { e.preventDefault() - this.compileTabLogic.runCompiler(this.compilerContainer.hhCompilation) + this.compileTabLogic.runCompiler(this.hhCompilation) } }) } + setHardHatCompilation (value) { + this.hhCompilation = value + } + getCompilationResult () { return this.compileTabLogic.compiler.state.lastCompilationResult } @@ -388,73 +390,73 @@ class CompileTab extends ViewPlugin { this.selectedContract = contractName } - details () { - const help = { - Assembly: 'Assembly opcodes describing the contract including corresponding solidity source code', - Opcodes: 'Assembly opcodes describing the contract', - 'Runtime Bytecode': 'Bytecode storing the state and being executed during normal contract call', - bytecode: 'Bytecode being executed during contract creation', - functionHashes: 'List of declared function and their corresponding hash', - gasEstimates: 'Gas estimation for each function call', - metadata: 'Contains all informations related to the compilation', - metadataHash: 'Hash representing all metadata information', - abi: 'ABI: describing all the functions (input/output params, scope, ...)', - name: 'Name of the compiled contract', - swarmLocation: 'Swarm url where all metadata information can be found (contract needs to be published first)', - web3Deploy: 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract' - } - if (!this.selectedContract) throw new Error('No contract compiled yet') - const contractProperties = this.contractsDetails[this.selectedContract] - const log = yo`
` - Object.keys(contractProperties).map(propertyName => { - const copyDetails = yo`${copyToClipboard(() => contractProperties[propertyName])}` - const questionMark = yo`` - log.appendChild(yo`
-
${propertyName} ${copyDetails} ${questionMark}
- ${this.insertValue(contractProperties, propertyName)} -
`) - }) - modalDialog(this.selectedContract, log, { label: '' }, { label: 'Close' }) - } + // details () { + // const help = { + // Assembly: 'Assembly opcodes describing the contract including corresponding solidity source code', + // Opcodes: 'Assembly opcodes describing the contract', + // 'Runtime Bytecode': 'Bytecode storing the state and being executed during normal contract call', + // bytecode: 'Bytecode being executed during contract creation', + // functionHashes: 'List of declared function and their corresponding hash', + // gasEstimates: 'Gas estimation for each function call', + // metadata: 'Contains all informations related to the compilation', + // metadataHash: 'Hash representing all metadata information', + // abi: 'ABI: describing all the functions (input/output params, scope, ...)', + // name: 'Name of the compiled contract', + // swarmLocation: 'Swarm url where all metadata information can be found (contract needs to be published first)', + // web3Deploy: 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract' + // } + // if (!this.selectedContract) throw new Error('No contract compiled yet') + // const contractProperties = this.contractsDetails[this.selectedContract] + // const log = yo`
` + // Object.keys(contractProperties).map(propertyName => { + // const copyDetails = yo`${copyToClipboard(() => contractProperties[propertyName])}` + // const questionMark = yo`` + // log.appendChild(yo`
+ //
${propertyName} ${copyDetails} ${questionMark}
+ // ${this.insertValue(contractProperties, propertyName)} + //
`) + // }) + // modalDialog(this.selectedContract, log, { label: '' }, { label: 'Close' }) + // } - insertValue (details, propertyName) { - var node - if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') { - node = yo`
${details[propertyName]}
` - } else if (propertyName === 'abi' || propertyName === 'metadata') { - const treeView = new TreeView({ - extractData: function (item, parent, key) { - var ret = {} - if (item instanceof Array) { - ret.children = item.map((item, index) => ({ key: index, value: item })) - ret.self = '' - } else if (item instanceof Object) { - ret.children = Object.keys(item).map((key) => ({ key: key, value: item[key] })) - ret.self = '' - } else { - ret.self = item - ret.children = [] - } - return ret - } - }) - if (details[propertyName] !== '') { - try { - node = yo` -
- ${treeView.render(typeof details[propertyName] === 'object' ? details[propertyName] : JSON.parse(details[propertyName]))} -
` // catch in case the parsing fails. - } catch (e) { - node = yo`
Unable to display "${propertyName}": ${e.message}
` - } - } else { - node = yo`
-
` - } - } else { - node = yo`
${JSON.stringify(details[propertyName], null, 4)}
` - } - return yo`
${node || ''}
` - } + // insertValue (details, propertyName) { + // var node + // if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') { + // node = yo`
${details[propertyName]}
` + // } else if (propertyName === 'abi' || propertyName === 'metadata') { + // const treeView = new TreeView({ + // extractData: function (item, parent, key) { + // var ret = {} + // if (item instanceof Array) { + // ret.children = item.map((item, index) => ({ key: index, value: item })) + // ret.self = '' + // } else if (item instanceof Object) { + // ret.children = Object.keys(item).map((key) => ({ key: key, value: item[key] })) + // ret.self = '' + // } else { + // ret.self = item + // ret.children = [] + // } + // return ret + // } + // }) + // if (details[propertyName] !== '') { + // try { + // node = yo` + //
+ // ${treeView.render(typeof details[propertyName] === 'object' ? details[propertyName] : JSON.parse(details[propertyName]))} + //
` // catch in case the parsing fails. + // } catch (e) { + // node = yo`
Unable to display "${propertyName}": ${e.message}
` + // } + // } else { + // node = yo`
-
` + // } + // } else { + // node = yo`
${JSON.stringify(details[propertyName], null, 4)}
` + // } + // return yo`
${node || ''}
` + // } getContractProperty (property) { if (!this.selectedContract) throw new Error('No contract compiled yet') @@ -505,6 +507,8 @@ class CompileTab extends ViewPlugin { compileTabLogic={this.compileTabLogic} compiledFileName={this.currentFile} contractsDetails={this.contractsDetails} + setHardHatCompilation={this.setHardHatCompilation} + contractMap={this.contractMap} /> , this.el) } diff --git a/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx b/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx index 04cc7f8c3b..f22aeff061 100644 --- a/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx +++ b/libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx @@ -4,107 +4,81 @@ import { RemixUiPublishToStorageProps } from './types' import { publishToIPFS } from './publishToIPFS' import { publishToSwarm } from './publishOnSwarm' -import './css/publish-to-storage.css' - export const PublishToStorage = (props: RemixUiPublishToStorageProps) => { - const { storage, fileProvider, fileManager, contract } = props + const { storage, fileProvider, fileManager, contract, resetStorage } = props const [state, setState] = useState({ modal: { title: '', message: null, hide: true, - ok: { - label: '', - fn: null - }, - cancel: { - label: '', - fn: null - } + okLabel: '', + okFn: null, + cancelLabel: '', + cancelFn: null } }) useEffect(() => { const storageService = async () => { if ((contract.metadata === undefined || contract.metadata.length === 0)) { - modal('Publish To Storage', 'This contract may be abstract, may not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.', { - label: 'OK', - fn: () => {} - }, null) + modal('Publish To Storage', 'This contract may be abstract, may not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') } else { if (storage === 'swarm') { try { const result = await publishToSwarm(contract, fileManager) - modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded), { - label: 'OK', - fn: () => {} - }, null) + modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded)) // triggered each time there's a new verified publish (means hash correspond) fileProvider.addExternal('swarm/' + result.item.hash, result.item.content) } catch (err) { try { err = JSON.stringify(err) } catch (e) {} - modal(`Swarm Publish Failed`, publishMessageFailed(storage, err), { - label: 'OK', - fn: () => {} - }, null) + modal(`Swarm Publish Failed`, publishMessageFailed(storage, err)) } } else { try { const result = await publishToIPFS(contract, fileManager) - modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded), { - label: 'OK', - fn: () => {} - }, null) + modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded)) // triggered each time there's a new verified publish (means hash correspond) - fileProvider.addExternal('swarm/' + result.item.hash, result.item.content) + fileProvider.addExternal('ipfs/' + result.item.hash, result.item.content) } catch (err) { - modal(`Swarm Publish Failed`, publishMessageFailed(storage, err), { - label: 'OK', - fn: () => {} - }, null) + modal(`IPFS Publish Failed`, publishMessageFailed(storage, err)) + } } } } - } - if (contract) { - storageService() - } -}, [contract]) + if (storage) { + storageService() + } + }, [storage]) - const publishMessage = (uploaded) => { - return ( - Metadata of {contract.name.toLowerCase()} was published successfully.
-
-          
- { uploaded.map((value) => { -
{ value.filename } :
{ value.output.url }
- }) } -
-
-
- ) - } + const publishMessage = (uploaded) => ( + Metadata of {contract.name.toLowerCase()} was published successfully.
+
+        
+ { uploaded.map((value, index) =>
{ value.filename } :
{ value.output.url }
) } +
+
+
+ ) - const publishMessageFailed = (storage, err) => { - return ( - Failed to publish metadata file to { storage }, please check the { storage } gateways is available.
- {err} -
- ) - } + const publishMessageFailed = (storage, err) => ( + Failed to publish metadata file to { storage }, please check the { storage } gateways is available.
+ {err} +
+ ) const handleHideModal = () => { setState(prevState => { return { ...prevState, modal: { ...prevState.modal, hide: true, message: null } } }) + resetStorage() } - const modal = async (title: string, message: string | JSX.Element, ok: { label: string, fn: () => void }, cancel: { label: string, fn: () => void }) => { + const modal = async (title: string, message: string | JSX.Element) => { await setState(prevState => { return { ...prevState, @@ -113,9 +87,6 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => { hide: false, message, title, - ok, - cancel, - handleHide: handleHideModal } } }) @@ -127,8 +98,8 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => { title={ state.modal.title } message={ state.modal.message } hide={ state.modal.hide } - ok={ state.modal.ok } - cancel={ state.modal.cancel } + okLabel='OK' + okFn={() => {}} handleHide={ handleHideModal }> { (typeof state.modal.message !== 'string') && state.modal.message } diff --git a/libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx b/libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx index 2521b90ea3..81be4e1662 100644 --- a/libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx +++ b/libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx @@ -6,7 +6,7 @@ export const publishToSwarm = async (contract, fileManager) => { // gather list of files to publish const sources = [] let metadata - let item = null + let item = { content: null, hash: null } const uploaded = [] try { @@ -52,8 +52,10 @@ export const publishToSwarm = async (contract, fileManager) => { })) // publish the list of sources in order, fail if any failed - await Promise.all(sources.map((item) => { - swarmVerifiedPublish(item.content, item.hash, (error, result) => { + await Promise.all(sources.map(async (item) => { + try { + const result = await swarmVerifiedPublish(item.content, item.hash) + try { item.hash = result.url.match('bzz-raw://(.+)')[1] } catch (e) { @@ -63,40 +65,45 @@ export const publishToSwarm = async (contract, fileManager) => { uploaded.push(item) // TODO this is a fix cause Solidity metadata does not contain the right swarm hash (poc 0.3) metadata.sources[item.filename].urls[0] = result.url - }) + } catch (error) { + throw new Error(error) + } })) const metadataContent = JSON.stringify(metadata) + try { + const result = await swarmVerifiedPublish(metadataContent, '') - swarmVerifiedPublish(metadataContent, '', (error, result) => { try { contract.metadataHash = result.url.match('bzz-raw://(.+)')[1] } catch (e) { contract.metadataHash = ' - metadata.json' } - if (!error) { - item.content = metadataContent - item.hash = contract.metadataHash - } + item.content = metadataContent + item.hash = contract.metadataHash uploaded.push({ content: contract.metadata, hash: contract.metadataHash, filename: 'metadata.json', output: result }) - }) + } catch (error) { + throw new Error(error) + } return { uploaded, item } } -const swarmVerifiedPublish = (content, expectedHash, cb) => { - swarmgw.put(content, function (err, ret) { - if (err) { - cb(err) - } else if (expectedHash && ret !== expectedHash) { - cb(null, { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'bzz-raw://' + ret, hash: ret }) - } else { - cb(null, { message: 'ok', url: 'bzz-raw://' + ret, hash: ret }) - } +const swarmVerifiedPublish = async (content, expectedHash): Promise> => { + return new Promise((resolve, reject) => { + swarmgw.put(content, function (err, ret) { + if (err) { + reject(err) + } else if (expectedHash && ret !== expectedHash) { + resolve({ message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'bzz-raw://' + ret, hash: ret }) + } else { + resolve({ message: 'ok', url: 'bzz-raw://' + ret, hash: ret }) + } + }) }) } diff --git a/libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx b/libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx index 6f09279b11..4d5e53c098 100644 --- a/libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx +++ b/libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx @@ -10,7 +10,7 @@ export const publishToIPFS = async (contract, fileManager) => { // gather list of files to publish const sources = [] let metadata - let item = null + let item = { content: null, hash: null } const uploaded = [] try { @@ -55,8 +55,10 @@ export const publishToIPFS = async (contract, fileManager) => { }) })) // publish the list of sources in order, fail if any failed - await Promise.all(sources.map(item => { - ipfsVerifiedPublish(item.content, item.hash, (error, result) => { + await Promise.all(sources.map(async (item) => { + try { + const result = await ipfsVerifiedPublish(item.content, item.hash) + try { item.hash = result.url.match('dweb:/ipfs/(.+)')[1] } catch (e) { @@ -64,41 +66,46 @@ export const publishToIPFS = async (contract, fileManager) => { } item.output = result uploaded.push(item) - }) + } catch (error) { + throw new Error(error) + } })) const metadataContent = JSON.stringify(metadata) - ipfsVerifiedPublish(metadataContent, '', (error, result) => { + try { + const result = await ipfsVerifiedPublish(metadataContent, '') + try { contract.metadataHash = result.url.match('dweb:/ipfs/(.+)')[1] } catch (e) { contract.metadataHash = ' - metadata.json' } - if (!error) { - item.content = metadataContent - item.hash = contract.metadataHash - } + item.content = metadataContent + item.hash = contract.metadataHash uploaded.push({ content: contract.metadata, hash: contract.metadataHash, filename: 'metadata.json', output: result }) - }) + } catch (error) { + throw new Error(error) + } return { uploaded, item } } -const ipfsVerifiedPublish = async (content, expectedHash, cb) => { +const ipfsVerifiedPublish = async (content, expectedHash) => { try { const results = await severalGatewaysPush(content) + if (expectedHash && results !== expectedHash) { - cb(null, { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'dweb:/ipfs/' + results, hash: results }) + return { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'dweb:/ipfs/' + results, hash: results } } else { - cb(null, { message: 'ok', url: 'dweb:/ipfs/' + results, hash: results }) + return { message: 'ok', url: 'dweb:/ipfs/' + results, hash: results } } } catch (error) { - cb(error) + throw new Error(error) } } diff --git a/libs/remix-ui/publish-to-storage/src/lib/types/index.ts b/libs/remix-ui/publish-to-storage/src/lib/types/index.ts index db95172c81..928315c621 100644 --- a/libs/remix-ui/publish-to-storage/src/lib/types/index.ts +++ b/libs/remix-ui/publish-to-storage/src/lib/types/index.ts @@ -2,5 +2,6 @@ export interface RemixUiPublishToStorageProps { storage: string, fileProvider: any, fileManager: any, - contract: any + contract: any, + resetStorage: () => void } 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 50c0d325ba..31fad99c8f 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -7,7 +7,7 @@ import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promi import './css/style.css' export const CompilerContainer = (props: CompilerContainerProps) => { - const { editor, config, queryParams, compileTabLogic, tooltip, modal, compiledFileName } = props // eslint-disable-line + const { editor, config, queryParams, compileTabLogic, tooltip, modal, compiledFileName, setHardHatCompilation } = props // eslint-disable-line const [state, setState] = useState({ hideWarnings: false, autoCompile: false, @@ -28,6 +28,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => { const compileIcon = useRef(null) const warningIcon = useRef(null) const promptMessageInput = useRef(null) + const [hhCompilation, sethhCompilation] = useState(false) useEffect(() => { fetchAllVersion((allversions, selectedVersion, isURL) => { @@ -441,6 +442,13 @@ export const CompilerContainer = (props: CompilerContainerProps) => { }) } + const updatehhCompilation = (event) => { + const checked = event.target.checked + + sethhCompilation(checked) + setHardHatCompilation(checked) + } + return (
@@ -515,6 +523,10 @@ export const CompilerContainer = (props: CompilerContainerProps) => { +
+ + +
} - + ) - - // if (contractList.length) { - // this.selectedContract = selectEl.value - // } else { - // delete this.selectedContract - // } - // return result - - // return () } export default ContractSelection 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 ea82b23352..60e22f3c39 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx @@ -8,7 +8,7 @@ import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import './css/style.css' export const SolidityCompiler = (props: SolidityCompilerProps) => { - const { editor, config, queryParams, plugin, compileTabLogic, compiledFileName, fileProvider, fileManager, contractsDetails } = props + const { editor, config, queryParams, plugin, compileTabLogic, compiledFileName, fileProvider, fileManager, contractsDetails, setHardHatCompilation, contractMap } = props const [state, setState] = useState({ contractsDetails: {}, eventHandlers: {}, @@ -65,8 +65,8 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => { return ( <>
- - + +
diff --git a/libs/remix-ui/solidity-compiler/src/lib/types/index.ts b/libs/remix-ui/solidity-compiler/src/lib/types/index.ts index 8dba36f4bd..fd9a30adcc 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/types/index.ts +++ b/libs/remix-ui/solidity-compiler/src/lib/types/index.ts @@ -9,7 +9,11 @@ export interface SolidityCompilerProps { queryParams: any, compileTabLogic: any, compiledFileName: string, - contractsDetails: Record + contractsDetails: Record, + setHardHatCompilation: (value: boolean) => void, + contractMap: { + file: string + } | Record } export interface CompilerContainerProps { @@ -19,12 +23,15 @@ export interface CompilerContainerProps { compileTabLogic: any, tooltip: (message: string) => void, modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, - compiledFileName: string + compiledFileName: string, + setHardHatCompilation: (value: boolean) => void } export interface ContractSelectionProps { contractMap: { file: string } | Record, fileManager: any, - fileProvider: any + fileProvider: any, + modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, + contractsDetails: Record }