parent
57c73094d8
commit
9dc1ad76db
@ -0,0 +1,391 @@ |
|||||||
|
var yo = require('yo-yo') |
||||||
|
var ethJSUtil = require('ethereumjs-util') |
||||||
|
var css = require('../styles/run-tab-styles') |
||||||
|
var executionContext = require('../../../execution-context') |
||||||
|
var modalDialogCustom = require('../../ui/modal-dialog-custom') |
||||||
|
var modalCustom = require('../../ui/modal-dialog-custom') |
||||||
|
var CompilerAbstract = require('../../compiler/compiler-abstract') |
||||||
|
var remixLib = require('remix-lib') |
||||||
|
var txExecution = remixLib.execution.txExecution |
||||||
|
var txFormat = remixLib.execution.txFormat |
||||||
|
var txHelper = remixLib.execution.txHelper |
||||||
|
var typeConversion = remixLib.execution.typeConversion |
||||||
|
var confirmDialog = require('../../execution/confirmDialog') |
||||||
|
var modalDialog = require('../../ui/modaldialog') |
||||||
|
var MultiParamManager = require('../../../multiParamManager') |
||||||
|
|
||||||
|
function contractDropdown (events, self) { |
||||||
|
var instanceContainer = self._view.instanceContainer |
||||||
|
var instanceContainerTitle = self._view.instanceContainerTitle |
||||||
|
instanceContainer.appendChild(instanceContainerTitle) |
||||||
|
instanceContainer.appendChild(self._view.noInstancesText) |
||||||
|
var compFails = yo`<i title="Contract compilation failed. Please check the compile tab for more information." class="fa fa-times-circle ${css.errorIcon}" ></i>` |
||||||
|
var info = yo`<i class="fa fa-info ${css.infoDeployAction}" aria-hidden="true" title="*.sol files allows deploying and accessing contracts. *.abi files only allows accessing contracts."></i>` |
||||||
|
|
||||||
|
var newlyCompiled = (success, data, source, compiler, compilerFullName) => { |
||||||
|
getContractNames(success, data, compiler, compilerFullName) |
||||||
|
if (success) { |
||||||
|
compFails.style.display = 'none' |
||||||
|
document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) |
||||||
|
} else { |
||||||
|
compFails.style.display = 'block' |
||||||
|
document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
self._deps.pluginManager.event.register('sendCompilationResult', (file, source, languageVersion, data) => { |
||||||
|
// TODO check whether the tab is configured
|
||||||
|
let compiler = new CompilerAbstract(languageVersion, data) |
||||||
|
self._deps.compilersArtefacts[languageVersion] = compiler |
||||||
|
self._deps.compilersArtefacts['__last'] = compiler |
||||||
|
newlyCompiled(true, data, source, compiler, languageVersion) |
||||||
|
}) |
||||||
|
|
||||||
|
self._deps.compiler.event.register('compilationFinished', (success, data, source) => { |
||||||
|
var name = 'solidity' |
||||||
|
let compiler = new CompilerAbstract(name, data) |
||||||
|
self._deps.compilersArtefacts[name] = compiler |
||||||
|
self._deps.compilersArtefacts['__last'] = compiler |
||||||
|
newlyCompiled(success, data, source, self._deps.compiler, name) |
||||||
|
}) |
||||||
|
|
||||||
|
var deployAction = (value) => { |
||||||
|
self._view.createPanel.style.display = value |
||||||
|
self._view.orLabel.style.display = value |
||||||
|
} |
||||||
|
|
||||||
|
self._deps.fileManager.event.register('currentFileChanged', (currentFile) => { |
||||||
|
document.querySelector(`.${css.contractNames}`).classList.remove(css.contractNamesError) |
||||||
|
var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) |
||||||
|
contractNames.innerHTML = '' |
||||||
|
if (/.(.abi)$/.exec(currentFile)) { |
||||||
|
deployAction('none') |
||||||
|
compFails.style.display = 'none' |
||||||
|
contractNames.appendChild(yo`<option>(abi)</option>`) |
||||||
|
selectContractNames.setAttribute('disabled', true) |
||||||
|
} else if (/.(.sol)$/.exec(currentFile)) { |
||||||
|
deployAction('block') |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
var atAddressButtonInput = yo`<input class="${css.input} ataddressinput" placeholder="Load contract from Address" title="atAddress" />` |
||||||
|
var selectContractNames = yo`<select class="${css.contractNames}" disabled></select>` |
||||||
|
|
||||||
|
function getSelectedContract () { |
||||||
|
var contract = selectContractNames.children[selectContractNames.selectedIndex] |
||||||
|
var contractName = contract.innerHTML |
||||||
|
var compiler = self._deps.compilersArtefacts[contract.getAttribute('compiler')] |
||||||
|
if (!compiler) return null |
||||||
|
|
||||||
|
if (contractName) { |
||||||
|
return { |
||||||
|
name: contractName, |
||||||
|
contract: compiler.getContract(contractName), |
||||||
|
compiler |
||||||
|
} |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
self._view.createPanel = yo`<div class="${css.button}"></div>` |
||||||
|
self._view.orLabel = yo`<div class="${css.orLabel}">or</div>` |
||||||
|
var el = yo` |
||||||
|
<div class="${css.container}"> |
||||||
|
<div class="${css.subcontainer}"> |
||||||
|
${selectContractNames} ${compFails} ${info} |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
${self._view.createPanel} |
||||||
|
${self._view.orLabel} |
||||||
|
<div class="${css.button} ${css.atAddressSect}"> |
||||||
|
<div class="${css.atAddress}" onclick=${function () { loadFromAddress() }}>At Address</div> |
||||||
|
${atAddressButtonInput} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
` |
||||||
|
|
||||||
|
function setInputParamsPlaceHolder () { |
||||||
|
self._view.createPanel.innerHTML = '' |
||||||
|
if (selectContractNames.selectedIndex >= 0 && selectContractNames.children.length > 0) { |
||||||
|
var selectedContract = getSelectedContract() |
||||||
|
var ctrabi = txHelper.getConstructorInterface(selectedContract.contract.object.abi) |
||||||
|
var ctrEVMbc = selectedContract.contract.object.evm.bytecode.object |
||||||
|
var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => { |
||||||
|
createInstance(inputsValues, selectedContract.compiler) |
||||||
|
}, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy', ctrEVMbc) |
||||||
|
self._view.createPanel.appendChild(createConstructorInstance.render()) |
||||||
|
return |
||||||
|
} else { |
||||||
|
self._view.createPanel.innerHTML = 'No compiled contracts' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
selectContractNames.addEventListener('change', setInputParamsPlaceHolder) |
||||||
|
|
||||||
|
function createInstanceCallback (selectedContract, data) { |
||||||
|
self._deps.logCallback(`creation of ${selectedContract.name} pending...`) |
||||||
|
if (data) { |
||||||
|
data.contractName = selectedContract.name |
||||||
|
data.linkReferences = selectedContract.contract.object.evm.bytecode.linkReferences |
||||||
|
data.contractABI = selectedContract.contract.object.abi |
||||||
|
} |
||||||
|
self._deps.udapp.createContract(data, |
||||||
|
|
||||||
|
(network, tx, gasEstimation, continueTxExecution, cancelCb) => { |
||||||
|
if (network.name !== 'Main') { |
||||||
|
return continueTxExecution(null) |
||||||
|
} |
||||||
|
var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') |
||||||
|
var content = confirmDialog(tx, amount, gasEstimation, self, |
||||||
|
(gasPrice, cb) => { |
||||||
|
let txFeeText, priceStatus |
||||||
|
// TODO: this try catch feels like an anti pattern, can/should be
|
||||||
|
// removed, but for now keeping the original logic
|
||||||
|
try { |
||||||
|
var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) |
||||||
|
txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' |
||||||
|
priceStatus = true |
||||||
|
} catch (e) { |
||||||
|
txFeeText = ' Please fix this issue before sending any transaction. ' + e.message |
||||||
|
priceStatus = false |
||||||
|
} |
||||||
|
cb(txFeeText, priceStatus) |
||||||
|
}, |
||||||
|
(cb) => { |
||||||
|
executionContext.web3().eth.getGasPrice((error, gasPrice) => { |
||||||
|
var warnMessage = ' Please fix this issue before sending any transaction. ' |
||||||
|
if (error) { |
||||||
|
return cb('Unable to retrieve the current network gas price.' + warnMessage + error) |
||||||
|
} |
||||||
|
try { |
||||||
|
var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') |
||||||
|
cb(null, gasPriceValue) |
||||||
|
} catch (e) { |
||||||
|
cb(warnMessage + e.message, null, false) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
) |
||||||
|
modalDialog('Confirm transaction', content, |
||||||
|
{ label: 'Confirm', |
||||||
|
fn: () => { |
||||||
|
self._deps.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 gas price is not correct') |
||||||
|
} else { |
||||||
|
var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') |
||||||
|
continueTxExecution(gasPrice) |
||||||
|
} |
||||||
|
}}, { |
||||||
|
label: 'Cancel', |
||||||
|
fn: () => { |
||||||
|
return cancelCb('Transaction canceled by user.') |
||||||
|
} |
||||||
|
}) |
||||||
|
}, |
||||||
|
(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() |
||||||
|
} |
||||||
|
}, |
||||||
|
function (okCb, cancelCb) { |
||||||
|
modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) |
||||||
|
}, |
||||||
|
(error, txResult) => { |
||||||
|
if (!error) { |
||||||
|
var isVM = executionContext.isVM() |
||||||
|
if (isVM) { |
||||||
|
var vmError = txExecution.checkVMError(txResult) |
||||||
|
if (vmError.error) { |
||||||
|
self._deps.logCallback(vmError.message) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
if (txResult.result.status && txResult.result.status === '0x0') { |
||||||
|
self._deps.logCallback(`creation of ${selectedContract.name} errored: transaction execution failed`) |
||||||
|
return |
||||||
|
} |
||||||
|
var noInstancesText = self._view.noInstancesText |
||||||
|
if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } |
||||||
|
var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress |
||||||
|
instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) |
||||||
|
} else { |
||||||
|
self._deps.logCallback(`creation of ${selectedContract.name} errored: ${error}`) |
||||||
|
} |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
// DEPLOY INSTANCE
|
||||||
|
function createInstance (args, compiler) { |
||||||
|
var selectedContract = getSelectedContract() |
||||||
|
|
||||||
|
if (selectedContract.contract.object.evm.bytecode.object.length === 0) { |
||||||
|
modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var forceSend = () => { |
||||||
|
var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) |
||||||
|
self._deps.filePanel.compilerMetadata().deployMetadataOf(selectedContract.name, (error, contractMetadata) => { |
||||||
|
if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) |
||||||
|
if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { |
||||||
|
txFormat.buildData(selectedContract.name, selectedContract.contract.object, compiler.getContracts(), true, constructor, args, (error, data) => { |
||||||
|
if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) |
||||||
|
createInstanceCallback(selectedContract, data) |
||||||
|
}, (msg) => { |
||||||
|
self._deps.logCallback(msg) |
||||||
|
}, (data, runTxCallback) => { |
||||||
|
// called for libraries deployment
|
||||||
|
self._deps.udapp.runTx(data, |
||||||
|
(network, tx, gasEstimation, continueTxExecution, cancelCb) => { |
||||||
|
if (network.name !== 'Main') { |
||||||
|
return continueTxExecution(null) |
||||||
|
} |
||||||
|
var amount = executionContext.web3().fromWei(typeConversion.toInt(tx.value), 'ether') |
||||||
|
var content = confirmDialog(tx, amount, gasEstimation, self, |
||||||
|
(gasPrice, cb) => { |
||||||
|
let txFeeText, priceStatus |
||||||
|
// TODO: this try catch feels like an anti pattern, can/should be
|
||||||
|
// removed, but for now keeping the original logic
|
||||||
|
try { |
||||||
|
var fee = executionContext.web3().toBigNumber(tx.gas).mul(executionContext.web3().toBigNumber(executionContext.web3().toWei(gasPrice.toString(10), 'gwei'))) |
||||||
|
txFeeText = ' ' + executionContext.web3().fromWei(fee.toString(10), 'ether') + ' Ether' |
||||||
|
priceStatus = true |
||||||
|
} catch (e) { |
||||||
|
txFeeText = ' Please fix this issue before sending any transaction. ' + e.message |
||||||
|
priceStatus = false |
||||||
|
} |
||||||
|
cb(txFeeText, priceStatus) |
||||||
|
}, |
||||||
|
(cb) => { |
||||||
|
executionContext.web3().eth.getGasPrice((error, gasPrice) => { |
||||||
|
var warnMessage = ' Please fix this issue before sending any transaction. ' |
||||||
|
if (error) { |
||||||
|
return cb('Unable to retrieve the current network gas price.' + warnMessage + error) |
||||||
|
} |
||||||
|
try { |
||||||
|
var gasPriceValue = executionContext.web3().fromWei(gasPrice.toString(10), 'gwei') |
||||||
|
cb(null, gasPriceValue) |
||||||
|
} catch (e) { |
||||||
|
cb(warnMessage + e.message, null, false) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
) |
||||||
|
modalDialog('Confirm transaction', content, |
||||||
|
{ label: 'Confirm', |
||||||
|
fn: () => { |
||||||
|
self._deps.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 gas price is not correct') |
||||||
|
} else { |
||||||
|
var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') |
||||||
|
continueTxExecution(gasPrice) |
||||||
|
} |
||||||
|
}}, { |
||||||
|
label: 'Cancel', |
||||||
|
fn: () => { |
||||||
|
return cancelCb('Transaction canceled by user.') |
||||||
|
} |
||||||
|
}) |
||||||
|
}, |
||||||
|
function (okCb, cancelCb) { |
||||||
|
modalCustom.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) |
||||||
|
}, |
||||||
|
runTxCallback) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
if (Object.keys(selectedContract.contract.object.evm.bytecode.linkReferences).length) self._deps.logCallback(`linking ${JSON.stringify(selectedContract.contract.object.evm.bytecode.linkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) |
||||||
|
txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.contract.object, args, constructor, contractMetadata.linkReferences, selectedContract.contract.object.evm.bytecode.linkReferences, (error, data) => { |
||||||
|
if (error) return self._deps.logCallback(`creation of ${selectedContract.name} errored: ` + error) |
||||||
|
createInstanceCallback(selectedContract, data) |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
if (selectedContract.contract.object.evm.deployedBytecode && selectedContract.contract.object.evm.deployedBytecode.object.length / 2 > 24576) { |
||||||
|
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: () => { |
||||||
|
forceSend() |
||||||
|
}}, { |
||||||
|
label: 'Cancel', |
||||||
|
fn: () => { |
||||||
|
self._deps.logCallback(`creation of ${selectedContract.name} canceled by user.`) |
||||||
|
} |
||||||
|
}) |
||||||
|
} else { |
||||||
|
forceSend() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ACCESS DEPLOYED INSTANCE
|
||||||
|
function loadFromAddress () { |
||||||
|
var noInstancesText = self._view.noInstancesText |
||||||
|
if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } |
||||||
|
var address = atAddressButtonInput.value |
||||||
|
if (!ethJSUtil.isValidAddress(address)) { |
||||||
|
return modalDialogCustom.alert('Invalid address.') |
||||||
|
} |
||||||
|
if (/[a-f]/.test(address) && /[A-F]/.test(address) && !ethJSUtil.isValidChecksumAddress(address)) { |
||||||
|
return modalDialogCustom.alert('Invalid checksum address.') |
||||||
|
} |
||||||
|
if (/.(.abi)$/.exec(self._deps.config.get('currentFile'))) { |
||||||
|
modalDialogCustom.confirm(null, 'Do you really want to interact with ' + address + ' using the current ABI definition ?', () => { |
||||||
|
var abi |
||||||
|
try { |
||||||
|
abi = JSON.parse(self._deps.editor.currentContent()) |
||||||
|
} catch (e) { |
||||||
|
return modalDialogCustom.alert('Failed to parse the current file as JSON ABI.') |
||||||
|
} |
||||||
|
instanceContainer.appendChild(self._deps.udappUI.renderInstanceFromABI(abi, address, address)) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
var selectedContract = getSelectedContract() |
||||||
|
instanceContainer.appendChild(self._deps.udappUI.renderInstance(selectedContract.contract.object, address, selectContractNames.value)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// GET NAMES OF ALL THE CONTRACTS
|
||||||
|
function getContractNames (success, data, compiler, compilerFullName) { |
||||||
|
var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`) |
||||||
|
contractNames.innerHTML = '' |
||||||
|
if (success) { |
||||||
|
selectContractNames.removeAttribute('disabled') |
||||||
|
compiler.visitContracts((contract) => { |
||||||
|
contractNames.appendChild(yo`<option compiler="${compilerFullName}">${contract.name}</option>`) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
selectContractNames.setAttribute('disabled', true) |
||||||
|
} |
||||||
|
setInputParamsPlaceHolder() |
||||||
|
} |
||||||
|
|
||||||
|
return el |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = contractDropdown |
@ -0,0 +1,299 @@ |
|||||||
|
var $ = require('jquery') |
||||||
|
var yo = require('yo-yo') |
||||||
|
var ethJSUtil = require('ethereumjs-util') |
||||||
|
var Personal = require('web3-eth-personal') |
||||||
|
var css = require('../styles/run-tab-styles') |
||||||
|
var executionContext = require('../../../execution-context') |
||||||
|
var copyToClipboard = require('../../ui/copy-to-clipboard') |
||||||
|
var modalDialogCustom = require('../../ui/modal-dialog-custom') |
||||||
|
var addTooltip = require('../../ui/tooltip') |
||||||
|
var modalCustom = require('../../ui/modal-dialog-custom') |
||||||
|
var tootip = require('../../ui/tooltip') |
||||||
|
var helper = require('../../../lib/helper.js') |
||||||
|
|
||||||
|
function settings (container, self) { |
||||||
|
// VARIABLES
|
||||||
|
var net = yo`<span class=${css.network}></span>` |
||||||
|
var networkcallid = 0 |
||||||
|
const updateNetwork = (cb) => { |
||||||
|
networkcallid++ |
||||||
|
(function (callid) { |
||||||
|
executionContext.detectNetwork((err, { id, name } = {}) => { |
||||||
|
if (networkcallid > callid) return |
||||||
|
networkcallid++ |
||||||
|
if (err) { |
||||||
|
console.error(err) |
||||||
|
net.innerHTML = 'can\'t detect network ' |
||||||
|
} else { |
||||||
|
net.innerHTML = `<i class="${css.networkItem} fa fa-plug" aria-hidden="true"></i> ${name} (${id || '-'})` |
||||||
|
} |
||||||
|
if (cb) cb(err, {id, name}) |
||||||
|
}) |
||||||
|
})(networkcallid) |
||||||
|
} |
||||||
|
var environmentEl = yo` |
||||||
|
<div class="${css.crow}"> |
||||||
|
<div id="selectExEnv" class="${css.col1_1}"> |
||||||
|
Environment |
||||||
|
</div> |
||||||
|
<div class=${css.environment}> |
||||||
|
${net} |
||||||
|
<select id="selectExEnvOptions" onchange=${() => { updateNetwork() }} class="${css.select}"> |
||||||
|
<option id="vm-mode" |
||||||
|
title="Execution environment does not connect to any node, everything is local and in memory only." |
||||||
|
value="vm" checked name="executionContext"> JavaScript VM |
||||||
|
</option> |
||||||
|
<option id="injected-mode" |
||||||
|
title="Execution environment has been provided by Metamask or similar provider." |
||||||
|
value="injected" checked name="executionContext"> Injected Web3 |
||||||
|
</option> |
||||||
|
<option id="web3-mode" |
||||||
|
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://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md" target="_blank"><i class="${css.icon} fa fa-info"></i></a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
` |
||||||
|
var accountEl = yo` |
||||||
|
<div class="${css.crow}"> |
||||||
|
<div class="${css.col1_1}"> |
||||||
|
Account |
||||||
|
<i class="fa fa-plus-circle ${css.icon}" aria-hidden="true" onclick=${newAccount} title="Create a new account"></i> |
||||||
|
</div> |
||||||
|
<div class=${css.account}> |
||||||
|
<select name="txorigin" class="${css.select}" id="txorigin"></select> |
||||||
|
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} |
||||||
|
<i class="fa fa-pencil-square-o ${css.icon}" aria-hiden="true" onclick=${signMessage} title="Sign a message using this account key"></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
` |
||||||
|
var gasPriceEl = yo` |
||||||
|
<div class="${css.crow}"> |
||||||
|
<div class="${css.col1_1}">Gas limit</div> |
||||||
|
<input type="number" class="${css.col2}" id="gasLimit" value="3000000"> |
||||||
|
</div> |
||||||
|
` |
||||||
|
var valueEl = yo` |
||||||
|
<div class="${css.crow}"> |
||||||
|
<div class="${css.col1_1}">Value</div> |
||||||
|
<input type="text" class="${css.col2_1}" id="value" value="0" title="Enter the value and choose the unit"> |
||||||
|
<select name="unit" class="${css.col2_2}" 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> |
||||||
|
` |
||||||
|
// DOM ELEMENT
|
||||||
|
var el = yo` |
||||||
|
<div class="${css.settings}"> |
||||||
|
${environmentEl} |
||||||
|
${accountEl} |
||||||
|
${gasPriceEl} |
||||||
|
${valueEl} |
||||||
|
</div> |
||||||
|
` |
||||||
|
// HELPER FUNCTIONS AND EVENTS
|
||||||
|
self._deps.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { |
||||||
|
if (error) return |
||||||
|
if (!lookupOnly) el.querySelector('#value').value = '0' |
||||||
|
updateAccountBalances(container, self) |
||||||
|
}) |
||||||
|
|
||||||
|
// DROPDOWN
|
||||||
|
var selectExEnv = environmentEl.querySelector('#selectExEnvOptions') |
||||||
|
|
||||||
|
function setFinalContext () { |
||||||
|
// set the final context. Cause it is possible that this is not the one we've originaly selected
|
||||||
|
selectExEnv.value = executionContext.getProvider() |
||||||
|
self.event.trigger('clearInstance', []) |
||||||
|
updateNetwork() |
||||||
|
fillAccountsList(el, self) |
||||||
|
} |
||||||
|
|
||||||
|
self.event.register('clearInstance', () => { |
||||||
|
var instanceContainer = self._view.instanceContainer |
||||||
|
var instanceContainerTitle = self._view.instanceContainerTitle |
||||||
|
instanceContainer.innerHTML = '' // clear the instances list
|
||||||
|
instanceContainer.appendChild(instanceContainerTitle) |
||||||
|
instanceContainer.appendChild(self._view.noInstancesText) |
||||||
|
}) |
||||||
|
|
||||||
|
executionContext.event.register('addProvider', (network) => { |
||||||
|
selectExEnv.appendChild(yo`<option
|
||||||
|
title="Manually added environment: ${network.url}" |
||||||
|
value="${network.name}" name="executionContext"> ${network.name} |
||||||
|
</option>`) |
||||||
|
tootip(`${network.name} [${network.url}] added`) |
||||||
|
}) |
||||||
|
|
||||||
|
executionContext.event.register('removeProvider', (name) => { |
||||||
|
var env = selectExEnv.querySelector(`option[value="${name}"]`) |
||||||
|
if (env) { |
||||||
|
selectExEnv.removeChild(env) |
||||||
|
tootip(`${name} removed`) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
selectExEnv.addEventListener('change', function (event) { |
||||||
|
let context = selectExEnv.options[selectExEnv.selectedIndex].value |
||||||
|
executionContext.executionContextChange(context, null, () => { |
||||||
|
modalDialogCustom.confirm(null, 'Are you sure you want to connect to an ethereum node?', () => { |
||||||
|
modalDialogCustom.prompt(null, 'Web3 Provider Endpoint', 'http://localhost:8545', (target) => { |
||||||
|
executionContext.setProviderFromEndpoint(target, context, (alertMsg) => { |
||||||
|
if (alertMsg) { |
||||||
|
modalDialogCustom.alert(alertMsg) |
||||||
|
} |
||||||
|
setFinalContext() |
||||||
|
}) |
||||||
|
}, setFinalContext) |
||||||
|
}, setFinalContext) |
||||||
|
}, (alertMsg) => { |
||||||
|
modalDialogCustom.alert(alertMsg) |
||||||
|
}, setFinalContext) |
||||||
|
}) |
||||||
|
|
||||||
|
selectExEnv.value = executionContext.getProvider() |
||||||
|
executionContext.event.register('contextChanged', (context, silent) => { |
||||||
|
setFinalContext() |
||||||
|
}) |
||||||
|
|
||||||
|
setInterval(() => { |
||||||
|
updateNetwork() |
||||||
|
fillAccountsList(el, self) |
||||||
|
}, 5000) |
||||||
|
|
||||||
|
setInterval(() => { |
||||||
|
updateAccountBalances(container, self) |
||||||
|
}, 10000) |
||||||
|
|
||||||
|
function newAccount () { |
||||||
|
self._deps.udapp.newAccount('', |
||||||
|
(cb) => { |
||||||
|
modalCustom.promptPassphraseCreation((error, passphrase) => { |
||||||
|
if (error) { |
||||||
|
return modalCustom.alert(error) |
||||||
|
} |
||||||
|
cb(passphrase) |
||||||
|
}, () => {}) |
||||||
|
}, |
||||||
|
(error, address) => { |
||||||
|
if (!error) { |
||||||
|
addTooltip(`account ${address} created`) |
||||||
|
} else { |
||||||
|
addTooltip('Cannot create an account: ' + error) |
||||||
|
} |
||||||
|
} |
||||||
|
) |
||||||
|
} |
||||||
|
function signMessage (event) { |
||||||
|
self._deps.udapp.getAccounts((err, accounts) => { |
||||||
|
if (err) { addTooltip(`Cannot get account list: ${err}`) } |
||||||
|
var signMessageDialog = { 'title': 'Sign a message', 'text': 'Enter a message to sign', 'inputvalue': 'Message to sign' } |
||||||
|
var $txOrigin = container.querySelector('#txorigin') |
||||||
|
var account = $txOrigin.selectedOptions[0].value |
||||||
|
var isVM = executionContext.isVM() |
||||||
|
var isInjected = executionContext.getProvider() === 'injected' |
||||||
|
function alertSignedData (error, hash, signedData) { |
||||||
|
if (error && error.message !== '') { |
||||||
|
console.log(error) |
||||||
|
addTooltip(error.message) |
||||||
|
} else { |
||||||
|
modalDialogCustom.alert(yo`<div><b>hash:</b>${hash}<br><b>signature:</b>${signedData}</div>`) |
||||||
|
} |
||||||
|
} |
||||||
|
if (isVM) { |
||||||
|
modalDialogCustom.promptMulti(signMessageDialog, (message) => { |
||||||
|
const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) |
||||||
|
var privKey = self._deps.udapp.accounts[account].privateKey |
||||||
|
try { |
||||||
|
var rsv = ethJSUtil.ecsign(personalMsg, privKey) |
||||||
|
var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s) |
||||||
|
alertSignedData(null, '0x' + personalMsg.toString('hex'), signedData) |
||||||
|
} catch (e) { |
||||||
|
addTooltip(e.message) |
||||||
|
return |
||||||
|
} |
||||||
|
}, false) |
||||||
|
} else if (isInjected) { |
||||||
|
modalDialogCustom.promptMulti(signMessageDialog, (message) => { |
||||||
|
const hashedMsg = executionContext.web3().sha3(message) |
||||||
|
try { |
||||||
|
executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { |
||||||
|
alertSignedData(error, hashedMsg, signedData) |
||||||
|
}) |
||||||
|
} catch (e) { |
||||||
|
addTooltip(e.message) |
||||||
|
console.log(e) |
||||||
|
return |
||||||
|
} |
||||||
|
}) |
||||||
|
} else { |
||||||
|
modalDialogCustom.promptPassphrase('Passphrase to sign a message', 'Enter your passphrase for this account to sign the message', '', (passphrase) => { |
||||||
|
modalDialogCustom.promptMulti(signMessageDialog, (message) => { |
||||||
|
const hashedMsg = executionContext.web3().sha3(message) |
||||||
|
try { |
||||||
|
var personal = new Personal(executionContext.web3().currentProvider) |
||||||
|
personal.sign(hashedMsg, account, passphrase, (error, signedData) => { |
||||||
|
alertSignedData(error, hashedMsg, signedData) |
||||||
|
}) |
||||||
|
} catch (e) { |
||||||
|
addTooltip(e.message) |
||||||
|
console.log(e) |
||||||
|
return |
||||||
|
} |
||||||
|
}) |
||||||
|
}, false) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return el |
||||||
|
} |
||||||
|
|
||||||
|
var accountListCallId = 0 |
||||||
|
var loadedAccounts = {} |
||||||
|
function fillAccountsList (container, self) { |
||||||
|
accountListCallId++ |
||||||
|
(function (callid) { |
||||||
|
var txOrigin = container.querySelector('#txorigin') |
||||||
|
self._deps.udapp.getAccounts((err, accounts) => { |
||||||
|
if (accountListCallId > callid) return |
||||||
|
accountListCallId++ |
||||||
|
if (err) { addTooltip(`Cannot get account list: ${err}`) } |
||||||
|
for (var loadedaddress in loadedAccounts) { |
||||||
|
if (accounts.indexOf(loadedaddress) === -1) { |
||||||
|
txOrigin.removeChild(txOrigin.querySelector('option[value="' + loadedaddress + '"]')) |
||||||
|
delete loadedAccounts[loadedaddress] |
||||||
|
} |
||||||
|
} |
||||||
|
for (var i in accounts) { |
||||||
|
var address = accounts[i] |
||||||
|
if (!loadedAccounts[address]) { |
||||||
|
txOrigin.appendChild(yo`<option value="${address}" >${address}</option>`) |
||||||
|
loadedAccounts[address] = 1 |
||||||
|
} |
||||||
|
} |
||||||
|
txOrigin.setAttribute('value', accounts[0]) |
||||||
|
}) |
||||||
|
})(accountListCallId) |
||||||
|
} |
||||||
|
|
||||||
|
function updateAccountBalances (container, self) { |
||||||
|
var accounts = $(container.querySelector('#txorigin')).children('option') |
||||||
|
accounts.each(function (index, value) { |
||||||
|
(function (acc) { |
||||||
|
self._deps.udapp.getBalanceInEther(accounts[acc].value, function (err, res) { |
||||||
|
if (!err) { |
||||||
|
accounts[acc].innerText = helper.shortenAddress(accounts[acc].value, res) |
||||||
|
} |
||||||
|
}) |
||||||
|
})(index) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = settings |
Loading…
Reference in new issue