delete unused files

yann300-patch-36
David Disu 3 years ago committed by yann300
parent 8933cb1adf
commit 39e8f543a8
  1. 423
      apps/remix-ide/src/app/tabs/runTab/contractDropdown.js
  2. 108
      apps/remix-ide/src/app/tabs/runTab/model/dropdownlogic.js
  3. 160
      apps/remix-ide/src/app/tabs/runTab/recorder.js
  4. 449
      apps/remix-ide/src/app/tabs/runTab/settings.js
  5. 225
      apps/remix-ide/src/app/tabs/styles/run-tab-styles.js
  6. 212
      apps/remix-ide/src/app/ui/TreeView.js
  7. 142
      apps/remix-ide/src/app/ui/confirmDialog.js
  8. 228
      apps/remix-ide/src/app/ui/multiParamManager.js
  9. 272
      apps/remix-ide/src/app/ui/universal-dapp-ui.js
  10. 121
      apps/remix-ide/src/lib/publishOnIpfs.js
  11. 109
      apps/remix-ide/src/lib/publishOnSwarm.js
  12. 47
      apps/remix-ide/src/publishToStorage.js
  13. 285
      apps/remix-ide/src/universal-dapp-styles.js

@ -1,423 +0,0 @@
import publishToStorage from '../../../publishToStorage'
const yo = require('yo-yo')
const ethJSUtil = require('ethereumjs-util')
const css = require('../styles/run-tab-styles')
const modalDialogCustom = require('../../ui/modal-dialog-custom')
const remixLib = require('@remix-project/remix-lib')
const EventManager = remixLib.EventManager
const confirmDialog = require('../../ui/confirmDialog')
const modalDialog = require('../../ui/modaldialog')
const MultiParamManager = require('../../ui/multiParamManager')
const helper = require('../../../lib/helper')
const addTooltip = require('../../ui/tooltip')
const _paq = window._paq = window._paq || []
class ContractDropdownUI {
constructor (blockchain, dropdownLogic, logCallback, runView) {
this.blockchain = blockchain
this.dropdownLogic = dropdownLogic
this.logCallback = logCallback
this.runView = runView
this.event = new EventManager()
this.listenToEvents()
this.ipfsCheckedState = false
this.exEnvironment = blockchain.getProvider()
this.listenToContextChange()
this.loadType = 'other'
}
listenToEvents () {
this.dropdownLogic.event.register('newlyCompiled', (success, data, source, compiler, compilerFullName, file) => {
if (!this.selectContractNames) return
this.selectContractNames.innerHTML = ''
if (success) {
this.dropdownLogic.getCompiledContracts(compiler, compilerFullName).forEach((contract) => {
this.selectContractNames.appendChild(yo`<option value="${contract.name}" compiler="${compilerFullName}">${contract.name} - ${contract.file}</option>`)
})
}
this.enableAtAddress(success)
this.enableContractNames(success)
this.setInputParamsPlaceHolder()
if (success) {
this.compFails.style.display = 'none'
} else {
this.compFails.style.display = 'block'
}
})
}
listenToContextChange () {
this.blockchain.event.register('networkStatus', ({ error, network }) => {
if (error) {
console.log('can\'t detect network')
return
}
this.exEnvironment = this.blockchain.getProvider()
this.networkName = network.name
this.networkId = network.id
const savedConfig = window.localStorage.getItem(`ipfs/${this.exEnvironment}/${this.networkName}`)
// check if an already selected option exist else use default workflow
if (savedConfig !== null) {
this.setCheckedState(savedConfig)
} else {
this.setCheckedState(this.networkName === 'Main')
}
})
}
setCheckedState (value) {
value = value === 'true' ? true : value === 'false' ? false : value
this.ipfsCheckedState = value
if (this.ipfsCheckbox) this.ipfsCheckbox.checked = value
}
toggleCheckedState () {
if (this.exEnvironment === 'vm') this.networkName = 'VM'
this.ipfsCheckedState = !this.ipfsCheckedState
window.localStorage.setItem(`ipfs/${this.exEnvironment}/${this.networkName}`, this.ipfsCheckedState)
}
enableContractNames (enable) {
if (enable) {
if (this.selectContractNames.value === '') return
this.selectContractNames.removeAttribute('disabled')
this.selectContractNames.setAttribute('title', 'Select contract for Deploy or At Address.')
} else {
this.selectContractNames.setAttribute('disabled', true)
if (this.loadType === 'sol') {
this.selectContractNames.setAttribute('title', '⚠ Select and compile *.sol file to deploy or access a contract.')
} else {
this.selectContractNames.setAttribute('title', '⚠ Selected *.abi file allows accessing contracts, select and compile *.sol file to deploy and access one.')
}
}
}
enableAtAddress (enable) {
if (enable) {
const address = this.atAddressButtonInput.value
if (!address || !ethJSUtil.isValidAddress(address)) {
this.enableAtAddress(false)
return
}
this.atAddress.removeAttribute('disabled')
this.atAddress.setAttribute('title', 'Interact with the given contract.')
} else {
this.atAddress.setAttribute('disabled', true)
if (this.atAddressButtonInput.value === '') {
this.atAddress.setAttribute('title', '⚠ Compile *.sol file or select *.abi file & then enter the address of deployed contract.')
} else {
this.atAddress.setAttribute('title', '⚠ Compile *.sol file or select *.abi file.')
}
}
}
render () {
this.compFails = yo`<i title="No contract compiled yet or compilation failed. Please check the compile tab for more information." class="m-2 ml-3 fas fa-times-circle ${css.errorIcon}" ></i>`
this.atAddress = yo`<button class="${css.atAddress} btn btn-sm btn-info" id="runAndDeployAtAdressButton" onclick=${this.loadFromAddress.bind(this)}>At Address</button>`
this.atAddressButtonInput = yo`<input class="${css.input} ${css.ataddressinput} ataddressinput form-control" placeholder="Load contract from Address" title="address of contract" oninput=${this.atAddressChanged.bind(this)} />`
this.selectContractNames = yo`<select class="${css.contractNames} custom-select" disabled title="Please compile *.sol file to deploy or access a contract"></select>`
this.abiLabel = yo`<span class="py-1">ABI file selected</span>`
if (this.exEnvironment === 'vm') this.networkName = 'VM'
this.enableAtAddress(false)
this.abiLabel.style.display = 'none'
const savedConfig = window.localStorage.getItem(`ipfs/${this.exEnvironment}/${this.networkName}`)
this.ipfsCheckedState = savedConfig === 'true' ? true : false // eslint-disable-line
this.ipfsCheckbox = yo`
<input
id="deployAndRunPublishToIPFS"
data-id="contractDropdownIpfsCheckbox"
class="form-check-input custom-control-input"
type="checkbox"
onchange=${() => this.toggleCheckedState()}
>
`
if (this.ipfsCheckedState) this.ipfsCheckbox.checked = true
this.deployCheckBox = yo`
<div class="d-flex py-1 align-items-center custom-control custom-checkbox">
${this.ipfsCheckbox}
<label
for="deployAndRunPublishToIPFS"
data-id="contractDropdownIpfsCheckboxLabel"
class="m-0 form-check-label custom-control-label ${css.checkboxAlign}"
title="Publishing the source code and metadata to IPFS facilitates source code verification using Sourcify and will greatly foster contract adoption (auditing, debugging, calling it, etc...)"
>
Publish to IPFS
</label>
</div>
`
this.createPanel = yo`<div class="${css.deployDropdown}"></div>`
this.orLabel = yo`<div class="${css.orLabel} mt-2">or</div>`
const contractNamesContainer = yo`
<div class="${css.container}" data-id="contractDropdownContainer">
<label class="${css.settingsLabel}">Contract</label>
<div class="${css.subcontainer}">
${this.selectContractNames} ${this.compFails}
${this.abiLabel}
</div>
<div>
${this.createPanel}
${this.orLabel}
<div class="${css.button} ${css.atAddressSect}">
${this.atAddress}
${this.atAddressButtonInput}
</div>
</div>
</div>
`
this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this))
this.setInputParamsPlaceHolder()
if (!this.contractNamesContainer) {
this.contractNamesContainer = contractNamesContainer
}
return contractNamesContainer
}
atAddressChanged (event) {
if (!this.atAddressButtonInput.value) {
this.enableAtAddress(false)
} else {
if ((this.selectContractNames && !this.selectContractNames.getAttribute('disabled') && this.loadType === 'sol') ||
this.loadType === 'abi') {
this.enableAtAddress(true)
} else {
this.enableAtAddress(false)
}
}
}
changeCurrentFile (currentFile) {
if (!this.selectContractNames) return
if (/.(.abi)$/.exec(currentFile)) {
this.createPanel.style.display = 'none'
this.orLabel.style.display = 'none'
this.compFails.style.display = 'none'
this.loadType = 'abi'
this.contractNamesContainer.style.display = 'block'
this.abiLabel.style.display = 'block'
this.abiLabel.innerHTML = currentFile
this.selectContractNames.style.display = 'none'
this.enableContractNames(true)
this.enableAtAddress(true)
} else if (/.(.sol)$/.exec(currentFile) ||
/.(.vy)$/.exec(currentFile) || // vyper
/.(.lex)$/.exec(currentFile) || // lexon
/.(.contract)$/.exec(currentFile)) {
this.createPanel.style.display = 'block'
this.orLabel.style.display = 'block'
this.contractNamesContainer.style.display = 'block'
this.loadType = 'sol'
this.selectContractNames.style.display = 'block'
this.abiLabel.style.display = 'none'
if (this.selectContractNames.value === '') this.enableAtAddress(false)
} else {
this.loadType = 'other'
this.createPanel.style.display = 'block'
this.orLabel.style.display = 'block'
this.contractNamesContainer.style.display = 'block'
this.selectContractNames.style.display = 'block'
this.abiLabel.style.display = 'none'
if (this.selectContractNames.value === '') this.enableAtAddress(false)
}
}
setInputParamsPlaceHolder () {
this.createPanel.innerHTML = ''
if (this.selectContractNames.selectedIndex < 0 || this.selectContractNames.children.length <= 0) {
this.createPanel.innerHTML = 'No compiled contracts'
return
}
const selectedContract = this.getSelectedContract()
const clickCallback = async (valArray, inputsValues) => {
var selectedContract = this.getSelectedContract()
this.createInstance(selectedContract, inputsValues)
}
const createConstructorInstance = new MultiParamManager(
0,
selectedContract.getConstructorInterface(),
clickCallback,
selectedContract.getConstructorInputs(),
'Deploy',
selectedContract.bytecodeObject,
true
)
this.createPanel.appendChild(createConstructorInstance.render())
this.createPanel.appendChild(this.deployCheckBox)
}
getSelectedContract () {
var contract = this.selectContractNames.children[this.selectContractNames.selectedIndex]
var contractName = contract.getAttribute('value')
var compilerAtributeName = contract.getAttribute('compiler')
return this.dropdownLogic.getSelectedContract(contractName, compilerAtributeName)
}
async createInstance (selectedContract, args) {
if (selectedContract.bytecodeObject.length === 0) {
return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.')
}
var continueCb = (error, continueTxExecution, cancelCb) => {
if (error) {
var msg = typeof error !== 'string' ? error.message : error
modalDialog('Gas estimation failed', yo`<div>Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending? <br>
${msg}
</div>`,
{
label: 'Send Transaction',
fn: () => {
continueTxExecution()
}
}, {
label: 'Cancel Transaction',
fn: () => {
cancelCb()
}
})
} else {
continueTxExecution()
}
}
const self = this
var promptCb = (okCb, cancelCb) => {
modalDialogCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
}
var statusCb = (msg) => {
return this.logCallback(msg)
}
var finalCb = (error, contractObject, address) => {
self.event.trigger('clearInstance')
if (error) {
return this.logCallback(error)
}
self.event.trigger('newContractInstanceAdded', [contractObject, address, contractObject.name])
const data = self.runView.compilersArtefacts.getCompilerAbstract(contractObject.contract.file)
self.runView.compilersArtefacts.addResolvedContract(helper.addressToString(address), data)
if (self.ipfsCheckedState) {
_paq.push(['trackEvent', 'udapp', 'DeployAndPublish', this.networkName + '_' + this.networkId])
publishToStorage('ipfs', self.runView.fileProvider, self.runView.fileManager, selectedContract)
} else {
_paq.push(['trackEvent', 'udapp', 'DeployOnly', this.networkName + '_' + this.networkId])
}
}
let contractMetadata
try {
contractMetadata = await this.runView.call('compilerMetadata', 'deployMetadataOf', selectedContract.name, selectedContract.contract.file)
} catch (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
}
const compilerContracts = this.dropdownLogic.getCompilerContracts()
const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog)
if (selectedContract.isOverSizeLimit()) {
return modalDialog('Contract code size over limit', yo`<div>Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. <br>
More info: <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md" target="_blank">eip-170</a>
</div>`,
{
label: 'Force Send',
fn: () => {
this.deployContract(selectedContract, args, contractMetadata, compilerContracts, { continueCb, promptCb, statusCb, finalCb }, confirmationCb)
}
}, {
label: 'Cancel',
fn: () => {
this.logCallback(`creation of ${selectedContract.name} canceled by user.`)
}
})
}
this.deployContract(selectedContract, args, contractMetadata, compilerContracts, { continueCb, promptCb, statusCb, finalCb }, confirmationCb)
}
deployContract (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) {
_paq.push(['trackEvent', 'udapp', 'DeployContractTo', this.networkName + '_' + this.networkId])
const { statusCb } = callbacks
if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) {
return this.blockchain.deployContractAndLibraries(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb)
}
if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`)
this.blockchain.deployContractWithLibrary(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb)
}
getConfirmationCb (modalDialog, confirmDialog) {
// this code is the same as in recorder.js. TODO need to be refactored out
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, network, amount, gasEstimation, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain))
modalDialog('Confirm transaction', content,
{
label: 'Confirm',
fn: () => {
this.blockchain.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
// TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) {
cancelCb('Given transaction fee is not correct')
} else {
continueTxExecution(content.txFee)
}
}
}, {
label: 'Cancel',
fn: () => {
return cancelCb('Transaction canceled by user.')
}
}
)
}
return confirmationCb
}
loadFromAddress () {
this.event.trigger('clearInstance')
let address = this.atAddressButtonInput.value
if (!ethJSUtil.isValidChecksumAddress(address)) {
addTooltip(yo`
<span>
It seems you are not using a checksumed address.
<br>A checksummed address is an address that contains uppercase letters, as specified in <a href="https://eips.ethereum.org/EIPS/eip-55" target="_blank">EIP-55</a>.
<br>Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.
</span>`)
address = ethJSUtil.toChecksumAddress(address)
}
this.dropdownLogic.loadContractFromAddress(address,
(cb) => {
modalDialogCustom.confirm('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, cb)
},
(error, loadType, abi) => {
if (error) {
return modalDialogCustom.alert(error)
}
if (loadType === 'abi') {
return this.event.trigger('newContractABIAdded', [abi, address])
}
var selectedContract = this.getSelectedContract()
this.event.trigger('newContractInstanceAdded', [selectedContract.object, address, this.selectContractNames.value])
}
)
}
}
module.exports = ContractDropdownUI

@ -1,108 +0,0 @@
import { CompilerAbstract } from '@remix-project/remix-solidity'
const remixLib = require('@remix-project/remix-lib')
const txHelper = remixLib.execution.txHelper
const EventManager = remixLib.EventManager
const _paq = window._paq = window._paq || []
class DropdownLogic {
constructor (compilersArtefacts, config, editor, runView) {
this.compilersArtefacts = compilersArtefacts
this.config = config
this.editor = editor
this.runView = runView
this.event = new EventManager()
this.listenToCompilationEvents()
}
// TODO: can be moved up; the event in contractDropdown will have to refactored a method instead
listenToCompilationEvents () {
const broadcastCompilationResult = (file, source, languageVersion, data) => {
// TODO check whether the tab is configured
const compiler = new CompilerAbstract(languageVersion, data, source)
this.compilersArtefacts[languageVersion] = compiler
this.compilersArtefacts.__last = compiler
this.event.trigger('newlyCompiled', [true, data, source, compiler, languageVersion, file])
}
this.runView.on('solidity', 'compilationFinished', (file, source, languageVersion, data) =>
broadcastCompilationResult(file, source, languageVersion, data)
)
this.runView.on('vyper', 'compilationFinished', (file, source, languageVersion, data) =>
broadcastCompilationResult(file, source, languageVersion, data)
)
this.runView.on('lexon', 'compilationFinished', (file, source, languageVersion, data) =>
broadcastCompilationResult(file, source, languageVersion, data)
)
this.runView.on('yulp', 'compilationFinished', (file, source, languageVersion, data) =>
broadcastCompilationResult(file, source, languageVersion, data)
)
this.runView.on('optimism-compiler', 'compilationFinished', (file, source, languageVersion, data) =>
broadcastCompilationResult(file, source, languageVersion, data)
)
}
loadContractFromAddress (address, confirmCb, cb) {
if (/.(.abi)$/.exec(this.config.get('currentFile'))) {
confirmCb(() => {
var abi
try {
abi = JSON.parse(this.editor.currentContent())
} catch (e) {
return cb('Failed to parse the current file as JSON ABI.')
}
_paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithABI'])
cb(null, 'abi', abi)
})
} else {
_paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithArtifacts'])
cb(null, 'instance')
}
}
getCompiledContracts (compiler, compilerFullName) {
var contracts = []
compiler.visitContracts((contract) => {
contracts.push(contract)
})
return contracts
}
getSelectedContract (contractName, compilerAtributeName) {
if (!contractName) return null
var compiler = this.compilersArtefacts[compilerAtributeName]
if (!compiler) return null
var contract = compiler.getContract(contractName)
return {
name: contractName,
contract: contract,
compiler: compiler,
abi: contract.object.abi,
bytecodeObject: contract.object.evm.bytecode.object,
bytecodeLinkReferences: contract.object.evm.bytecode.linkReferences,
object: contract.object,
deployedBytecode: contract.object.evm.deployedBytecode,
getConstructorInterface: () => {
return txHelper.getConstructorInterface(contract.object.abi)
},
getConstructorInputs: () => {
var constructorInteface = txHelper.getConstructorInterface(contract.object.abi)
return txHelper.inputParametersDeclarationToString(constructorInteface.inputs)
},
isOverSizeLimit: () => {
var deployedBytecode = contract.object.evm.deployedBytecode
return (deployedBytecode && deployedBytecode.object.length / 2 > 24576)
},
metadata: contract.object.metadata
}
}
getCompilerContracts () {
return this.compilersArtefacts.__last.getData().contracts
}
}
module.exports = DropdownLogic

@ -1,160 +0,0 @@
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../../package.json'
var yo = require('yo-yo')
var remixLib = require('@remix-project/remix-lib')
var EventManager = remixLib.EventManager
var csjs = require('csjs-inject')
var css = require('../styles/run-tab-styles')
var modalDialogCustom = require('../../ui/modal-dialog-custom')
var modalDialog = require('../../ui/modaldialog')
var confirmDialog = require('../../ui/confirmDialog')
var helper = require('../../../lib/helper.js')
const profile = {
name: 'recorder',
methods: ['runScenario'],
version: packageJson.version
}
class RecorderUI extends Plugin {
constructor (blockchain, fileManager, recorder, logCallBack, config) {
super(profile)
this.fileManager = fileManager
this.blockchain = blockchain
this.recorder = recorder
this.logCallBack = logCallBack
this.config = config
this.event = new EventManager()
}
render () {
var css2 = csjs`
.container {}
.runTxs {}
.recorder {}
`
this.runButton = yo`<i class="fas fa-play runtransaction ${css2.runTxs} ${css.icon}" title="Run Transactions" aria-hidden="true"></i>`
this.recordButton = yo`
<i class="fas fa-save savetransaction ${css2.recorder} ${css.icon}"
onclick=${this.triggerRecordButton.bind(this)} title="Save Transactions" aria-hidden="true">
</i>`
this.runButton.onclick = () => {
const file = this.config.get('currentFile')
if (!file) return modalDialogCustom.alert('A scenario file has to be selected')
this.runScenario(file)
}
}
runScenario (file) {
if (!file) return modalDialogCustom.alert('Unable to run scenerio, no specified scenario file')
var continueCb = (error, continueTxExecution, cancelCb) => {
if (error) {
var msg = typeof error !== 'string' ? error.message : error
modalDialog('Gas estimation failed', yo`<div>Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending? <br>
${msg}
</div>`,
{
label: 'Send Transaction',
fn: () => {
continueTxExecution()
}
}, {
label: 'Cancel Transaction',
fn: () => {
cancelCb()
}
})
} else {
continueTxExecution()
}
}
var promptCb = (okCb, cancelCb) => {
modalDialogCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
}
var alertCb = (msg) => {
modalDialogCustom.alert(msg)
}
const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog)
this.fileManager.readFile(file).then((json) => {
// TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily
this.recorder.runScenario(json, continueCb, promptCb, alertCb, confirmationCb, this.logCallBack, (error, abi, address, contractName) => {
if (error) {
return modalDialogCustom.alert(error)
}
this.event.trigger('newScenario', [abi, address, contractName])
})
}).catch((error) => modalDialogCustom.alert(error))
}
getConfirmationCb (modalDialog, confirmDialog) {
// this code is the same as in contractDropdown.js. TODO need to be refactored out
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, network, amount, gasEstimation, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain))
modalDialog('Confirm transaction', content,
{
label: 'Confirm',
fn: () => {
this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
// TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) {
cancelCb('Given transaction fee is not correct')
} else {
continueTxExecution(content.txFee)
}
}
}, {
label: 'Cancel',
fn: () => {
return cancelCb('Transaction canceled by user.')
}
}
)
}
return confirmationCb
}
triggerRecordButton () {
this.saveScenario(
(path, cb) => {
modalDialogCustom.prompt('Save transactions as scenario', 'Transactions will be saved in a file under ' + path, 'scenario.json', cb)
},
(error) => {
if (error) return modalDialogCustom.alert(error)
}
)
}
saveScenario (promptCb, cb) {
var txJSON = JSON.stringify(this.recorder.getAll(), null, 2)
var path = this.fileManager.currentPath()
promptCb(path, input => {
var fileProvider = this.fileManager.fileProviderOf(path)
if (!fileProvider) return
var newFile = path + '/' + input
helper.createNonClashingName(newFile, fileProvider, (error, newFile) => {
if (error) return cb('Failed to create file. ' + newFile + ' ' + error)
if (!fileProvider.set(newFile, txJSON)) return cb('Failed to create file ' + newFile)
this.fileManager.open(newFile)
})
})
}
}
module.exports = RecorderUI

@ -1,449 +0,0 @@
import { BN } from 'ethereumjs-util'
import Registry from '../../state/registry'
const $ = require('jquery')
const yo = require('yo-yo')
const remixLib = require('@remix-project/remix-lib')
const EventManager = remixLib.EventManager
const css = require('../styles/run-tab-styles')
const copyToClipboard = require('../../ui/copy-to-clipboard')
const modalDialogCustom = require('../../ui/modal-dialog-custom')
const addTooltip = require('../../ui/tooltip')
const helper = require('../../../lib/helper.js')
class SettingsUI {
constructor (blockchain, networkModule) {
this.blockchain = blockchain
this.event = new EventManager()
this._components = {}
this.blockchain.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
if (!lookupOnly) this.el.querySelector('#value').value = 0
if (error) return
this.updateAccountBalances()
})
this._components = {
registry: Registry.getInstance(),
networkModule: networkModule
}
this._components.registry = Registry.getInstance()
this._deps = {
config: this._components.registry.get('config').api
}
this._deps.config.events.on('settings/personal-mode_changed', this.onPersonalChange.bind(this))
setInterval(() => {
this.updateAccountBalances()
}, 1000)
this.accountListCallId = 0
this.loadedAccounts = {}
}
updateAccountBalances () {
if (!this.el) return
var accounts = $(this.el.querySelector('#txorigin')).children('option')
accounts.each((index, account) => {
this.blockchain.getBalanceInEther(account.value, (err, balance) => {
if (err) return
const updated = helper.shortenAddress(account.value, balance)
if (updated !== account.innerText) { // check if the balance has been updated and update UI accordingly.
account.innerText = updated
}
})
})
}
validateInputKey (e) {
// preventing not numeric keys
// preventing 000 case
if (!helper.isNumeric(e.key) ||
(e.key === '0' && !parseInt(this.el.querySelector('#value').value) && this.el.querySelector('#value').value.length > 0)) {
e.preventDefault()
e.stopImmediatePropagation()
}
}
validateValue () {
const valueEl = this.el.querySelector('#value')
if (!valueEl.value) {
// assign 0 if given value is
// - empty
valueEl.value = 0
return
}
let v
try {
v = new BN(valueEl.value, 10)
valueEl.value = v.toString(10)
} catch (e) {
// assign 0 if given value is
// - not valid (for ex 4345-54)
// - contains only '0's (for ex 0000) copy past or edit
valueEl.value = 0
}
// if giveen value is negative(possible with copy-pasting) set to 0
if (v.lt(0)) valueEl.value = 0
}
render () {
this.netUI = yo`<span class="${css.network} badge badge-secondary"></span>`
var environmentEl = yo`
<div class="${css.crow}">
<label id="selectExEnv" class="${css.settingsLabel}">
Environment
</label>
<div class="${css.environment}">
<select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" class="form-control ${css.select} custom-select">
<option id="vm-mode-london" data-id="settingsVMLondonMode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-london" name="executionContext" fork="london"> JavaScript VM (London)
</option>
<option id="vm-mode-berlin" data-id="settingsVMBerlinMode"
title="Execution environment does not connect to any node, everything is local and in memory only."
value="vm-berlin" name="executionContext" fork="berlin" > JavaScript VM (Berlin)
</option>
<option id="injected-mode" data-id="settingsInjectedMode"
title="Execution environment has been provided by Metamask or similar provider."
value="injected" name="executionContext"> Injected Web3
</option>
<option id="web3-mode" data-id="settingsWeb3Mode"
title="Execution environment connects to node at localhost (or via IPC if available), transactions will be sent to the network and can cause loss of money or worse!
If this page is served via https and you access your node via http, it might not work. In this case, try cloning the repository and serving it via http."
value="web3" name="executionContext"> Web3 Provider
</option>
</select>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#run-setup" target="_blank"><i class="${css.infoDeployAction} ml-2 fas fa-info" title="check out docs to setup Environment"></i></a>
</div>
</div>
`
const networkEl = yo`
<div class="${css.crow}">
<div class="${css.settingsLabel}">
</div>
<div class="${css.environment}" data-id="settingsNetworkEnv">
${this.netUI}
</div>
</div>
`
const accountEl = yo`
<div class="${css.crow}">
<label class="${css.settingsLabel}">
Account
<span id="remixRunPlusWraper" title="Create a new account" onload=${this.updatePlusButton.bind(this)}>
<i id="remixRunPlus" class="fas fa-plus-circle ${css.icon}" aria-hidden="true" onclick=${this.newAccount.bind(this)}"></i>
</span>
</label>
<div class="${css.account}">
<select data-id="runTabSelectAccount" name="txorigin" class="form-control ${css.select} custom-select pr-4" id="txorigin"></select>
<div style="margin-left: -5px;">${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}</div>
<i id="remixRunSignMsg" data-id="settingsRemixRunSignMsg" class="mx-1 fas fa-edit ${css.icon}" aria-hidden="true" onclick=${this.signMessage.bind(this)} title="Sign a message using this account key"></i>
</div>
</div>
`
const gasPriceEl = yo`
<div class="${css.crow}">
<label class="${css.settingsLabel}">Gas limit</label>
<input type="number" class="form-control ${css.gasNval} ${css.col2}" id="gasLimit" value="3000000">
</div>
`
const valueEl = yo`
<div class="${css.crow}">
<label class="${css.settingsLabel}" data-id="remixDRValueLabel">Value</label>
<div class="${css.gasValueContainer}">
<input
type="number"
min="0"
pattern="^[0-9]"
step="1"
class="form-control ${css.gasNval} ${css.col2}"
id="value"
data-id="dandrValue"
value="0"
title="Enter the value and choose the unit"
onkeypress=${(e) => this.validateInputKey(e)}
onchange=${() => this.validateValue()}
>
<select name="unit" class="form-control p-1 ${css.gasNvalUnit} ${css.col2_2} custom-select" id="unit">
<option data-unit="wei">Wei</option>
<option data-unit="gwei">Gwei</option>
<option data-unit="finney">Finney</option>
<option data-unit="ether">Ether</option>
</select>
</div>
</div>
`
const el = yo`
<div class="${css.settings}">
${environmentEl}
${networkEl}
${accountEl}
${gasPriceEl}
${valueEl}
</div>
`
var selectExEnv = environmentEl.querySelector('#selectExEnvOptions')
this.setDropdown(selectExEnv)
this.blockchain.event.register('contextChanged', (context, silent) => {
this.setFinalContext()
})
this.blockchain.event.register('networkStatus', ({ error, network }) => {
if (error) {
this.netUI.innerHTML = 'can\'t detect network '
return
}
const networkProvider = this._components.networkModule.getNetworkProvider.bind(this._components.networkModule)
this.netUI.innerHTML = (networkProvider() !== 'vm') ? `${network.name} (${network.id || '-'}) network` : ''
})
setInterval(() => {
this.fillAccountsList()
}, 1000)
this.el = el
this.fillAccountsList()
return el
}
setDropdown (selectExEnv) {
this.selectExEnv = selectExEnv
const addProvider = (network) => {
selectExEnv.appendChild(yo`<option
title="provider name: ${network.name}"
value="${network.name}"
name="executionContext"
>
${network.name}
</option>`)
addTooltip(yo`<span><b>${network.name}</b> provider added</span>`)
}
const removeProvider = (name) => {
var env = selectExEnv.querySelector(`option[value="${name}"]`)
if (env) {
selectExEnv.removeChild(env)
addTooltip(yo`<span><b>${name}</b> provider removed</span>`)
}
}
this.blockchain.event.register('addProvider', provider => addProvider(provider))
this.blockchain.event.register('removeProvider', name => removeProvider(name))
selectExEnv.addEventListener('change', (event) => {
const provider = selectExEnv.options[selectExEnv.selectedIndex]
const fork = provider.getAttribute('fork') // can be undefined if connected to an external source (web3 provider / injected)
let context = provider.value
context = context.startsWith('vm') ? 'vm' : context // context has to be 'vm', 'web3' or 'injected'
this.setExecutionContext({ context, fork })
})
selectExEnv.value = this._getProviderDropdownValue()
}
setExecutionContext (context) {
this.blockchain.changeExecutionContext(context, () => {
modalDialogCustom.prompt('External node request', this.web3ProviderDialogBody(), 'http://127.0.0.1:8545', (target) => {
this.blockchain.setProviderFromEndpoint(target, context, (alertMsg) => {
if (alertMsg) addTooltip(alertMsg)
this.setFinalContext()
})
}, this.setFinalContext.bind(this))
}, (alertMsg) => {
addTooltip(alertMsg)
}, this.setFinalContext.bind(this))
}
web3ProviderDialogBody () {
const thePath = '<path/to/local/folder/for/test/chain>'
return yo`
<div class="">
Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see <a href="https://geth.ethereum.org/docs/rpc/server" target="_blank">Geth Docs on rpc server</a>)
<div class="border p-1">geth --http --http.corsdomain https://remix.ethereum.org</div>
<br>
To run Remix & a local Geth test node, use this command: (see <a href="https://geth.ethereum.org/getting-started/dev-mode" target="_blank">Geth Docs on Dev mode</a>)
<div class="border p-1">geth --http --http.corsdomain="${window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir ${thePath} --dev console</div>
<br>
<br>
<b>WARNING:</b> It is not safe to use the --http.corsdomain flag with a wildcard: <b>--http.corsdomain *</b>
<br>
<br>For more info: <a href="https://remix-ide.readthedocs.io/en/latest/run.html#more-about-web3-provider" target="_blank">Remix Docs on Web3 Provider</a>
<br>
<br>
Web3 Provider Endpoint
</div>
`
}
/**
* generate a value used by the env dropdown list.
* @return {String} - can return 'vm-berlin, 'vm-london', 'injected' or 'web3'
*/
_getProviderDropdownValue () {
const provider = this.blockchain.getProvider()
const fork = this.blockchain.getCurrentFork()
return provider === 'vm' ? provider + '-' + fork : provider
}
setFinalContext () {
// set the final context. Cause it is possible that this is not the one we've originaly selected
this.selectExEnv.value = this._getProviderDropdownValue()
this.event.trigger('clearInstance', [])
this.updatePlusButton()
}
updatePlusButton () {
// enable/disable + button
const plusBtn = document.getElementById('remixRunPlus')
const plusTitle = document.getElementById('remixRunPlusWraper')
switch (this.selectExEnv.value) {
case 'injected':
plusBtn.classList.add(css.disableMouseEvents)
plusTitle.title = "Unfortunately it's not possible to create an account using injected web3. Please create the account directly from your provider (i.e metamask or other of the same type)."
break
case 'vm':
plusBtn.classList.remove(css.disableMouseEvents)
plusTitle.title = 'Create a new account'
break
case 'web3':
this.onPersonalChange()
break
default: {
plusBtn.classList.add(css.disableMouseEvents)
plusTitle.title = `Unfortunately it's not possible to create an account using an external wallet (${this.selectExEnv.value}).`
}
}
}
onPersonalChange () {
const plusBtn = document.getElementById('remixRunPlus')
const plusTitle = document.getElementById('remixRunPlusWraper')
if (!this._deps.config.get('settings/personal-mode')) {
plusBtn.classList.add(css.disableMouseEvents)
plusTitle.title = 'Creating an account is possible only in Personal mode. Please go to Settings to enable it.'
} else {
plusBtn.classList.remove(css.disableMouseEvents)
plusTitle.title = 'Create a new account'
}
}
newAccount () {
this.blockchain.newAccount(
'',
(cb) => {
modalDialogCustom.promptPassphraseCreation((error, passphrase) => {
if (error) {
return modalDialogCustom.alert(error)
}
cb(passphrase)
}, () => {})
},
(error, address) => {
if (error) {
return addTooltip('Cannot create an account: ' + error)
}
addTooltip(`account ${address} created`)
}
)
}
getSelectedAccount () {
return this.el.querySelector('#txorigin').selectedOptions[0].value
}
getEnvironment () {
return this.blockchain.getProvider()
}
signMessage () {
this.blockchain.getAccounts((err, accounts) => {
if (err) {
return addTooltip(`Cannot get account list: ${err}`)
}
var signMessageDialog = { title: 'Sign a message', text: 'Enter a message to sign', inputvalue: 'Message to sign' }
var $txOrigin = this.el.querySelector('#txorigin')
if (!$txOrigin.selectedOptions[0] && (this.blockchain.isInjectedWeb3() || this.blockchain.isWeb3Provider())) {
return addTooltip('Account list is empty, please make sure the current provider is properly connected to remix')
}
var account = $txOrigin.selectedOptions[0].value
var promptCb = (passphrase) => {
const modal = modalDialogCustom.promptMulti(signMessageDialog, (message) => {
this.blockchain.signMessage(message, account, passphrase, (err, msgHash, signedData) => {
if (err) {
return addTooltip(err)
}
modal.hide()
modalDialogCustom.alert(yo`
<div>
<b>hash:</b><br>
<span id="remixRunSignMsgHash" data-id="settingsRemixRunSignMsgHash">${msgHash}</span>
<br><b>signature:</b><br>
<span id="remixRunSignMsgSignature" data-id="settingsRemixRunSignMsgSignature">${signedData}</span>
</div>
`)
})
}, false)
}
if (this.blockchain.isWeb3Provider()) {
return modalDialogCustom.promptPassphrase(
'Passphrase to sign a message',
'Enter your passphrase for this account to sign the message',
'',
promptCb,
false
)
}
promptCb()
})
}
// TODO: unclear what's the goal of accountListCallId, feels like it can be simplified
async fillAccountsList () {
this.accountListCallId++
const callid = this.accountListCallId
const txOrigin = this.el.querySelector('#txorigin')
let accounts = []
try {
accounts = await this.blockchain.getAccounts()
} catch (e) {
addTooltip(`Cannot get account list: ${e}`)
}
if (!accounts) accounts = []
if (this.accountListCallId > callid) return
this.accountListCallId++
for (const loadedaddress in this.loadedAccounts) {
if (accounts.indexOf(loadedaddress) === -1) {
txOrigin.removeChild(txOrigin.querySelector('option[value="' + loadedaddress + '"]'))
delete this.loadedAccounts[loadedaddress]
}
}
for (const i in accounts) {
const address = accounts[i]
if (!this.loadedAccounts[address]) {
txOrigin.appendChild(yo`<option value="${address}" >${address}</option>`)
this.loadedAccounts[address] = 1
}
}
txOrigin.setAttribute('value', accounts[0])
}
}
module.exports = SettingsUI

@ -1,225 +0,0 @@
var csjs = require('csjs-inject')
var css = csjs`
.runTabView {
display: flex;
flex-direction: column;
}
.runTabView::-webkit-scrollbar {
display: none;
}
.settings {
padding: 0 24px 16px;
}
.crow {
display: block;
margin-top: 8px;
}
.col1 {
width: 30%;
float: left;
align-self: center;
}
.settingsLabel {
font-size: 11px;
margin-bottom: 4px;
text-transform: uppercase;
}
.environment {
display: flex;
align-items: center;
position: relative;
width: 100%;
}
.environment a {
margin-left: 7px;
}
.account {
display: flex;
align-items: center;
}
.account i {
margin-left: 12px;
}
.col2 {
border-radius: 3px;
}
.col2_1 {
width: 164px;
min-width: 164px;
}
.col2_2 {
}
.select {
font-weight: normal;
width: 100%;
overflow: hidden;
}
.instanceContainer {
display: flex;
flex-direction: column;
margin-bottom: 2%;
border: none;
text-align: center;
padding: 0 14px 16px;
}
.pendingTxsContainer {
display: flex;
flex-direction: column;
margin-top: 2%;
border: none;
text-align: center;
}
.container {
padding: 0 24px 16px;
}
.recorderDescription {
margin: 0 15px 15px 0;
}
.contractNames {
width: 100%;
border: 1px solid
}
.subcontainer {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 8px;
}
.subcontainer i {
width: 16px;
display: flex;
justify-content: center;
margin-left: 1px;
}
.button button{
flex: none;
}
.button {
display: flex;
align-items: center;
margin-top: 13px;
}
.transaction {
}
.atAddress {
margin: 0;
min-width: 100px;
width: 100px;
height: 100%;
word-break: inherit;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
}
.atAddressSect {
margin-top: 8px;
height: 32px;
}
.atAddressSect input {
height: 32px;
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.ataddressinput {
padding: .25rem;
}
.create {
}
.input {
font-size: 10px !important;
}
.noInstancesText {
font-style: italic;
text-align: left;
padding-left: 15px;
}
.pendingTxsText {
font-style: italic;
display: flex;
justify-content: space-evenly;
align-items: center;
flex-wrap: wrap;
}
.item {
margin-right: 1em;
display: flex;
align-items: center;
}
.pendingContainer {
display: flex;
align-items: baseline;
}
.pending {
height: 25px;
text-align: center;
padding-left: 10px;
border-radius: 3px;
margin-left: 5px;
}
.disableMouseEvents {
pointer-events: none;
}
.icon {
cursor: pointer;
font-size: 12px;
cursor: pointer;
margin-left: 5px;
}
.icon:hover {
font-size: 12px;
color: var(--warning);
}
.errorIcon {
color: var(--warning);
margin-left: 15px;
}
.failDesc {
color: var(--warning);
padding-left: 10px;
display: inline;
}
.network {
margin-left: 8px;
pointer-events: none;
}
.networkItem {
margin-right: 5px;
}
.transactionActions {
display: flex;
justify-content: space-evenly;
width: 145px;
}
.orLabel {
text-align: center;
text-transform: uppercase;
}
.infoDeployAction {
margin-left: 1px;
font-size: 13px;
color: var(--info);
}
.gasValueContainer {
flex-direction: row;
display: flex;
}
.gasNval {
width: 55%;
font-size: 0.8rem;
}
.gasNvalUnit {
width: 41%;
margin-left: 10px;
font-size: 0.8rem;
}
.deployDropdown {
text-align: center;
text-transform: uppercase;
}
.checkboxAlign {
padding-top: 2px;
}
`
module.exports = css

@ -1,212 +0,0 @@
'use strict'
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var css = csjs`
.li_tv {
list-style-type: none;
-webkit-margin-before: 0px;
-webkit-margin-after: 0px;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
-webkit-padding-start: 0px;
}
.ul_tv {
list-style-type: none;
-webkit-margin-before: 0px;
-webkit-margin-after: 0px;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
-webkit-padding-start: 0px;
}
.caret_tv {
width: 10px;
flex-shrink: 0;
padding-right: 5px;
}
.label_item {
word-break: break-all;
}
.label_key {
min-width: max-content;
max-width: 80%;
word-break: break-word;
}
.label_value {
min-width: 10%;
}
.cursor_pointer {
cursor: pointer;
}
`
var EventManager = require('../../lib/events')
/**
* TreeView
* - extendable by specifying custom `extractData` and `formatSelf` function
* - trigger `nodeClick` and `leafClick`
*/
class TreeView {
constructor (opts) {
this.event = new EventManager()
this.extractData = opts.extractData || this.extractDataDefault
this.formatSelf = opts.formatSelf || this.formatSelfDefault
this.loadMore = opts.loadMore
this.view = null
this.expandPath = []
}
render (json, expand) {
var view = this.renderProperties(json, expand)
if (!this.view) {
this.view = view
}
return view
}
update (json) {
if (this.view) {
yo.update(this.view, this.render(json))
}
}
renderObject (item, parent, key, expand, keyPath) {
var data = this.extractData(item, parent, key)
var children = (data.children || []).map((child, index) => {
return this.renderObject(child.value, data, child.key, expand, keyPath + '/' + child.key)
})
return this.formatData(key, data, children, expand, keyPath)
}
renderProperties (json, expand, key) {
key = key || ''
var children = Object.keys(json).map((innerkey) => {
return this.renderObject(json[innerkey], json, innerkey, expand, innerkey)
})
return yo`<ul key=${key} data-id="treeViewUl${key}" class="${css.ul_tv} ml-0 px-2">${children}</ul>`
}
formatData (key, data, children, expand, keyPath) {
var self = this
var li = yo`<li key=${keyPath} data-id="treeViewLi${keyPath}" class=${css.li_tv}></li>`
var caret = yo`<div class="px-1 fas fa-caret-right caret ${css.caret_tv}"></div>`
var label = yo`
<div key=${keyPath} data-id="treeViewDiv${keyPath}" class="d-flex flex-row align-items-center">
${caret}
<span class="w-100">${self.formatSelf(key, data, li)}</span>
</div>`
const expanded = self.expandPath.includes(keyPath)
li.appendChild(label)
if (data.children) {
var list = yo`<ul key=${keyPath} data-id="treeViewUlList${keyPath}" class="pl-2 ${css.ul_tv}">${children}</ul>`
list.style.display = expanded ? 'block' : 'none'
caret.className = list.style.display === 'none' ? `fas fa-caret-right caret ${css.caret_tv}` : `fas fa-caret-down caret ${css.caret_tv}`
caret.setAttribute('data-id', `treeViewToggle${keyPath}`)
label.onclick = function () {
self.expand(keyPath)
if (self.isExpanded(keyPath)) {
if (!self.expandPath.includes(keyPath)) self.expandPath.push(keyPath)
} else {
self.expandPath = self.expandPath.filter(path => !path.startsWith(keyPath))
}
}
label.oncontextmenu = function (event) {
self.event.trigger('nodeRightClick', [keyPath, data, label, event])
}
li.appendChild(list)
if (data.hasNext) {
list.appendChild(yo`<li><span class="w-100 text-primary ${css.cursor_pointer}" data-id="treeViewLoadMore" onclick="${() => self.loadMore(data.cursor)}">Load more</span></li>`)
}
} else {
caret.style.visibility = 'hidden'
label.oncontextmenu = function (event) {
self.event.trigger('leafRightClick', [keyPath, data, label, event])
}
label.onclick = function (event) {
self.event.trigger('leafClick', [keyPath, data, label, event])
}
}
return li
}
isExpanded (path) {
var current = this.nodeAt(path)
if (current) {
return current.style.display !== 'none'
}
return false
}
expand (path) {
var caret = this.caretAt(path)
var node = this.nodeAt(path)
if (node) {
node.style.display = node.style.display === 'none' ? 'block' : 'none'
caret.className = node.style.display === 'none' ? `fas fa-caret-right caret ${css.caret_tv}` : `fas fa-caret-down caret ${css.caret_tv}`
this.event.trigger('nodeClick', [path, node])
}
}
caretAt (path) {
var label = this.labelAt(path)
if (label) {
return label.querySelector('.caret')
}
}
itemAt (path) {
return this.view.querySelector(`li[key="${path}"]`)
}
labelAt (path) {
return this.view.querySelector(`div[key="${path}"]`)
}
nodeAt (path) {
return this.view.querySelector(`ul[key="${path}"]`)
}
updateNodeFromJSON (path, jsonTree, expand) {
var newTree = this.renderProperties(jsonTree, expand, path)
var current = this.nodeAt(path)
if (current && current.parentElement) {
current.parentElement.replaceChild(newTree, current)
}
}
formatSelfDefault (key, data) {
return yo`
<div class="d-flex mt-2 flex-row ${css.label_item}">
<label class="small font-weight-bold pr-1 ${css.label_key}">${key}:</label>
<label class="m-0 ${css.label_value}">${data.self}</label>
</div>
`
}
extractDataDefault (item, parent, key) {
var ret = {}
if (item instanceof Array) {
ret.children = item.map((item, index) => {
return { key: index, value: item }
})
ret.self = 'Array'
ret.isNode = true
ret.isLeaf = false
} else if (item instanceof Object) {
ret.children = Object.keys(item).map((key) => {
return { key: key, value: item[key] }
})
ret.self = 'Object'
ret.isNode = true
ret.isLeaf = false
} else {
ret.self = item
ret.children = null
ret.isNode = false
ret.isLeaf = true
}
return ret
}
}
module.exports = TreeView

@ -1,142 +0,0 @@
var yo = require('yo-yo')
var csjs = require('csjs-inject')
const copyToClipboard = require('./copy-to-clipboard')
const Web3 = require('web3')
var css = csjs`
#confirmsetting {
z-index: 1;
}
.txInfoBox {
}
.wrapword {
white-space: pre-wrap; /* Since CSS 2.1 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
`
function confirmDialog (tx, network, amount, gasEstimation, newGasPriceCb, initialParamsCb) {
const onGasPriceChange = function () {
var gasPrice = el.querySelector('#gasprice').value
newGasPriceCb(gasPrice, (txFeeText, priceStatus) => {
el.querySelector('#txfee').innerHTML = txFeeText
el.gasPriceStatus = priceStatus
el.txFee = { gasPrice }
})
}
const onMaxFeeChange = function () {
var maxFee = el.querySelector('#maxfee').value
var confirmBtn = document.querySelector('#modal-footer-ok')
var maxPriorityFee = el.querySelector('#maxpriorityfee').value
if (parseInt(network.lastBlock.baseFeePerGas, 16) > Web3.utils.toWei(maxFee, 'Gwei')) {
el.querySelector('#txfee').innerHTML = 'Transaction is invalid. Max fee should not be less than Base fee'
el.gasPriceStatus = false
confirmBtn.hidden = true
return
} else {
el.gasPriceStatus = true
confirmBtn.hidden = false
}
newGasPriceCb(maxFee, (txFeeText, priceStatus) => {
el.querySelector('#txfee').innerHTML = txFeeText
if (priceStatus) {
confirmBtn.hidden = false
} else {
confirmBtn.hidden = true
}
el.gasPriceStatus = priceStatus
el.txFee = { maxFee, maxPriorityFee, baseFeePerGas: network.lastBlock.baseFeePerGas }
})
}
const el = yo`
<div>
<div class="text-dark">You are about to create a transaction on ${network.name} Network. Confirm the details to send the info to your provider.
<br>The provider for many users is MetaMask. The provider will ask you to sign the transaction before it is sent to ${network.name} Network.
</div>
<div class="mt-3 ${css.txInfoBox}">
<div>
<span class="text-dark mr-2">From:</span>
<span>${tx.from}</span>
</div>
<div>
<span class="text-dark mr-2">To:</span>
<span>${tx.to ? tx.to : '(Contract Creation)'}</span>
</div>
<div class="d-flex align-items-center">
<span class="text-dark mr-2">Data:</span>
<pre class="${css.wrapword} mb-0">${tx.data && tx.data.length > 50 ? tx.data.substring(0, 49) + '...' : tx.data} ${copyToClipboard(() => { return tx.data })}</pre>
</div>
<div class="mb-3">
<span class="text-dark mr-2">Amount:</span>
<span>${amount} Ether</span>
</div>
<div>
<span class="text-dark mr-2">Gas estimation:</span>
<span>${gasEstimation}</span>
</div>
<div>
<span class="text-dark mr-2">Gas limit:</span>
<span>${tx.gas}</span>
</div>
${
network.lastBlock.baseFeePerGas ? yo`
<div class="align-items-center my-1" title="Represents the part of the tx fee that goes to the miner.">
<div class='d-flex'>
<span class="text-dark mr-2 text-nowrap">Max Priority fee:</span>
<input class="form-control mr-1 text-right" style='height: 1.2rem; width: 6rem;' value="1" id='maxpriorityfee' />
<span title="visit https://ethgasstation.info for current gas price info.">Gwei</span>
</div>
</div>
<div class="align-items-center my-1" title="Represents the maximum amount of fee that you will pay for this transaction. The minimun needs to be set to base fee.">
<div class='d-flex'>
<span class="text-dark mr-2 text-nowrap">Max fee (Not less than base fee ${Web3.utils.fromWei(Web3.utils.toBN(parseInt(network.lastBlock.baseFeePerGas, 16)), 'Gwei')} Gwei):</span>
<input class="form-control mr-1 text-right" style='height: 1.2rem; width: 6rem;' id='maxfee' oninput=${onMaxFeeChange} />
<span>Gwei</span>
<span class="text-dark ml-2"></span>
</div>
</div>`
: yo`<div class="d-flex align-items-center my-1">
<span class="text-dark mr-2 text-nowrap">Gas price:</span>
<input class="form-control mr-1 text-right" style='width: 40px; height: 28px;' id='gasprice' oninput=${onGasPriceChange} />
<span>Gwei (visit <a target='_blank' href='https://ethgasstation.info'>ethgasstation.info</a> for current gas price info.)</span>
</div>`
}
<div class="mb-3">
<span class="text-dark mr-2">Max transaction fee:</span>
<span class="text-warning" id='txfee'></span>
</div>
</div>
<div class="d-flex py-1 align-items-center custom-control custom-checkbox ${css.checkbox}">
<input class="form-check-input custom-control-input" id="confirmsetting" type="checkbox">
<label class="m-0 form-check-label custom-control-label" for="confirmsetting">Do not show this warning again.</label>
</div>
</div>
`
initialParamsCb((txFeeText, gasPriceValue, gasPriceStatus) => {
if (txFeeText) {
el.querySelector('#txfee').innerHTML = txFeeText
}
if (el.querySelector('#gasprice') && gasPriceValue) {
el.querySelector('#gasprice').value = gasPriceValue
onGasPriceChange()
}
if (el.querySelector('#maxfee') && network && network.lastBlock && network.lastBlock.baseFeePerGas) {
el.querySelector('#maxfee').value = Web3.utils.fromWei(Web3.utils.toBN(parseInt(network.lastBlock.baseFeePerGas, 16)), 'Gwei')
onMaxFeeChange()
}
if (gasPriceStatus !== undefined) {
el.gasPriceStatus = gasPriceStatus
}
})
return el
}
module.exports = confirmDialog

@ -1,228 +0,0 @@
'use strict'
var yo = require('yo-yo')
var css = require('../../universal-dapp-styles')
var copyToClipboard = require('./copy-to-clipboard')
var remixLib = require('@remix-project/remix-lib')
var txFormat = remixLib.execution.txFormat
class MultiParamManager {
/**
*
* @param {bool} lookupOnly
* @param {Object} funABI
* @param {Function} clickMultiCallBack
* @param {string} inputs
* @param {string} title
* @param {string} evmBC
*
*/
constructor (lookupOnly, funABI, clickCallBack, inputs, title, evmBC, isDeploy) {
this.lookupOnly = lookupOnly
this.funABI = funABI
this.clickCallBack = clickCallBack
this.inputs = inputs
this.title = title
this.evmBC = evmBC
this.basicInputField = null
this.multiFields = null
this.isDeploy = isDeploy
}
switchMethodViewOn () {
this.contractActionsContainerSingle.style.display = 'none'
this.contractActionsContainerMulti.style.display = 'flex'
this.makeMultiVal()
}
switchMethodViewOff () {
this.contractActionsContainerSingle.style.display = 'flex'
this.contractActionsContainerMulti.style.display = 'none'
var multiValString = this.getMultiValsString()
if (multiValString) this.basicInputField.value = multiValString
}
getValue (item, index) {
var valStr = item.value.join('')
return valStr
}
getMultiValsString () {
var valArray = this.multiFields.querySelectorAll('input')
var ret = ''
var valArrayTest = []
for (var j = 0; j < valArray.length; j++) {
if (ret !== '') ret += ','
var elVal = valArray[j].value
valArrayTest.push(elVal)
elVal = elVal.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number
elVal = elVal.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string
try {
JSON.parse(elVal)
} catch (e) {
elVal = '"' + elVal + '"'
}
ret += elVal
}
var valStringTest = valArrayTest.join('')
if (valStringTest) {
return ret
} else {
return ''
}
}
emptyInputs () {
var valArray = this.multiFields.querySelectorAll('input')
for (var k = 0; k < valArray.length; k++) {
valArray[k].value = ''
}
this.basicInputField.value = ''
}
makeMultiVal () {
var inputString = this.basicInputField.value
if (inputString) {
inputString = inputString.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number
inputString = inputString.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string
var inputJSON = JSON.parse('[' + inputString + ']')
var multiInputs = this.multiFields.querySelectorAll('input')
for (var k = 0; k < multiInputs.length; k++) {
if (inputJSON[k]) {
multiInputs[k].value = JSON.stringify(inputJSON[k])
}
}
}
}
createMultiFields () {
if (this.funABI.inputs) {
return yo`<div>
${this.funABI.inputs.map(function (inp) {
return yo`<div class="${css.multiArg}"><label for="${inp.name}"> ${inp.name}: </label><input class="form-control" placeholder="${inp.type}" title="${inp.name}" data-id="multiParamManagerInput${inp.name}"></div>`
})}
</div>`
}
}
render () {
var title
if (this.title) {
title = this.title
} else if (this.funABI.name) {
title = this.funABI.name
} else {
title = this.funABI.type === 'receive' ? '(receive)' : '(fallback)'
}
this.basicInputField = yo`<input class="form-control" data-id="multiParamManagerBasicInputField"></input>`
this.basicInputField.setAttribute('placeholder', this.inputs)
this.basicInputField.setAttribute('title', this.inputs)
this.basicInputField.setAttribute('data-id', this.inputs)
var onClick = () => {
this.clickCallBack(this.funABI.inputs, this.basicInputField.value)
}
const width = this.isDeploy ? '' : 'w-50'
const funcButton = yo`<button onclick=${() => onClick()} class="${css.instanceButton} ${width} btn btn-sm" data-id="multiParamManagerFuncButton">${title}</button>`
this.contractActionsContainerSingle = yo`
<div class="${css.contractActionsContainerSingle} pt-2">
${funcButton}
${this.basicInputField}
<i class="fas fa-angle-down ${css.methCaret}" onclick=${() => this.switchMethodViewOn()} title=${title} ></i>
</div>`
this.multiFields = this.createMultiFields()
var multiOnClick = () => {
var valsString = this.getMultiValsString()
if (valsString) {
this.clickCallBack(this.funABI.inputs, valsString)
} else {
this.clickCallBack(this.funABI.inputs, '')
}
}
var expandedButton = yo`<button onclick=${() => { multiOnClick() }} class="${css.instanceButton}" data-id="multiParamManagerExpandedButton"></button>`
this.contractActionsContainerMulti = yo`<div class="${css.contractActionsContainerMulti}" >
<div class="${css.contractActionsContainerMultiInner} text-dark" >
<div onclick=${() => { this.switchMethodViewOff() }} class="${css.multiHeader}">
<div class="${css.multiTitle} run-instance-multi-title">${title}</div>
<i class='fas fa-angle-up ${css.methCaret}'></i>
</div>
${this.multiFields}
<div class="${css.group} ${css.multiArg}" >
${copyToClipboard(
() => {
var multiString = this.getMultiValsString()
var multiJSON = JSON.parse('[' + multiString + ']')
var encodeObj
if (this.evmBC) {
encodeObj = txFormat.encodeData(this.funABI, multiJSON, this.evmBC)
} else {
encodeObj = txFormat.encodeData(this.funABI, multiJSON)
}
if (encodeObj.error) {
throw new Error(encodeObj.error)
} else {
return encodeObj.data
}
}, 'Encode values of input fields & copy to clipboard', 'fa-clipboard')}
${expandedButton}
</div>
</div>
</div>`
var contractProperty = yo`
<div class="${css.contractProperty}">
${this.contractActionsContainerSingle} ${this.contractActionsContainerMulti}
</div>
`
if (this.lookupOnly) {
// call. stateMutability is either pure or view
expandedButton.setAttribute('title', (title + ' - call'))
expandedButton.innerHTML = 'call'
expandedButton.classList.add('btn-info')
expandedButton.setAttribute('data-id', (title + ' - call'))
funcButton.setAttribute('title', (title + ' - call'))
funcButton.classList.add('btn-info')
funcButton.setAttribute('data-id', (title + ' - call'))
} else if (this.funABI.stateMutability === 'payable' || this.funABI.payable) {
// transact. stateMutability = payable
expandedButton.setAttribute('title', (title + ' - transact (payable)'))
expandedButton.innerHTML = 'transact'
expandedButton.classList.add('btn-danger')
expandedButton.setAttribute('data-id', (title + ' - transact (payable)'))
funcButton.setAttribute('title', (title + ' - transact (payable)'))
funcButton.classList.add('btn-danger')
funcButton.setAttribute('data-id', (title + ' - transact (payable)'))
} else {
// transact. stateMutability = nonpayable
expandedButton.setAttribute('title', (title + ' - transact (not payable)'))
expandedButton.innerHTML = 'transact'
expandedButton.classList.add('btn-warning')
expandedButton.setAttribute('data-id', (title + ' - transact (not payable)'))
funcButton.classList.add('btn-warning')
funcButton.setAttribute('title', (title + ' - transact (not payable)'))
funcButton.setAttribute('data-id', (title + ' - transact (not payable)'))
}
if (this.funABI.inputs && this.funABI.inputs.length > 0) {
contractProperty.classList.add(css.hasArgs)
} else if (this.funABI.type === 'fallback' || this.funABI.type === 'receive') {
contractProperty.classList.add(css.hasArgs)
this.basicInputField.setAttribute('title', `'(${this.funABI.type}')`) // probably should pass name instead
this.contractActionsContainerSingle.querySelector('i').style.visibility = 'hidden'
this.basicInputField.setAttribute('data-id', `'(${this.funABI.type}')`)
} else {
this.contractActionsContainerSingle.querySelector('i').style.visibility = 'hidden'
this.basicInputField.style.visibility = 'hidden'
}
return contractProperty
}
}
module.exports = MultiParamManager

@ -1,272 +0,0 @@
/* global */
'use strict'
var $ = require('jquery')
var yo = require('yo-yo')
var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var helper = require('../../lib/helper')
var copyToClipboard = require('./copy-to-clipboard')
var css = require('../../universal-dapp-styles')
var MultiParamManager = require('./multiParamManager')
var remixLib = require('@remix-project/remix-lib')
var txFormat = remixLib.execution.txFormat
const txHelper = remixLib.execution.txHelper
var TreeView = require('./TreeView')
var txCallBacks = require('./sendTxCallbacks')
const _paq = window._paq = window._paq || []
function UniversalDAppUI (blockchain, logCallback) {
this.blockchain = blockchain
this.logCallback = logCallback
this.compilerData = { contractsDetails: {} }
}
function decodeResponseToTreeView (response, fnabi) {
var treeView = new TreeView({
extractData: (item, parent, key) => {
var ret = {}
if (BN.isBN(item)) {
ret.self = item.toString(10)
ret.children = []
} else {
ret = treeView.extractDataDefault(item, parent, key)
}
return ret
}
})
return treeView.render(txFormat.decodeResponse(response, fnabi))
}
UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) {
var noInstances = document.querySelector('[data-id="deployAndRunNoInstanceText"]')
if (noInstances) {
noInstances.parentNode.removeChild(noInstances)
}
const abi = txHelper.sortAbiFunction(contract.abi)
return this.renderInstanceFromABI(abi, address, contractName, contract)
}
// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address, contractName, contract) {
const self = this
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
address = ethJSUtil.toChecksumAddress(address)
var instance = yo`<div class="instance run-instance border-dark ${css.instance} ${css.hidesub}" id="instance${address}" data-shared="universalDappUiInstance"></div>`
const context = this.blockchain.context()
var shortAddress = helper.shortenAddress(address)
var title = yo`
<div class="${css.title} alert alert-secondary">
<button data-id="universalDappUiTitleExpander" class="btn ${css.titleExpander}" onclick="${(e) => { toggleClass(e) }}">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</button>
<div class="input-group ${css.nameNbuts}">
<div class="${css.titleText} input-group-prepend">
<span class="input-group-text ${css.spanTitleText}">
${contractName} at ${shortAddress} (${context})
</span>
</div>
<div class="btn-group">
<button class="btn p-1 btn-secondary">${copyToClipboard(() => address)}</button>
</div>
</div>
</div>
`
var close = yo`
<button
class="${css.udappClose} mr-1 p-1 btn btn-secondary align-items-center"
data-id="universalDappUiUdappClose"
onclick=${remove}
title="Remove from the list"
>
<i class="${css.closeIcon} fas fa-times" aria-hidden="true"></i>
</button>`
title.querySelector('.btn-group').appendChild(close)
var contractActionsWrapper = yo`
<div class="${css.cActionsWrapper}" data-id="universalDappUiContractActionWrapper">
</div>
`
function remove () {
instance.remove()
// @TODO perhaps add a callack here to warn the caller that the instance has been removed
}
function toggleClass (e) {
$(instance).toggleClass(`${css.hidesub} bg-light`)
// e.currentTarget.querySelector('i')
e.currentTarget.querySelector('i').classList.toggle('fa-angle-right')
e.currentTarget.querySelector('i').classList.toggle('fa-angle-down')
}
instance.appendChild(title)
instance.appendChild(contractActionsWrapper)
$.each(contractABI, (i, funABI) => {
if (funABI.type !== 'function') {
return
}
// @todo getData cannot be used with overloaded functions
contractActionsWrapper.appendChild(this.getCallButton({
funABI: funABI,
address: address,
contractABI: contractABI,
contractName: contractName,
contract
}))
})
const calldataInput = yo`
<input id="deployAndRunLLTxCalldata" class="${css.calldataInput} form-control" title="The Calldata to send to fallback function of the contract.">
`
const llIError = yo`
<label id="deployAndRunLLTxError" class="text-danger my-2"></label>
`
// constract LLInteractions elements
const lowLevelInteracions = yo`
<div class="d-flex flex-column">
<div class="d-flex flex-row justify-content-between mt-2">
<div class="py-2 border-top d-flex justify-content-start flex-grow-1">
Low level interactions
</div>
<a
href="https://solidity.readthedocs.io/en/v0.6.2/contracts.html#receive-ether-function"
title="check out docs for using 'receive'/'fallback'"
target="_blank"
>
<i aria-hidden="true" class="fas fa-info my-2 mr-1"></i>
</a>
</div>
<div class="d-flex flex-column align-items-start">
<label class="">CALLDATA</label>
<div class="d-flex justify-content-end w-100 align-items-center">
${calldataInput}
<button id="deployAndRunLLTxSendTransaction" data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction" class="${css.instanceButton} p-0 w-50 btn border-warning text-warning" title="Send data to contract." onclick=${() => sendData()}>Transact</button>
</div>
</div>
<div>
${llIError}
</div>
</div>
`
function sendData () {
function setLLIError (text) {
llIError.innerText = text
}
setLLIError('')
const fallback = txHelper.getFallbackInterface(contractABI)
const receive = txHelper.getReceiveInterface(contractABI)
const args = {
funABI: fallback || receive,
address: address,
contractName: contractName,
contractABI: contractABI
}
const amount = document.querySelector('#value').value
if (amount !== '0') {
// check for numeric and receive/fallback
if (!helper.isNumeric(amount)) {
return setLLIError('Value to send should be a number')
} else if (!receive && !(fallback && fallback.stateMutability === 'payable')) {
return setLLIError("In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function")
}
}
let calldata = calldataInput.value
if (calldata) {
if (calldata.length < 4 && helper.is0XPrefixed(calldata)) {
return setLLIError('The calldata should be a valid hexadecimal value with size of at least one byte.')
} else {
if (helper.is0XPrefixed(calldata)) {
calldata = calldata.substr(2, calldata.length)
}
if (!helper.isHexadecimal(calldata)) {
return setLLIError('The calldata should be a valid hexadecimal value.')
}
}
if (!fallback) {
return setLLIError("'Fallback' function is not defined")
}
}
if (!receive && !fallback) return setLLIError('Both \'receive\' and \'fallback\' functions are not defined')
// we have to put the right function ABI:
// if receive is defined and that there is no calldata => receive function is called
// if fallback is defined => fallback function is called
if (receive && !calldata) args.funABI = receive
else if (fallback) args.funABI = fallback
if (!args.funABI) return setLLIError('Please define a \'Fallback\' function to send calldata and a either \'Receive\' or payable \'Fallback\' to send ethers')
self.runTransaction(false, args, null, calldataInput.value, null)
}
contractActionsWrapper.appendChild(lowLevelInteracions)
return instance
}
// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
UniversalDAppUI.prototype.getCallButton = function (args) {
const self = this
var outputOverride = yo`<div class=${css.value}></div>` // show return value
const isConstant = args.funABI.constant !== undefined ? args.funABI.constant : false
const lookupOnly = args.funABI.stateMutability === 'view' || args.funABI.stateMutability === 'pure' || isConstant
const multiParamManager = new MultiParamManager(
lookupOnly,
args.funABI,
(valArray, inputsValues) => self.runTransaction(lookupOnly, args, valArray, inputsValues, outputOverride),
self.blockchain.getInputs(args.funABI)
)
const contractActionsContainer = yo`<div class="${css.contractActionsContainer}" >${multiParamManager.render()}</div>`
contractActionsContainer.appendChild(outputOverride)
return contractActionsContainer
}
UniversalDAppUI.prototype.runTransaction = function (lookupOnly, args, valArr, inputsValues, outputOverride) {
const functionName = args.funABI.type === 'function' ? args.funABI.name : `(${args.funABI.type})`
const logMsg = `${lookupOnly ? 'call' : 'transact'} to ${args.contractName}.${functionName}`
const callbacksInContext = txCallBacks.getCallBacksWithContext(this, this.blockchain)
const outputCb = (returnValue) => {
if (outputOverride) {
const decoded = decodeResponseToTreeView(returnValue, args.funABI)
outputOverride.innerHTML = ''
outputOverride.appendChild(decoded)
}
}
let callinfo = ''
if (lookupOnly) callinfo = 'call'
else if (args.funABI.type === 'fallback' || args.funABI.type === 'receive') callinfo = 'lowLevelInteracions'
else callinfo = 'transact'
_paq.push(['trackEvent', 'udapp', callinfo, this.blockchain.getCurrentNetworkStatus().network.name])
const params = args.funABI.type !== 'fallback' ? inputsValues : ''
this.blockchain.runOrCallContractMethod(
args.contractName,
args.contractABI,
args.funABI,
args.contract,
inputsValues,
args.address,
params,
lookupOnly,
logMsg,
this.logCallback,
outputCb,
callbacksInContext.confirmationCb.bind(callbacksInContext),
callbacksInContext.continueCb.bind(callbacksInContext),
callbacksInContext.promptCb.bind(callbacksInContext))
}
module.exports = UniversalDAppUI

@ -1,121 +0,0 @@
'use strict'
const async = require('async')
const IpfsClient = require('ipfs-mini')
const ipfsNodes = [
new IpfsClient({ host: 'ipfs.remixproject.org', port: 443, protocol: 'https' }),
new IpfsClient({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' }),
new IpfsClient({ host: '127.0.0.1', port: 5001, protocol: 'http' })
]
module.exports = (contract, fileManager, cb, ipfsVerifiedPublishCallBack) => {
// gather list of files to publish
var sources = []
var metadata
try {
metadata = JSON.parse(contract.metadata)
} catch (e) {
return cb(e)
}
if (metadata === undefined) {
return cb('No metadata')
}
async.eachSeries(Object.keys(metadata.sources), function (fileName, cb) {
// find hash
let hash = null
try {
// we try extract the hash defined in the metadata.json
// in order to check if the hash that we get after publishing is the same as the one located in metadata.json
// if it's not the same, we throw "hash mismatch between solidity bytecode and uploaded content"
// if we don't find the hash in the metadata.json, the check is not done.
//
// TODO: refactor this with publishOnSwarm
if (metadata.sources[fileName].urls) {
metadata.sources[fileName].urls.forEach(url => {
if (url.includes('ipfs')) hash = url.match('dweb:/ipfs/(.+)')[1]
})
}
} catch (e) {
return cb('Error while extracting the hash from metadata.json')
}
fileManager.fileProviderOf(fileName).get(fileName, (error, content) => {
if (error) {
console.log(error)
} else {
sources.push({
content: content,
hash: hash,
filename: fileName
})
}
cb()
})
}, function (error) {
if (error) {
cb(error)
} else {
// publish the list of sources in order, fail if any failed
var uploaded = []
async.eachSeries(sources, function (item, cb) {
ipfsVerifiedPublish(item.content, item.hash, (error, result) => {
try {
item.hash = result.url.match('dweb:/ipfs/(.+)')[1]
} catch (e) {
item.hash = '<Metadata inconsistency> - ' + item.fileName
}
if (!error && ipfsVerifiedPublishCallBack) ipfsVerifiedPublishCallBack(item)
item.output = result
uploaded.push(item)
cb(error)
})
}, () => {
const metadataContent = JSON.stringify(metadata)
ipfsVerifiedPublish(metadataContent, '', (error, result) => {
try {
contract.metadataHash = result.url.match('dweb:/ipfs/(.+)')[1]
} catch (e) {
contract.metadataHash = '<Metadata inconsistency> - metadata.json'
}
if (!error && ipfsVerifiedPublishCallBack) {
ipfsVerifiedPublishCallBack({
content: metadataContent,
hash: contract.metadataHash
})
}
uploaded.push({
content: contract.metadata,
hash: contract.metadataHash,
filename: 'metadata.json',
output: result
})
cb(error, uploaded)
})
})
}
})
}
async function ipfsVerifiedPublish (content, expectedHash, cb) {
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 })
} else {
cb(null, { message: 'ok', url: 'dweb:/ipfs/' + results, hash: results })
}
} catch (error) {
cb(error)
}
}
function severalGatewaysPush (content) {
const invert = p => new Promise((resolve, reject) => p.then(reject).catch(resolve)) // Invert res and rej
const promises = ipfsNodes.map((node) => invert(node.add(content)))
return invert(Promise.all(promises))
}

@ -1,109 +0,0 @@
'use strict'
var async = require('async')
var swarmgw = require('swarmgw')()
module.exports = (contract, fileManager, cb, swarmVerifiedPublishCallBack) => {
// gather list of files to publish
var sources = []
var metadata
try {
metadata = JSON.parse(contract.metadata)
} catch (e) {
return cb(e)
}
if (metadata === undefined) {
return cb('No metadata')
}
async.eachSeries(Object.keys(metadata.sources), function (fileName, cb) {
// find hash
let hash = null
try {
// we try extract the hash defined in the metadata.json
// in order to check if the hash that we get after publishing is the same as the one located in metadata.json
// if it's not the same, we throw "hash mismatch between solidity bytecode and uploaded content"
// if we don't find the hash in the metadata.json, the check is not done.
//
// TODO: refactor this with publishOnIpfs
if (metadata.sources[fileName].urls) {
metadata.sources[fileName].urls.forEach(url => {
if (url.includes('bzz')) hash = url.match('(bzzr|bzz-raw)://(.+)')[1]
})
}
} catch (e) {
return cb('Error while extracting the hash from metadata.json')
}
fileManager.fileProviderOf(fileName).get(fileName, (error, content) => {
if (error) {
console.log(error)
} else {
sources.push({
content: content,
hash: hash,
filename: fileName
})
}
cb()
})
}, function (error) {
if (error) {
cb(error)
} else {
// publish the list of sources in order, fail if any failed
var uploaded = []
async.eachSeries(sources, function (item, cb) {
swarmVerifiedPublish(item.content, item.hash, (error, result) => {
try {
item.hash = result.url.match('bzz-raw://(.+)')[1]
} catch (e) {
item.hash = '<Metadata inconsistency> - ' + item.fileName
}
if (!error && swarmVerifiedPublishCallBack) swarmVerifiedPublishCallBack(item)
item.output = result
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
cb(error)
})
}, () => {
const metadataContent = JSON.stringify(metadata)
swarmVerifiedPublish(metadataContent, '', (error, result) => {
try {
contract.metadataHash = result.url.match('bzz-raw://(.+)')[1]
} catch (e) {
contract.metadataHash = '<Metadata inconsistency> - metadata.json'
}
if (!error && swarmVerifiedPublishCallBack) {
swarmVerifiedPublishCallBack({
content: metadataContent,
hash: contract.metadataHash
})
}
uploaded.push({
content: contract.metadata,
hash: contract.metadataHash,
filename: 'metadata.json',
output: result
})
cb(error, uploaded)
})
})
}
})
}
function 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 })
}
})
}

@ -1,47 +0,0 @@
const yo = require('yo-yo')
const publishOnSwarm = require('./lib/publishOnSwarm')
const publishOnIpfs = require('./lib/publishOnIpfs')
const modalDialogCustom = require('./app/ui/modal-dialog-custom')
export default function publish (storage, fileProvider, fileManager, contract) {
if (contract) {
if (contract.metadata === undefined || contract.metadata.length === 0) {
modalDialogCustom.alert('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') {
publishOnSwarm(contract, fileManager, function (err, uploaded) {
if (err) {
try {
err = JSON.stringify(err)
} catch (e) {}
console.log(`Failed to publish metadata file to swarm, please check the Swarm gateways is available ( swarm-gateways.net ) ${err}`)
} else {
var result = yo`<div>${uploaded.map((value) => {
return yo`<div><b>${value.filename}</b> : <pre>${value.output.url}</pre></div>`
})}</div>`
modalDialogCustom.alert(`Published ${contract.name}'s Metadata`, yo`<span>Metadata of "${contract.name.toLowerCase()}" was published successfully.<br> <pre>${result}</pre> </span>`)
}
}, (item) => { // triggered each time there's a new verified publish (means hash correspond)
fileProvider.addExternal('swarm/' + item.hash, item.content)
})
} else {
publishOnIpfs(contract, fileManager, function (err, uploaded) {
if (err) {
try {
err = JSON.stringify(err)
} catch (e) {}
modalDialogCustom.alert(yo`<span>Failed to publish metadata file to ${storage}, please check the ${storage} gateways is available.<br />
${err}</span>`)
} else {
var result = yo`<div>${uploaded.map((value) => {
return yo`<div><b>${value.filename}</b> : <pre>${value.output.url.replace('dweb:/ipfs/', 'ipfs://')}</pre></div>`
})}</div>`
modalDialogCustom.alert(`Published ${contract.name}'s Metadata`, yo`<span>Metadata of "${contract.name.toLowerCase()}" was published successfully.<br> <pre>${result}</pre> </span>`)
}
}, (item) => { // triggered each time there's a new verified publish (means hash correspond)
fileProvider.addExternal('ipfs/' + item.hash, item.content)
})
}
}
}
}

@ -1,285 +0,0 @@
const csjs = require('csjs-inject')
var css = csjs`
.instanceTitleContainer {
display: flex;
align-items: center;
}
.calldataInput{
height: 32px;
}
.title {
display: flex;
justify-content: space-between;
font-size: 11px;
width: 100%;
overflow: hidden;
word-break: break-word;
line-height: initial;
overflow: visible;
padding: 0 0 8px;
margin: 0;
background: none;
border: none;
}
.title button {
background: none;
border: none;
}
.titleLine {
display: flex;
align-items: baseline;
}
.titleText {
word-break: break-word;
width: 100%;
border: none;
overflow: hidden;
}
.spanTitleText {
line-height: 12px;
padding: 0;
font-size: 11px;
width:100%;
border: none;
background: none;
text-transform: uppercase;
overflow: hidden;
}
.inputGroupText {
width: 100%;
}
.title .copy {
color: var(--primary);
}
.titleExpander {
padding: 5px 7px;
}
.nameNbuts {
display: contents;
flex-wrap: nowrap;
width: 100%;
}
.instance {
display: block;
flex-direction: column;
margin-bottom: 12px;
background: none;
border-radius: 2px;
}
.instance.hidesub {
border-bottom: 1px solid;
}
.instance.hidesub .title {
display: flex;
}
.instance.hidesub .udappClose {
display: flex;
}
.instance.hidesub > * {
display: none;
}
.methCaret {
min-width: 12px;
width: 12px;
margin-left: 4px;
cursor: pointer;
font-size: 16px;
line-height: 0.6;
vertical-align: middle;
padding: 0;
}
.cActionsWrapper {
border-top-left-radius: 0;
border-bottom-left-radius: 0.25rem;
border-top-rightt-radius: 0;
border-bottom-right-radius: 0.25rem;
padding: 8px 10px 7px;
}
.group:after {
content: "";
display: table;
clear: both;
}
.buttonsContainer {
margin-top: 2%;
display: flex;
overflow: hidden;
}
.instanceButton {
height: 32px;
border-radius: 3px;
white-space: nowrap;
font-size: 11px;
overflow: hidden;
text-overflow: ellipsis;
}
.closeIcon {
font-size: 12px;
cursor: pointer;
margin-left: 5px;
}
.udappClose {
display: flex;
justify-content: flex-end;
}
.contractProperty {
width:100%;
}
.contractProperty.hasArgs input {
padding: .36em;
border-radius: 5px;
}
.contractProperty .contractActionsContainerSingle input{
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.contractProperty button {
min-width: 100px;
width: 100px;
margin:0;
word-break: inherit;
}
.contractProperty button:disabled {
cursor: not-allowed;
background-color: white;
border-color: lightgray;
}
.contractProperty.constant button {
min-width: 100px;
width: 100px;
margin:0;
word-break: inherit;
outline: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.contractProperty > .value {
box-sizing: border-box;
float: left;
align-self: center;
margin-left: 4px;
}
.contractActionsContainer {
width: 100%;
margin-bottom: 8px;
}
.contractActionsContainerSingle {
display: flex;
width: 100%;
}
.contractActionsContainerSingle i {
line-height: 2;
}
.contractActionsContainerMulti {
display:none;
width: 100%;
}
.contractActionsContainerMultiInner {
width: 100%;
padding: 16px 8px 16px 14px;
border-radius: 3px;
margin-bottom: 8px;
}
.multiHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
text-align: left;
font-size: 10px;
font-weight: bold;
}
.contractActionsContainerMultiInner .multiTitle {
padding-left: 10px;
}
.contractProperty .multiTitle {
padding: 0;
line-height: 16px;
display: inline-block;
font-size: 12px;
font-weight: bold;
cursor: default;
}
.contractProperty .contractActionsContainerMultiInner .multiArg label{
text-align: right;
}
.multiHeader .methCaret {
float: right;
margin-right: 0;
}
.contractProperty.constant .multiTitle {
display: inline-block;
width: 90%;
/* font-size: 10px; */
height: 25px;
padding-left: 20px;
font-weight: bold;
line-height: 25px;
cursor: default;
}
.multiArg {
display: flex;
align-items: center;
justify-content: flex-end;
margin-top: 4px;
}
.multiArg input{
padding: 5px;
}
.multiArg label {
width: auto;
padding: 0;
margin: 0 4px 0 0;
font-size: 10px;
line-height: 12px;
text-align: right;
word-break: initial;
}
.multiArg button {
max-width: 100px;
border-radius: 3px;
border-width: 1px;
width: inherit;
}
.multiHeader button {
display: inline-block;
width: 94%;
}
.hasArgs .multiArg input {
border-left: 1px solid #dddddd;
width: 67%;
}
.hasArgs input {
display: block;
height: 32px;
border: 1px solid #dddddd;
padding: .36em;
border-left: none;
padding: 8px 8px 8px 10px;
font-size: 10px !important;
}
.hasArgs button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 11px;
}
.hasArgs .contractActionsContainerMulti button {
border-radius: 3px;
}
.contractActionsContainerMultiInner .multiArg i {
padding-right: 10px;
}
.hideWarningsContainer {
display: flex;
align-items: center;
margin-left: 2%
}
`
module.exports = css
Loading…
Cancel
Save