diff --git a/src/app.js b/src/app.js index 5dae940db5..6df7b03935 100644 --- a/src/app.js +++ b/src/app.js @@ -23,6 +23,8 @@ var toolTip = require('./app/ui/tooltip') var CompilerMetadata = require('./app/files/compiler-metadata') var CompilerImport = require('./app/compiler/compiler-imports') +var executionContext = remixLib.execution.executionContext + const PluginManagerComponent = require('./app/components/plugin-manager-component') const CompilersArtefacts = require('./app/compiler/compiler-artefacts') @@ -222,15 +224,15 @@ Please make a backup of your contracts and start using http://remix.ethereum.org const fileManager = new FileManager(editor) registry.put({api: fileManager, name: 'filemanager'}) // ----------------- compilation metadata generation servive ---------------------------- - const compilerMetadataGenerator = new CompilerMetadata(fileManager, registry.get('config').api) + const compilerMetadataGenerator = new CompilerMetadata(executionContext, fileManager, registry.get('config').api) // ----------------- compilation result service (can keep track of compilation results) ---------------------------- const compilersArtefacts = new CompilersArtefacts() // store all the compilation results (key represent a compiler name) registry.put({api: compilersArtefacts, name: 'compilersartefacts'}) // ----------------- universal dapp: run transaction, listen on transactions, decode events - const udapp = new UniversalDApp(registry.get('config').api) - const {eventsDecoder, txlistener} = makeUdapp(udapp, compilersArtefacts, (domEl) => mainview.getTerminal().logHtml(domEl)) + const udapp = new UniversalDApp(registry.get('config').api, executionContext) + const {eventsDecoder, txlistener} = makeUdapp(udapp, executionContext, compilersArtefacts, (domEl) => mainview.getTerminal().logHtml(domEl)) // ----------------- network service (resolve network id / name) ---------------------------- - const networkModule = new NetworkModule() + const networkModule = new NetworkModule(executionContext) // ----------------- convert offset to line/column service ---------------------------- var offsetToLineColumnConverter = new OffsetToLineColumnConverter() registry.put({api: offsetToLineColumnConverter, name: 'offsettolinecolumnconverter'}) @@ -248,7 +250,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // LAYOUT & SYSTEM VIEWS const appPanel = new MainPanel() - const mainview = new MainView(editor, appPanel, fileManager, appManager, txlistener, eventsDecoder) + const mainview = new MainView(editor, appPanel, fileManager, appManager, txlistener, eventsDecoder, executionContext) registry.put({ api: mainview, name: 'mainview' }) appManager.register([ @@ -293,6 +295,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org ) const run = new RunTab( udapp, + executionContext, registry.get('config').api, registry.get('filemanager').api, registry.get('editor').api, @@ -302,7 +305,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org mainview ) const analysis = new AnalysisTab(registry) - const debug = new DebuggerTab() + const debug = new DebuggerTab(executionContext) const test = new TestTab( registry.get('filemanager').api, filePanel, diff --git a/src/app/files/compiler-metadata.js b/src/app/files/compiler-metadata.js index 69974afc58..1485455049 100644 --- a/src/app/files/compiler-metadata.js +++ b/src/app/files/compiler-metadata.js @@ -1,5 +1,4 @@ 'use strict' -var executionContext = require('../../execution-context') var CompilerAbstract = require('../compiler/compiler-abstract') import { Plugin } from '@remixproject/engine' import * as packageJson from '../../../package.json' @@ -12,9 +11,10 @@ const profile = { } class CompilerMetadata extends Plugin { - constructor (fileManager, config) { + constructor (executionContext, fileManager, config) { super(profile) var self = this + self.executionContext = executionContext self.fileManager = fileManager self.config = config self.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'görli:5', 'Custom'] @@ -97,7 +97,7 @@ class CompilerMetadata extends Plugin { var provider = self.fileManager.currentFileProvider() var path = self.fileManager.currentPath() if (provider && path) { - executionContext.detectNetwork((err, { id, name } = {}) => { + self.executionContext.detectNetwork((err, { id, name } = {}) => { if (err) { console.log(err) } else { diff --git a/src/app/panels/main-view.js b/src/app/panels/main-view.js index 8437b7a41e..6d8f1bde65 100644 --- a/src/app/panels/main-view.js +++ b/src/app/panels/main-view.js @@ -20,7 +20,7 @@ var css = csjs` ` export class MainView { - constructor (editor, mainPanel, fileManager, appManager, txListener, eventsDecoder) { + constructor (editor, mainPanel, fileManager, appManager, txListener, eventsDecoder, executionContext) { var self = this self.event = new EventManager() self._view = {} @@ -31,6 +31,7 @@ export class MainView { self.mainPanel = mainPanel self.txListener = txListener self.eventsDecoder = eventsDecoder + self.executionContext = executionContext this.appManager = appManager this.init() } @@ -99,7 +100,8 @@ export class MainView { self._components.terminal = new Terminal({ appManager: this.appManager, eventsDecoder: this.eventsDecoder, - txListener: this.txListener + txListener: this.txListener, + executionContext: this.executionContext }, { getPosition: (event) => { diff --git a/src/app/panels/terminal.js b/src/app/panels/terminal.js index ade3113339..ea9d103cc6 100644 --- a/src/app/panels/terminal.js +++ b/src/app/panels/terminal.js @@ -10,7 +10,6 @@ var Web3 = require('web3') var swarmgw = require('swarmgw')() var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI') -var executionContext = require('../../execution-context') var AutoCompletePopup = require('../ui/auto-complete-popup') var TxLogger = require('../../app/ui/txLogger') @@ -42,6 +41,7 @@ class Terminal extends Plugin { super(profile) var self = this self.event = new EventManager() + self.executionContext = opts.executionContext self._api = api self._opts = opts self.data = { @@ -52,7 +52,7 @@ class Terminal extends Plugin { } self._view = { el: null, bar: null, input: null, term: null, journal: null, cli: null } self._components = {} - self._components.cmdInterpreter = new CommandInterpreterAPI(this) + self._components.cmdInterpreter = new CommandInterpreterAPI(this, null, self.executionContext) self._components.autoCompletePopup = new AutoCompletePopup(self._opts) self._components.autoCompletePopup.event.register('handleSelect', function (input) { let textList = self._view.input.innerText.split(' ') @@ -437,7 +437,7 @@ class Terminal extends Plugin { self._shell('remix.help()', self.commands, () => {}) self.commands.html(intro) - self._components.txLogger = new TxLogger(self._opts.eventsDecoder, self._opts.txListener, this) + self._components.txLogger = new TxLogger(self._opts.eventsDecoder, self._opts.txListener, this, self.executionContext) self._components.txLogger.event.register('debuggingRequested', (hash) => { // TODO should probably be in the run module if (!self._opts.appManager.isActive('debugger')) self._opts.appManager.activateOne('debugger') @@ -668,7 +668,7 @@ class Terminal extends Plugin { return done(null, 'This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.') } var self = this - var context = domTerminalFeatures(self, scopedCommands) + var context = domTerminalFeatures(self, scopedCommands, self.executionContext) try { var cmds = vm.createContext(Object.assign(self._jsSandboxContext, context, self._jsSandboxRegistered)) var result = vm.runInContext(script, cmds) @@ -680,7 +680,7 @@ class Terminal extends Plugin { } } -function domTerminalFeatures (self, scopedCommands) { +function domTerminalFeatures (self, scopedCommands, executionContext) { return { swarmgw, ethers, diff --git a/src/app/tabs/debugger-tab.js b/src/app/tabs/debugger-tab.js index 4d091524b0..d86db9aef5 100644 --- a/src/app/tabs/debugger-tab.js +++ b/src/app/tabs/debugger-tab.js @@ -21,9 +21,10 @@ const profile = { class DebuggerTab extends ViewPlugin { - constructor () { + constructor (executionContext) { super(profile) this.el = null + this.executionContext = executionContext } render () { @@ -33,7 +34,7 @@ class DebuggerTab extends ViewPlugin {
` - this.debuggerUI = new DebuggerUI(this.el.querySelector('#debugger')) + this.debuggerUI = new DebuggerUI(this.el.querySelector('#debugger'), this.executionContext) return this.el } diff --git a/src/app/tabs/debugger/debuggerUI.js b/src/app/tabs/debugger/debuggerUI.js index bd2b8f9f15..d30f32b5b9 100644 --- a/src/app/tabs/debugger/debuggerUI.js +++ b/src/app/tabs/debugger/debuggerUI.js @@ -9,7 +9,6 @@ var SourceHighlighter = require('../../editor/sourceHighlighter') var EventManager = require('../../../lib/events') -var executionContext = require('../../../execution-context') var globalRegistry = require('../../../global/registry') var remixLib = require('remix-lib') @@ -31,8 +30,9 @@ var css = csjs` class DebuggerUI { - constructor (container) { + constructor (container, executionContext) { this.registry = globalRegistry + this.executionContext = executionContext this.event = new EventManager() this.isActive = false @@ -105,13 +105,13 @@ class DebuggerUI { getDebugWeb3 () { return new Promise((resolve, reject) => { - executionContext.detectNetwork((error, network) => { + this.executionContext.detectNetwork((error, network) => { let web3 if (error || !network) { - web3 = init.web3DebugNode(executionContext.web3()) + web3 = init.web3DebugNode(this.executionContext.web3()) } else { const webDebugNode = init.web3DebugNode(network.name) - web3 = !webDebugNode ? executionContext.web3() : webDebugNode + web3 = !webDebugNode ? this.executionContext.web3() : webDebugNode } init.extendWeb3(web3) resolve(web3) diff --git a/src/app/tabs/network-module.js b/src/app/tabs/network-module.js index c57893578b..4ead25261e 100644 --- a/src/app/tabs/network-module.js +++ b/src/app/tabs/network-module.js @@ -1,4 +1,3 @@ -const executionContext = require('../../execution-context') import { Plugin } from '@remixproject/engine' import * as packageJson from '../../../package.json' @@ -15,10 +14,11 @@ export const profile = { // - methods: ['getNetworkProvider', 'getEndpoint', 'detectNetwork', 'addNetwork', 'removeNetwork'] export class NetworkModule extends Plugin { - constructor () { + constructor (executionContext) { super(profile) + this.executionContext = executionContext // TODO: See with remix-lib to make sementic coherent - executionContext.event.register('contextChanged', (provider) => { + this.executionContext.event.register('contextChanged', (provider) => { this.emit('providerChanged', provider) }) /* @@ -37,13 +37,13 @@ export class NetworkModule extends Plugin { /** Return the current network provider (web3, vm, injected) */ getNetworkProvider () { - return executionContext.getProvider() + return this.executionContext.getProvider() } /** Return the current network */ detectNetwork () { return new Promise((resolve, reject) => { - executionContext.detectNetwork((error, network) => { + this.executionContext.detectNetwork((error, network) => { error ? reject(error) : resolve(network) }) }) @@ -51,20 +51,20 @@ export class NetworkModule extends Plugin { /** Return the url only if network provider is 'web3' */ getEndpoint () { - const provider = executionContext.getProvider() + const provider = this.executionContext.getProvider() if (provider !== 'web3') { throw new Error('no endpoint: current provider is either injected or vm') } - return executionContext.web3().currentProvider.host + return this.executionContext.web3().currentProvider.host } /** Add a custom network to the list of available networks */ addNetwork (customNetwork) { - executionContext.addProvider(customNetwork) + this.executionContext.addProvider(customNetwork) } /** Remove a network to the list of availble networks */ removeNetwork (name) { - executionContext.removeProvider(name) + this.executionContext.removeProvider(name) } } diff --git a/src/app/tabs/runTab/contractDropdown.js b/src/app/tabs/runTab/contractDropdown.js index da3acb8886..fa727bbaed 100644 --- a/src/app/tabs/runTab/contractDropdown.js +++ b/src/app/tabs/runTab/contractDropdown.js @@ -8,9 +8,11 @@ var modalDialog = require('../../ui/modaldialog') var MultiParamManager = require('../../ui/multiParamManager') class ContractDropdownUI { - constructor (dropdownLogic, logCallback) { + constructor (blockchain, dropdownLogic, logCallback, runView) { + this.blockchain = blockchain this.dropdownLogic = dropdownLogic this.logCallback = logCallback + this.runView = runView this.event = new EventManager() this.listenToEvents() @@ -39,8 +41,6 @@ class ContractDropdownUI { document.querySelector(`.${css.contractNames}`).classList.add(css.contractNamesError) } }) - - this.dropdownLogic.event.register('currentFileChanged', this.changeCurrentFile.bind(this)) } render () { @@ -108,8 +108,9 @@ class ContractDropdownUI { } const selectedContract = this.getSelectedContract() - const clickCallback = (valArray, inputsValues) => { - this.createInstance(inputsValues) + const clickCallback = async (valArray, inputsValues) => { + var selectedContract = this.getSelectedContract() + this.createInstance(selectedContract, inputsValues) } const createConstructorInstance = new MultiParamManager( 0, @@ -130,9 +131,7 @@ class ContractDropdownUI { return this.dropdownLogic.getSelectedContract(contractName, compilerAtributeName) } - createInstance (args) { - var selectedContract = this.getSelectedContract() - + 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.') } @@ -177,6 +176,16 @@ class ContractDropdownUI { this.event.trigger('newContractInstanceAdded', [contractObject, address, contractObject.name]) } + let contractMetadata + try { + contractMetadata = await this.runView.call('compilerMetadata', 'deployMetadataOf', selectedContract.name) + } catch (error) { + return statusCb(`creation of ${selectedContract.name} errored: ` + error) + } + + const compilerContracts = this.dropdownLogic.getCompilerContracts() + const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog) + if (selectedContract.isOverSizeLimit()) { return modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
More info: eip-170 @@ -184,7 +193,7 @@ class ContractDropdownUI { { label: 'Force Send', fn: () => { - this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, finalCb) + this.blockchain.deployContract(selectedContract, args, contractMetadata, compilerContracts, {continueCb, promptCb, statusCb, finalCb}, confirmationCb) }}, { label: 'Cancel', fn: () => { @@ -192,7 +201,38 @@ class ContractDropdownUI { } }) } - this.dropdownLogic.forceSend(selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, finalCb) + this.blockchain.deployContract(selectedContract, args, contractMetadata, compilerContracts, {continueCb, promptCb, statusCb, finalCb}, confirmationCb) + } + + getConfirmationCb (modalDialog, confirmDialog) { + const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { + if (network.name !== 'Main') { + return continueTxExecution(null) + } + const amount = this.dropdownLogic.fromWei(tx.value, true, 'ether') + const content = confirmDialog(tx, amount, gasEstimation, null, this.dropdownLogic.determineGasFees(tx), this.blockchain.determineGasPrice) + + 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 gas price is not correct') + } else { + var gasPrice = this.dropdownLogic.toWei(content.querySelector('#gasprice').value, 'gwei') + continueTxExecution(gasPrice) + } + }}, { + label: 'Cancel', + fn: () => { + return cancelCb('Transaction canceled by user.') + } + } + ) + } + + return confirmationCb } loadFromAddress () { @@ -215,7 +255,6 @@ class ContractDropdownUI { } ) } - } module.exports = ContractDropdownUI diff --git a/src/app/tabs/runTab/model/blockchain.js b/src/app/tabs/runTab/model/blockchain.js new file mode 100644 index 0000000000..db5643136b --- /dev/null +++ b/src/app/tabs/runTab/model/blockchain.js @@ -0,0 +1,98 @@ +const remixLib = require('remix-lib') +const txFormat = remixLib.execution.txFormat +const txExecution = remixLib.execution.txExecution +const typeConversion = remixLib.execution.typeConversion +const Web3 = require('web3') + +class Blockchain { + + constructor (executionContext, udapp) { + this.executionContext = executionContext + this.udapp = udapp + } + + async deployContract (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { + const { continueCb, promptCb, statusCb, finalCb } = callbacks + + var constructor = selectedContract.getConstructorInterface() + if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { + return txFormat.buildData(selectedContract.name, selectedContract.object, compilerContracts, true, constructor, args, (error, data) => { + if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) + + statusCb(`creation of ${selectedContract.name} pending...`) + this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) + }, statusCb, (data, runTxCallback) => { + // called for libraries deployment + this.runTransaction(data, continueCb, promptCb, confirmationCb, runTxCallback) + }) + } + if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) + txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { + if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) + + statusCb(`creation of ${selectedContract.name} pending...`) + this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) + }) + } + + runTransaction (data, continueCb, promptCb, confirmationCb, finalCb) { + this.udapp.runTx(data, confirmationCb, continueCb, promptCb, finalCb) + } + + createContract (selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) { + if (data) { + data.contractName = selectedContract.name + data.linkReferences = selectedContract.bytecodeLinkReferences + data.contractABI = selectedContract.abi + } + + this.udapp.createContract(data, confirmationCb, continueCb, promptCb, + (error, txResult) => { + if (error) { + return finalCb(`creation of ${selectedContract.name} errored: ${error}`) + } + var isVM = this.executionContext.isVM() + if (isVM) { + var vmError = txExecution.checkVMError(txResult) + if (vmError.error) { + return finalCb(vmError.message) + } + } + if (txResult.result.status && txResult.result.status === '0x0') { + return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) + } + var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress + finalCb(null, selectedContract, address) + } + ) + } + + determineGasPrice (cb) { + this.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 = this.fromWei(gasPrice, false, 'gwei') + cb(null, gasPriceValue) + } catch (e) { + cb(warnMessage + e.message, null, false) + } + }) + } + + getGasPrice (cb) { + return this.executionContext.web3().eth.getGasPrice(cb) + } + + fromWei (value, doTypeConversion, unit) { + if (doTypeConversion) { + return Web3.utils.fromWei(typeConversion.toInt(value), unit || 'ether') + } + return Web3.utils.fromWei(value.toString(10), unit || 'ether') + } + +} + +module.exports = Blockchain diff --git a/src/app/tabs/runTab/model/dropdownlogic.js b/src/app/tabs/runTab/model/dropdownlogic.js index a5c603d6fe..573245d223 100644 --- a/src/app/tabs/runTab/model/dropdownlogic.js +++ b/src/app/tabs/runTab/model/dropdownlogic.js @@ -1,30 +1,21 @@ var ethJSUtil = require('ethereumjs-util') var remixLib = require('remix-lib') var txHelper = remixLib.execution.txHelper -var txFormat = remixLib.execution.txFormat -var executionContext = remixLib.execution.executionContext var typeConversion = remixLib.execution.typeConversion -var txExecution = remixLib.execution.txExecution var CompilerAbstract = require('../../../compiler/compiler-abstract') var EventManager = remixLib.EventManager var Web3 = require('web3') class DropdownLogic { - constructor (fileManager, compilersArtefacts, config, editor, udapp, filePanel, runView) { + constructor (compilersArtefacts, config, editor, runView) { this.compilersArtefacts = compilersArtefacts this.config = config this.editor = editor - this.udapp = udapp this.runView = runView - this.filePanel = filePanel this.event = new EventManager() this.listenToCompilationEvents() - - fileManager.events.on('currentFileChanged', (currentFile) => { - this.event.trigger('currentFileChanged', [currentFile]) - }) } // TODO: can be moved up; the event in contractDropdown will have to refactored a method instead @@ -120,185 +111,27 @@ class DropdownLogic { return Web3.utils.toBN(gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10), unit || 'gwei'))) } - getGasPrice (cb) { - return executionContext.web3().eth.getGasPrice(cb) - } - - isVM () { - return executionContext.isVM() - } - - // TODO: check if selectedContract and data can be joined - createContract (selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, finalCb) { - if (data) { - data.contractName = selectedContract.name - data.linkReferences = selectedContract.bytecodeLinkReferences - data.contractABI = selectedContract.abi - } - - var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) + determineGasFees (tx) { + const determineGasFeesCb = (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 = this.calculateFee(tx.gas, gasPrice) + txFeeText = ' ' + this.fromWei(fee, false, 'ether') + ' Ether' + priceStatus = true + } catch (e) { + txFeeText = ' Please fix this issue before sending any transaction. ' + e.message + priceStatus = false } - var amount = Web3.utils.fromWei(typeConversion.toInt(tx.value), 'ether') - - // TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily - var content = confirmDialog(tx, amount, gasEstimation, this.recorder, - (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 = Web3.utils.toBN(tx.gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10), 'gwei'))) - txFeeText = ' ' + Web3.utils.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 = Web3.utils.fromWei(gasPrice.toString(10), 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - 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 gas price is not correct') - } else { - var gasPrice = Web3.utils.toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - }) + cb(txFeeText, priceStatus) } - this.udapp.createContract(data, confirmationCb, continueCb, promptCb, - (error, txResult) => { - if (error) { - return finalCb(`creation of ${selectedContract.name} errored: ${error}`) - } - var isVM = executionContext.isVM() - if (isVM) { - var vmError = txExecution.checkVMError(txResult) - if (vmError.error) { - return finalCb(vmError.message) - } - } - if (txResult.result.status && txResult.result.status === '0x0') { - return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) - } - var address = isVM ? txResult.result.createdAddress : txResult.result.contractAddress - finalCb(null, selectedContract, address) - } - ) - } - - runTransaction (data, continueCb, promptCb, modalDialog, confirmDialog, finalCb) { - var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - if (network.name !== 'Main') { - return continueTxExecution(null) - } - var amount = this.fromWei(tx.value, true, 'ether') - var content = confirmDialog(tx, amount, gasEstimation, null, - (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 = this.calculateFee(tx.gas, gasPrice) - txFeeText = ' ' + this.fromWei(fee, false, 'ether') + ' Ether' - priceStatus = true - } catch (e) { - txFeeText = ' Please fix this issue before sending any transaction. ' + e.message - priceStatus = false - } - cb(txFeeText, priceStatus) - }, - (cb) => { - this.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 = this.fromWei(gasPrice, false, 'gwei') - cb(null, gasPriceValue) - } catch (e) { - cb(warnMessage + e.message, null, false) - } - }) - } - ) - 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 gas price is not correct') - } else { - var gasPrice = this.toWei(content.querySelector('#gasprice').value, 'gwei') - continueTxExecution(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return cancelCb('Transaction canceled by user.') - } - } - ) - } - - this.udapp.runTx(data, confirmationCb, continueCb, promptCb, finalCb) + return determineGasFeesCb } - async forceSend (selectedContract, args, continueCb, promptCb, modalDialog, confirmDialog, statusCb, cb) { - var constructor = selectedContract.getConstructorInterface() - // TODO: deployMetadataOf can be moved here - let contractMetadata - try { - contractMetadata = await this.runView.call('compilerMetadata', 'deployMetadataOf', selectedContract.name) - } catch (error) { - return statusCb(`creation of ${selectedContract.name} errored: ` + error) - } - if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { - return txFormat.buildData(selectedContract.name, selectedContract.object, this.compilersArtefacts['__last'].getData().contracts, true, constructor, args, (error, data) => { - if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) - - statusCb(`creation of ${selectedContract.name} pending...`) - this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) - }, statusCb, (data, runTxCallback) => { - // called for libraries deployment - this.runTransaction(data, continueCb, promptCb, modalDialog, confirmDialog, runTxCallback) - }) - } - if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`) - txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { - if (error) return statusCb(`creation of ${selectedContract.name} errored: ` + error) - - statusCb(`creation of ${selectedContract.name} pending...`) - this.createContract(selectedContract, data, continueCb, promptCb, modalDialog, confirmDialog, cb) - }) + getCompilerContracts () { + return this.compilersArtefacts['__last'].getData().contracts } } diff --git a/src/app/tabs/runTab/model/recorder.js b/src/app/tabs/runTab/model/recorder.js index c6c68f4d94..b25070aac2 100644 --- a/src/app/tabs/runTab/model/recorder.js +++ b/src/app/tabs/runTab/model/recorder.js @@ -2,7 +2,6 @@ var async = require('async') var ethutil = require('ethereumjs-util') var remixLib = require('remix-lib') var EventManager = remixLib.EventManager -var executionContext = remixLib.execution.executionContext var format = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper var typeConversion = remixLib.execution.typeConversion @@ -15,9 +14,10 @@ var Web3 = require('web3') * */ class Recorder { - constructor (udapp, fileManager, config) { + constructor (executionContext, udapp, fileManager, config) { var self = this self.event = new EventManager() + self.executionContext = executionContext self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} } this.udapp = udapp this.fileManager = fileManager @@ -74,7 +74,7 @@ class Recorder { if (error) return console.log(error) if (call) return - const rawAddress = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress + const rawAddress = this.executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (!rawAddress) return // not a contract creation const stringAddress = this.addressToString(rawAddress) const address = ethutil.toChecksumAddress(stringAddress) @@ -82,7 +82,7 @@ class Recorder { this.data._createdContracts[address] = timestamp this.data._createdContractsReverse[timestamp] = address }) - executionContext.event.register('contextChanged', this.clearAll.bind(this)) + this.executionContext.event.register('contextChanged', this.clearAll.bind(this)) this.event.register('newTxRecorded', (count) => { this.event.trigger('recorderCountChange', [count]) }) @@ -261,7 +261,7 @@ class Recorder { console.error(err) logCallBack(err + '. Execution failed at ' + index) } else { - const rawAddress = executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress + const rawAddress = self.executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress if (rawAddress) { const stringAddress = self.addressToString(rawAddress) const address = ethutil.toChecksumAddress(stringAddress) @@ -335,7 +335,7 @@ class Recorder { cb(txFeeText, priceStatus) }, (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { + this.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) diff --git a/src/app/tabs/runTab/model/settings.js b/src/app/tabs/runTab/model/settings.js index 3d514487d2..e126e583f0 100644 --- a/src/app/tabs/runTab/model/settings.js +++ b/src/app/tabs/runTab/model/settings.js @@ -4,11 +4,11 @@ var remixLib = require('remix-lib') var Web3 = require('web3') const addTooltip = require('../../../ui/tooltip') var EventManager = remixLib.EventManager -var executionContext = remixLib.execution.executionContext class Settings { - constructor (udapp) { + constructor (executionContext, udapp) { + this.executionContext = executionContext this.udapp = udapp this.event = new EventManager() @@ -16,15 +16,15 @@ class Settings { this.event.trigger('transactionExecuted', [error, from, to, data, lookupOnly, txResult]) }) - executionContext.event.register('contextChanged', (context, silent) => { + this.executionContext.event.register('contextChanged', (context, silent) => { this.event.trigger('contextChanged', [context, silent]) }) - executionContext.event.register('addProvider', (network) => { + this.executionContext.event.register('addProvider', (network) => { this.event.trigger('addProvider', [network]) }) - executionContext.event.register('removeProvider', (name) => { + this.executionContext.event.register('removeProvider', (name) => { this.event.trigger('removeProvider', [name]) }) @@ -32,15 +32,15 @@ class Settings { } changeExecutionContext (context, confirmCb, infoCb, cb) { - return executionContext.executionContextChange(context, null, confirmCb, infoCb, cb) + return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb) } setProviderFromEndpoint (target, context, cb) { - return executionContext.setProviderFromEndpoint(target, context, cb) + return this.executionContext.setProviderFromEndpoint(target, context, cb) } getProvider () { - return executionContext.getProvider() + return this.executionContext.getProvider() } getAccountBalanceForAddress (address, cb) { @@ -50,7 +50,7 @@ class Settings { updateNetwork (cb) { this.networkcallid++ ((callid) => { - executionContext.detectNetwork((err, { id, name } = {}) => { + this.executionContext.detectNetwork((err, { id, name } = {}) => { if (this.networkcallid > callid) return this.networkcallid++ if (err) { @@ -70,18 +70,18 @@ class Settings { } isWeb3Provider () { - var isVM = executionContext.isVM() - var isInjected = executionContext.getProvider() === 'injected' + var isVM = this.executionContext.isVM() + var isInjected = this.executionContext.getProvider() === 'injected' return (!isVM && !isInjected) } isInjectedWeb3 () { - return executionContext.getProvider() === 'injected' + return this.executionContext.getProvider() === 'injected' } signMessage (message, account, passphrase, cb) { - var isVM = executionContext.isVM() - var isInjected = executionContext.getProvider() === 'injected' + var isVM = this.executionContext.isVM() + var isInjected = this.executionContext.getProvider() === 'injected' if (isVM) { const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message)) @@ -99,7 +99,7 @@ class Settings { const hashedMsg = Web3.utils.sha3(message) try { addTooltip('Please check your provider to approve') - executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { + this.executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => { cb(error.message, hashedMsg, signedData) }) } catch (e) { @@ -110,7 +110,7 @@ class Settings { const hashedMsg = Web3.utils.sha3(message) try { - var personal = new Personal(executionContext.web3().currentProvider) + var personal = new Personal(this.executionContext.web3().currentProvider) personal.sign(hashedMsg, account, passphrase, (error, signedData) => { cb(error.message, hashedMsg, signedData) }) diff --git a/src/app/tabs/runTab/settings.js b/src/app/tabs/runTab/settings.js index 61f657fbe1..76032c4650 100644 --- a/src/app/tabs/runTab/settings.js +++ b/src/app/tabs/runTab/settings.js @@ -320,7 +320,7 @@ class SettingsUI { this.netUI.innerHTML = 'can\'t detect network ' return } - let network = this._components.networkModule.getNetworkProvider + let network = this._components.networkModule.getNetworkProvider.bind(this._components.networkModule) this.netUI.innerHTML = (network() !== 'vm') ? `${name} (${id || '-'}) network` : '' }) this.fillAccountsList() diff --git a/src/app/udapp/make-udapp.js b/src/app/udapp/make-udapp.js index 202315228d..04f32fef74 100644 --- a/src/app/udapp/make-udapp.js +++ b/src/app/udapp/make-udapp.js @@ -1,12 +1,11 @@ var registry = require('../../global/registry') var remixLib = require('remix-lib') var yo = require('yo-yo') -var executionContext = remixLib.execution.executionContext var Txlistener = remixLib.execution.txListener var EventsDecoder = remixLib.execution.EventsDecoder var TransactionReceiptResolver = require('../../lib/transactionReceiptResolver') -export function makeUdapp (udapp, compilersArtefacts, logHtmlCallback) { +export function makeUdapp (udapp, executionContext, compilersArtefacts, logHtmlCallback) { // ----------------- UniversalDApp ----------------- // TODO: to remove when possible udapp.event.register('transactionBroadcasted', (txhash, networkName) => { @@ -15,7 +14,7 @@ export function makeUdapp (udapp, compilersArtefacts, logHtmlCallback) { }) // ----------------- Tx listener ----------------- - const transactionReceiptResolver = new TransactionReceiptResolver() + const transactionReceiptResolver = new TransactionReceiptResolver(executionContext) const txlistener = new Txlistener({ api: { @@ -29,7 +28,7 @@ export function makeUdapp (udapp, compilersArtefacts, logHtmlCallback) { }, event: { udapp: udapp.event - }}) + }}, executionContext) registry.put({api: txlistener, name: 'txlistener'}) udapp.startListening(txlistener) diff --git a/src/app/udapp/run-tab.js b/src/app/udapp/run-tab.js index 01f9b3e322..a9e33c7184 100644 --- a/src/app/udapp/run-tab.js +++ b/src/app/udapp/run-tab.js @@ -15,9 +15,9 @@ const Recorder = require('../tabs/runTab/model/recorder.js') const RecorderUI = require('../tabs/runTab/recorder.js') const DropdownLogic = require('../tabs/runTab/model/dropdownlogic.js') const ContractDropdownUI = require('../tabs/runTab/contractDropdown.js') +const Blockchain = require('../tabs/runTab/model/blockchain.js') const UniversalDAppUI = require('../ui/universal-dapp-ui') -const executionContext = require('../../execution-context') const profile = { name: 'udapp', @@ -35,11 +35,12 @@ const profile = { export class RunTab extends LibraryPlugin { - constructor (udapp, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, mainView) { + constructor (udapp, executionContext, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, mainView) { super(udapp, profile) this.event = new EventManager() this.config = config this.udapp = udapp + this.executionContext = executionContext this.fileManager = fileManager this.editor = editor this.logCallback = (msg) => { mainView.getTerminal().logHtml(msg) } @@ -49,7 +50,7 @@ export class RunTab extends LibraryPlugin { } onActivationInternal () { - this.udappUI = new UniversalDAppUI(this.udapp, this.logCallback) + this.udappUI = new UniversalDAppUI(this.udapp, this.logCallback, this.executionContext) this.udapp.resetAPI({ getAddress: (cb) => { cb(null, $('#txorigin').val()) @@ -122,7 +123,7 @@ export class RunTab extends LibraryPlugin { } renderSettings (udapp) { - var settings = new Settings(udapp) + var settings = new Settings(this.executionContext, udapp) this.settingsUI = new SettingsUI(settings, this.networkModule) this.settingsUI.event.register('clearInstance', () => { @@ -131,8 +132,11 @@ export class RunTab extends LibraryPlugin { } renderDropdown (udappUI, fileManager, compilersArtefacts, config, editor, udapp, filePanel, logCallback) { - const dropdownLogic = new DropdownLogic(fileManager, compilersArtefacts, config, editor, udapp, filePanel, this) - this.contractDropdownUI = new ContractDropdownUI(dropdownLogic, logCallback) + const dropdownLogic = new DropdownLogic(compilersArtefacts, config, editor, this) + const blockchain = new Blockchain(this.executionContext, udapp) + this.contractDropdownUI = new ContractDropdownUI(blockchain, dropdownLogic, logCallback, this) + + fileManager.events.on('currentFileChanged', this.contractDropdownUI.changeCurrentFile.bind(this.contractDropdownUI)) this.contractDropdownUI.event.register('clearInstance', () => { const noInstancesText = this.noInstancesText @@ -149,7 +153,7 @@ export class RunTab extends LibraryPlugin { renderRecorder (udapp, udappUI, fileManager, config, logCallback) { this.recorderCount = yo`0` - const recorder = new Recorder(udapp, fileManager, config) + const recorder = new Recorder(this.executionContext, udapp, fileManager, config) recorder.event.register('recorderCountChange', (count) => { this.recorderCount.innerText = count }) @@ -200,9 +204,9 @@ export class RunTab extends LibraryPlugin { render () { this.onActivationInternal() - executionContext.init(this.config) - executionContext.stopListenOnLastBlock() - executionContext.listenOnLastBlock() + this.executionContext.init(this.config) + this.executionContext.stopListenOnLastBlock() + this.executionContext.listenOnLastBlock() this.udapp.resetEnvironment() this.renderInstanceContainer() this.renderSettings(this.udapp) diff --git a/src/app/ui/txLogger.js b/src/app/ui/txLogger.js index 0d694b2295..010a277715 100644 --- a/src/app/ui/txLogger.js +++ b/src/app/ui/txLogger.js @@ -8,7 +8,6 @@ var remixLib = require('remix-lib') var EventManager = require('../../lib/events') var helper = require('../../lib/helper') -var executionContext = require('../../execution-context') var modalDialog = require('./modal-dialog-custom') var typeConversion = remixLib.execution.typeConversion var globlalRegistry = require('../../global/registry') @@ -117,7 +116,7 @@ var css = csjs` * */ class TxLogger { - constructor (eventsDecoder, txListener, terminal) { + constructor (eventsDecoder, txListener, terminal, executionContext) { this.event = new EventManager() this.seen = {} function filterTx (value, query) { @@ -140,7 +139,7 @@ class TxLogger { if (data.tx.isCall) { el = renderCall(this, data) } else { - el = renderKnownTransaction(this, data) + el = renderKnownTransaction(this, data, executionContext) } this.seen[data.tx.hash] = el append(el) @@ -149,7 +148,7 @@ class TxLogger { this.logUnknownTX = this.terminal.registerCommand('unknownTransaction', (args, cmds, append) => { // triggered for transaction AND call var data = args[0] - var el = renderUnknownTransaction(this, data) + var el = renderUnknownTransaction(this, data, executionContext) append(el) }, { activate: false, filterFn: filterTx }) @@ -205,7 +204,7 @@ function log (self, tx, receipt) { } } -function renderKnownTransaction (self, data) { +function renderKnownTransaction (self, data, executionContext) { var from = data.tx.from var to = data.resolvedData.contractName + '.' + data.resolvedData.fn var obj = {from, to} @@ -214,7 +213,7 @@ function renderKnownTransaction (self, data) {
txDetails(e, tx, data, obj)}> ${checkTxStatus(data.receipt, txType)} - ${context(self, {from, to, data})} + ${context(self, {from, to, data}, executionContext)}
@@ -251,7 +250,7 @@ function renderCall (self, data) { return tx } -function renderUnknownTransaction (self, data) { +function renderUnknownTransaction (self, data, executionContext) { var from = data.tx.from var to = data.tx.to var obj = {from, to} @@ -260,7 +259,7 @@ function renderUnknownTransaction (self, data) {
txDetails(e, tx, data, obj)}> ${checkTxStatus(data.receipt || data.tx, txType)} - ${context(self, {from, to, data})} + ${context(self, {from, to, data}, executionContext)}
debug(e, data, self)}>Debug
@@ -291,7 +290,7 @@ function checkTxStatus (tx, type) { } } -function context (self, opts) { +function context (self, opts, executionContext) { var data = opts.data || '' var from = opts.from ? helper.shortenHexData(opts.from) : '' var to = opts.to diff --git a/src/app/ui/universal-dapp-ui.js b/src/app/ui/universal-dapp-ui.js index cc9a8c3e44..a02e616280 100644 --- a/src/app/ui/universal-dapp-ui.js +++ b/src/app/ui/universal-dapp-ui.js @@ -15,17 +15,16 @@ var typeConversion = remixLib.execution.typeConversion var txExecution = remixLib.execution.txExecution var txFormat = remixLib.execution.txFormat -var executionContext = require('../../execution-context') - var confirmDialog = require('./confirmDialog') var modalCustom = require('./modal-dialog-custom') var modalDialog = require('./modaldialog') var TreeView = require('./TreeView') -function UniversalDAppUI (udapp, logCallback) { +function UniversalDAppUI (udapp, logCallback, executionContext) { this.udapp = udapp this.logCallback = logCallback this.compilerData = {contractsDetails: {}} + this.executionContext = executionContext } function decodeResponseToTreeView (response, fnabi) { @@ -181,7 +180,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { cb(txFeeText, priceStatus) }, (cb) => { - executionContext.web3().eth.getGasPrice((error, gasPrice) => { + self.executionContext.web3().eth.getGasPrice((error, gasPrice) => { const warnMessage = ' Please fix this issue before sending any transaction. ' if (error) { return cb('Unable to retrieve the current network gas price.' + warnMessage + error) @@ -263,7 +262,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { if (args.funABI.type === 'fallback') data.dataHex = value self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, promptCb, (error, txResult) => { if (!error) { - var isVM = executionContext.isVM() + var isVM = self.executionContext.isVM() if (isVM) { var vmError = txExecution.checkVMError(txResult) if (vmError.error) { @@ -272,7 +271,7 @@ UniversalDAppUI.prototype.getCallButton = function (args) { } } if (lookupOnly) { - const decoded = decodeResponseToTreeView(executionContext.isVM() ? txResult.result.execResult.returnValue : ethJSUtil.toBuffer(txResult.result), args.funABI) + const decoded = decodeResponseToTreeView(self.executionContext.isVM() ? txResult.result.execResult.returnValue : ethJSUtil.toBuffer(txResult.result), args.funABI) outputCb(decoded) } } else { diff --git a/src/execution-context.js b/src/execution-context.js deleted file mode 100644 index eafad31e9d..0000000000 --- a/src/execution-context.js +++ /dev/null @@ -1,4 +0,0 @@ -var remixLib = require('remix-lib') -var executionContext = remixLib.execution.executionContext - -module.exports = executionContext diff --git a/src/lib/cmdInterpreterAPI.js b/src/lib/cmdInterpreterAPI.js index 2494e3b7fe..fffeb24f59 100644 --- a/src/lib/cmdInterpreterAPI.js +++ b/src/lib/cmdInterpreterAPI.js @@ -5,7 +5,6 @@ var remixLib = require('remix-lib') var EventManager = require('../lib/events') var CompilerImport = require('../app/compiler/compiler-imports') -var executionContext = require('../execution-context') var toolTip = require('../app/ui/tooltip') var globalRegistry = require('../global/registry') var SourceHighlighter = require('../app/editor/sourceHighlighter') @@ -15,9 +14,10 @@ var solidityTypeFormatter = require('../app/tabs/debugger/debuggerUI/vmDebugger/ var GistHandler = require('./gist-handler') class CmdInterpreterAPI { - constructor (terminal, localRegistry) { + constructor (terminal, localRegistry, executionContext) { const self = this self.event = new EventManager() + self.executionContext = executionContext self._components = {} self._components.registry = localRegistry || globalRegistry self._components.terminal = terminal @@ -62,14 +62,14 @@ class CmdInterpreterAPI { debug (hash, cb) { var self = this delete self.d - executionContext.web3().eth.getTransaction(hash, (error, tx) => { + self.executionContext.web3().eth.getTransaction(hash, (error, tx) => { if (error) return cb(error) var debugSession = new RemixDebug({ compilationResult: () => { return self._deps.compilersArtefacts['__last'].getData() } }) - debugSession.addProvider('web3', executionContext.web3()) + debugSession.addProvider('web3', self.executionContext.web3()) debugSession.switchProvider('web3') debugSession.debug(tx) self.d = debugSession @@ -180,7 +180,7 @@ class CmdInterpreterAPI { }) } setproviderurl (url, cb) { - executionContext.setProviderFromEndpoint(url, 'web3', (error) => { + this.executionContext.setProviderFromEndpoint(url, 'web3', (error) => { if (error) toolTip(error) if (cb) cb() }) diff --git a/src/lib/transactionReceiptResolver.js b/src/lib/transactionReceiptResolver.js index e498779c53..78608eff31 100644 --- a/src/lib/transactionReceiptResolver.js +++ b/src/lib/transactionReceiptResolver.js @@ -1,16 +1,16 @@ 'use strict' -var executionContext = require('../execution-context') module.exports = class TransactionReceiptResolver { - constructor () { + constructor (executionContext) { this._transactionReceipts = {} + this.executionContext = executionContext } resolve (tx, cb) { if (this._transactionReceipts[tx.hash]) { return cb(null, this._transactionReceipts[tx.hash]) } - executionContext.web3().eth.getTransactionReceipt(tx.hash, (error, receipt) => { + this.executionContext.web3().eth.getTransactionReceipt(tx.hash, (error, receipt) => { if (!error) { this._transactionReceipts[tx.hash] = receipt cb(null, receipt)