Fixed publish to storage.

pull/5370/head
ioedeveloper 3 years ago
parent 4a39276d23
commit 3c38fdcc50
  1. 154
      apps/remix-ide/src/app/tabs/compile-tab.js
  2. 97
      libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx
  3. 45
      libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx
  4. 35
      libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx
  5. 3
      libs/remix-ui/publish-to-storage/src/lib/types/index.ts
  6. 14
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  7. 138
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  8. 6
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  9. 13
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts

@ -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`<span data-id="compilationFinishedWith_${this.getCurrentVersion()}"></span>`)
// this._view.errorContainer.appendChild(yo`<span data-id="compilationFinishedWith_${this.getCurrentVersion()}"></span>`)
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`<div class="${css.detailsJSON}"></div>`
Object.keys(contractProperties).map(propertyName => {
const copyDetails = yo`<span class="${css.copyDetails}">${copyToClipboard(() => contractProperties[propertyName])}</span>`
const questionMark = yo`<span class="${css.questionMark}"><i title="${help[propertyName]}" class="fas fa-question-circle" aria-hidden="true"></i></span>`
log.appendChild(yo`<div class=${css.log}>
<div class="${css.key}">${propertyName} ${copyDetails} ${questionMark}</div>
${this.insertValue(contractProperties, propertyName)}
</div>`)
})
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`<div class="${css.detailsJSON}"></div>`
// Object.keys(contractProperties).map(propertyName => {
// const copyDetails = yo`<span class="${css.copyDetails}">${copyToClipboard(() => contractProperties[propertyName])}</span>`
// const questionMark = yo`<span class="${css.questionMark}"><i title="${help[propertyName]}" class="fas fa-question-circle" aria-hidden="true"></i></span>`
// log.appendChild(yo`<div class=${css.log}>
// <div class="${css.key}">${propertyName} ${copyDetails} ${questionMark}</div>
// ${this.insertValue(contractProperties, propertyName)}
// </div>`)
// })
// modalDialog(this.selectedContract, log, { label: '' }, { label: 'Close' })
// }
insertValue (details, propertyName) {
var node
if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') {
node = yo`<pre>${details[propertyName]}</pre>`
} 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`
<div>
${treeView.render(typeof details[propertyName] === 'object' ? details[propertyName] : JSON.parse(details[propertyName]))}
</div>` // catch in case the parsing fails.
} catch (e) {
node = yo`<div>Unable to display "${propertyName}": ${e.message}</div>`
}
} else {
node = yo`<div> - </div>`
}
} else {
node = yo`<div>${JSON.stringify(details[propertyName], null, 4)}</div>`
}
return yo`<pre class="${css.value}">${node || ''}</pre>`
}
// insertValue (details, propertyName) {
// var node
// if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') {
// node = yo`<pre>${details[propertyName]}</pre>`
// } 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`
// <div>
// ${treeView.render(typeof details[propertyName] === 'object' ? details[propertyName] : JSON.parse(details[propertyName]))}
// </div>` // catch in case the parsing fails.
// } catch (e) {
// node = yo`<div>Unable to display "${propertyName}": ${e.message}</div>`
// }
// } else {
// node = yo`<div> - </div>`
// }
// } else {
// node = yo`<div>${JSON.stringify(details[propertyName], null, 4)}</div>`
// }
// return yo`<pre class="${css.value}">${node || ''}</pre>`
// }
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)
}

@ -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 (
<span> Metadata of {contract.name.toLowerCase()} was published successfully. <br />
<pre>
<div>
{ uploaded.map((value) => {
<div><b>{ value.filename }</b> : <pre>{ value.output.url }</pre></div>
}) }
</div>
</pre>
</span>
)
}
const publishMessage = (uploaded) => (
<span> Metadata of {contract.name.toLowerCase()} was published successfully. <br />
<pre>
<div>
{ uploaded.map((value, index) => <div key={index}><b>{ value.filename }</b> : <pre>{ value.output.url }</pre></div>) }
</div>
</pre>
</span>
)
const publishMessageFailed = (storage, err) => {
return (
<span>Failed to publish metadata file to { storage }, please check the { storage } gateways is available. <br />
{err}
</span>
)
}
const publishMessageFailed = (storage, err) => (
<span>Failed to publish metadata file to { storage }, please check the { storage } gateways is available. <br />
{err}
</span>
)
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 }
</ModalDialog>

@ -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 inconsistency> - 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<Record<string, any>> => {
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 })
}
})
})
}

@ -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 inconsistency> - 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)
}
}

@ -2,5 +2,6 @@ export interface RemixUiPublishToStorageProps {
storage: string,
fileProvider: any,
fileManager: any,
contract: any
contract: any,
resetStorage: () => void
}

@ -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 (
<section>
<article>
@ -515,6 +523,10 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="form-check-label custom-control-label" htmlFor="hideWarningsBox">Hide warnings</label>
</div>
</div>
<div className="mt-2 remixui_compilerConfig custom-control custom-checkbox" style={{ display: "none" }}>
<input className="remixui_autocompile custom-control-input" onChange={updatehhCompilation} id="enableHardhat" type="checkbox" title="Enable Hardhat Compilation" checked={hhCompilation} />
<label className="form-check-label custom-control-label" htmlFor="enableHardhat">Enable Hardhat Compilation</label>
</div>
<button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block remixui_disabled mt-3" title="Compile" onClick={compile} disabled={!state.compiledFileName || (state.compiledFileName && !isSolFileSelected(state.compiledFileName))}>
<span>
<i ref={warningIcon} title="Compilation Slow" style={{ visibility: 'hidden' }} className="remixui_warnCompilationSlow fas fa-exclamation-triangle" aria-hidden="true"></i>

@ -1,13 +1,16 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line
import { ContractSelectionProps } from './types'
import { PublishToStorage } from '@remix-ui/publish-to-storage'
import { TreeView, TreeViewItem } from '@remix-ui/tree-view'
import { CopyToClipboard } from '@remix-ui/clipboard'
import './css/style.css'
export const ContractSelection = (props: ContractSelectionProps) => {
const { contractMap, fileProvider, fileManager, contractsDetails } = props
const { contractMap, fileProvider, fileManager, contractsDetails, modal } = props
const [contractList, setContractList] = useState([])
const [selectedContract, setSelectedContract] = useState('')
const [storage, setStorage] = useState(null)
useEffect(() => {
const contractList = contractMap ? Object.keys(contractMap).map((key) => ({
@ -16,7 +19,13 @@ export const ContractSelection = (props: ContractSelectionProps) => {
})) : []
setContractList(contractList)
}, [])
if (!selectedContract && contractList.length) setSelectedContract(contractList[0].name)
}, [contractMap])
const resetStorage = () => {
setStorage('')
}
// Return the file name of a path: ex "browser/ballot.sol" -> "ballot.sol"
const getFileName = (path) => {
const part = path.split('/')
@ -24,12 +33,12 @@ export const ContractSelection = (props: ContractSelectionProps) => {
return part[part.length - 1]
}
const selectContract = (contractName: string) => {
const handleContractChange = (contractName: string) => {
setSelectedContract(contractName)
}
const handlePublishToStorage = (type) => {
setStorage(type)
}
const copyABI = () => {
@ -60,6 +69,81 @@ export const ContractSelection = (props: ContractSelectionProps) => {
return contractProperties[property] || null
}
const renderData = (item, key: string | number, keyPath: string) => {
const data = extractData(item)
const children = (data.children || []).map((child) => renderData(child.value, child.key, keyPath + '/' + child.key))
if (children && children.length > 0) {
return (
<TreeViewItem id={`treeViewItem${key}`} key={keyPath} label={
<div className="d-flex mt-2 flex-row remixui_label_item">
<label className="small font-weight-bold pr-1 remixui_label_key">{ key }:</label>
<label className="m-0 remixui_label_value">{ data.self }</label>
</div>
}>
<TreeView id={`treeView${key}`} key={keyPath}>
{children}
</TreeView>
</TreeViewItem>
)
} else {
return <TreeViewItem id={key.toString()} key={keyPath} label={
<div className="d-flex mt-2 flex-row remixui_label_item">
<label className="small font-weight-bold pr-1 remixui_label_key">{ key }:</label>
<label className="m-0 remixui_label_value">{ data.self }</label>
</div>
} />
}
}
const extractData = (item) => {
let ret = { children: null, self: null }
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
}
const insertValue = (details, propertyName) => {
let node
if (propertyName === 'web3Deploy' || propertyName === 'name' || propertyName === 'Assembly') {
node = <pre>{ details[propertyName] }</pre>
} else if (propertyName === 'abi' || propertyName === 'metadata') {
if (details[propertyName] !== '') {
try {
node = <div>
{ (typeof details[propertyName] === 'object') ?
<TreeView id="treeView">
{
Object.keys(details[propertyName]).map((innerkey) => renderData(details[propertyName][innerkey], innerkey, innerkey))
}
</TreeView> : <TreeView id="treeView">
{
Object.keys(JSON.parse(details[propertyName])).map((innerkey) => renderData(JSON.parse(details[propertyName])[innerkey], innerkey, innerkey))
}
</TreeView>
}
</div> // catch in case the parsing fails.
} catch (e) {
node = <div>Unable to display "${propertyName}": ${e.message}</div>
}
} else {
node = <div> - </div>
}
} else {
node = <div>{JSON.stringify(details[propertyName], null, 4)}</div>
}
return <pre className="remixui_value">{node || ''}</pre>
}
const details = () => {
if (!selectedContract) throw new Error('No contract compiled yet')
@ -78,22 +162,21 @@ export const ContractSelection = (props: ContractSelectionProps) => {
web3Deploy: 'Copy/paste this code to any JavaScript/Web3 console to deploy this contract'
}
const contractProperties = contractsDetails[selectedContract]
const log = <div className="remixui_detailsJSON"></div>
Object.keys(contractProperties).map(propertyName => {
const copyDetails = <span className="remixui_copyDetails">{ copyToClipboard(() => contractProperties[propertyName]) }</span>
const questionMark = <span className="remixui_questionMark"><i title={ help[propertyName] } className="fas fa-question-circle" aria-hidden="true"></i></span>
log.appendChild(yo`<div class=${css.log}>
<div class="${css.key}">${propertyName} ${copyDetails} ${questionMark}</div>
${this.insertValue(contractProperties, propertyName)}
</div>`)
})
return {
const log = <div className="remixui_detailsJSON">
{
Object.keys(contractProperties).map((propertyName, index) => {
const copyDetails = <span className="remixui_copyDetails"><CopyToClipboard content={contractProperties[propertyName]} /></span>
const questionMark = <span className="remixui_questionMark"><i title={ help[propertyName] } className="fas fa-question-circle" aria-hidden="true"></i></span>
return (<div className="remixui_log" key={index}>
<div className="remixui_key">{ propertyName } { copyDetails } { questionMark }</div>
{ insertValue(contractProperties, propertyName) }
</div>)
})
}
</div>
}
modalDialog(this.selectedContract, log, { label: '' }, { label: 'Close' })
modal(selectedContract, log, 'Close', null)
}
const copyBytecode = () => {
@ -108,8 +191,8 @@ export const ContractSelection = (props: ContractSelectionProps) => {
{/* Select Compiler Version */}
<div className="mb-3">
<label className="remixui_compilerLabel form-check-label" htmlFor="compiledContracts">Contract</label>
<select onChange={(e) => selectContract(e.target.value)} data-id="compiledContracts" id="compiledContracts" className="custom-select">
{ contractList.map(({ name, file }) => <option value={name}>{name} ({file})</option>)}
<select onChange={(e) => handleContractChange(e.target.value)} value={selectedContract} data-id="compiledContracts" id="compiledContracts" className="custom-select">
{ contractList.map(({ name, file }, index) => <option value={name} key={index}>{name} ({file})</option>)}
</select>
</div>
<article className="mt-2 pb-0">
@ -119,7 +202,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
</button>
<button id="publishOnIpfs" className="btn btn-secondary btn-block" title="Publish on Ipfs" onClick={() => { handlePublishToStorage('ipfs') }}>
<span>Publish on Ipfs</span>
<img id="ipfsLogo" className="remixui_storageLogo} ml-2" src="assets/img/ipfs.webp" />
<img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" />
</button>
<button data-id="compilation-details" className="btn btn-secondary btn-block" title="Display Contract Details" onClick={() => { details() }}>
Compilation Details
@ -144,18 +227,9 @@ export const ContractSelection = (props: ContractSelectionProps) => {
<span className="mt-2 mx-3 w-100 alert alert-warning" role="alert">No Contract Compiled Yet</span>
</article></section>
}
<PublishToStorage />
<PublishToStorage storage={storage} fileManager={fileManager} fileProvider={fileProvider} contract={contractsDetails[selectedContract]} resetStorage={resetStorage} />
</>
)
// if (contractList.length) {
// this.selectedContract = selectEl.value
// } else {
// delete this.selectedContract
// }
// return result
// return ()
}
export default ContractSelection

@ -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 (
<>
<div id="compileTabView">
<CompilerContainer editor={editor} config={config} queryParams={queryParams} compileTabLogic={compileTabLogic} tooltip={toast} modal={modal} compiledFileName={compiledFileName} />
<ContractSelection contractMap={{}} fileProvider={fileProvider} fileManager={fileManager} contractsDetails={contractsDetails} />
<CompilerContainer editor={editor} config={config} queryParams={queryParams} compileTabLogic={compileTabLogic} tooltip={toast} modal={modal} compiledFileName={compiledFileName} setHardHatCompilation={setHardHatCompilation} />
<ContractSelection contractMap={contractMap} fileProvider={fileProvider} fileManager={fileManager} contractsDetails={contractsDetails} modal={modal} />
<div className="remixui_errorBlobs p-4" data-id="compiledErrors"></div>
</div>
<Toaster message={state.toasterMsg} />

@ -9,7 +9,11 @@ export interface SolidityCompilerProps {
queryParams: any,
compileTabLogic: any,
compiledFileName: string,
contractsDetails: Record<string, any>
contractsDetails: Record<string, any>,
setHardHatCompilation: (value: boolean) => void,
contractMap: {
file: string
} | Record<string, any>
}
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<string, any>,
fileManager: any,
fileProvider: any
fileProvider: any,
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
contractsDetails: Record<string, any>
}

Loading…
Cancel
Save