Merge pull request #2510 from ethereum/refactor_logic2

Refactor executionContext and udapp, into common class
pull/1/head
Iuri Matias 5 years ago committed by GitHub
commit 1ff9e8ce2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      src/app.js
  2. 22
      src/app/files/compiler-metadata.js
  3. 6
      src/app/panels/main-view.js
  4. 12
      src/app/panels/terminal.js
  5. 6
      src/app/tabs/runTab/contractDropdown.js
  6. 172
      src/app/tabs/runTab/model/blockchain.js
  7. 36
      src/app/tabs/runTab/model/dropdownlogic.js
  8. 119
      src/app/tabs/runTab/model/recorder.js
  9. 124
      src/app/tabs/runTab/model/settings.js
  10. 39
      src/app/tabs/runTab/recorder.js
  11. 36
      src/app/tabs/runTab/settings.js
  12. 36
      src/app/udapp/make-udapp.js
  13. 27
      src/app/udapp/run-tab.js
  14. 20
      src/app/ui/txLogger.js
  15. 10
      src/lib/cmdInterpreterAPI.js
  16. 6
      src/lib/transactionReceiptResolver.js

@ -24,6 +24,8 @@ var CompilerImport = require('./app/compiler/compiler-imports')
var executionContext = remixLib.execution.executionContext
const Blockchain = require('./app/tabs/runTab/model/blockchain.js')
const PluginManagerComponent = require('./app/components/plugin-manager-component')
const CompilersArtefacts = require('./app/compiler/compiler-artefacts')
@ -222,14 +224,18 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// ----------------- fileManager servive ----------------------------
const fileManager = new FileManager(editor)
registry.put({api: fileManager, name: 'filemanager'})
// ----------------- universal dapp: run transaction, listen on transactions, decode events
const udapp = new UniversalDApp(registry.get('config').api, executionContext)
const blockchain = new Blockchain(executionContext, udapp)
// ----------------- compilation metadata generation servive ----------------------------
const compilerMetadataGenerator = new CompilerMetadata(executionContext, fileManager, registry.get('config').api)
const compilerMetadataGenerator = new CompilerMetadata(blockchain, 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, executionContext)
const {eventsDecoder, txlistener} = makeUdapp(udapp, executionContext, compilersArtefacts, (domEl) => mainview.getTerminal().logHtml(domEl))
const {eventsDecoder, txlistener} = makeUdapp(blockchain, compilersArtefacts, (domEl) => mainview.getTerminal().logHtml(domEl))
// ----------------- network service (resolve network id / name) ----------------------------
const networkModule = new NetworkModule(executionContext)
// ----------------- convert offset to line/column service ----------------------------
@ -249,7 +255,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, executionContext)
const mainview = new MainView(editor, appPanel, fileManager, appManager, txlistener, eventsDecoder, blockchain)
registry.put({ api: mainview, name: 'mainview' })
appManager.register([
@ -293,6 +299,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
registry.get('filemanager').api
)
const run = new RunTab(
blockchain,
udapp,
executionContext,
registry.get('config').api,

@ -11,14 +11,13 @@ const profile = {
}
class CompilerMetadata extends Plugin {
constructor (executionContext, fileManager, config) {
constructor (blockchain, 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']
self.innerPath = 'artifacts'
this.blockchain = blockchain
this.fileManager = fileManager
this.config = config
this.networks = ['VM:-', 'main:1', 'ropsten:3', 'rinkeby:4', 'kovan:42', 'görli:5', 'Custom']
this.innerPath = 'artifacts'
}
_JSONFileName (path, contractName) {
@ -93,15 +92,14 @@ class CompilerMetadata extends Plugin {
// TODO: is only called by dropdownLogic and can be moved there
deployMetadataOf (contractName) {
return new Promise((resolve, reject) => {
var self = this
var provider = self.fileManager.currentFileProvider()
var path = self.fileManager.currentPath()
var provider = this.fileManager.currentFileProvider()
var path = this.fileManager.currentPath()
if (provider && path) {
self.executionContext.detectNetwork((err, { id, name } = {}) => {
this.blockchain.detectNetwork((err, { id, name } = {}) => {
if (err) {
console.log(err)
} else {
var fileName = self._JSONFileName(path, contractName)
var fileName = this._JSONFileName(path, contractName)
provider.get(fileName, (error, content) => {
if (error) return reject(error)
if (!content) return resolve()

@ -20,7 +20,7 @@ var css = csjs`
`
export class MainView {
constructor (editor, mainPanel, fileManager, appManager, txListener, eventsDecoder, executionContext) {
constructor (editor, mainPanel, fileManager, appManager, txListener, eventsDecoder, blockchain) {
var self = this
self.event = new EventManager()
self._view = {}
@ -31,7 +31,7 @@ export class MainView {
self.mainPanel = mainPanel
self.txListener = txListener
self.eventsDecoder = eventsDecoder
self.executionContext = executionContext
self.blockchain = blockchain
this.appManager = appManager
this.init()
}
@ -101,7 +101,7 @@ export class MainView {
appManager: this.appManager,
eventsDecoder: this.eventsDecoder,
txListener: this.txListener,
executionContext: this.executionContext
blockchain: this.blockchain
},
{
getPosition: (event) => {

@ -41,7 +41,7 @@ class Terminal extends Plugin {
super(profile)
var self = this
self.event = new EventManager()
self.executionContext = opts.executionContext
self.blockchain = opts.blockchain
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, null, self.executionContext)
self._components.cmdInterpreter = new CommandInterpreterAPI(this, null, self.blockchain)
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.executionContext)
self._components.txLogger = new TxLogger(self._opts.eventsDecoder, self._opts.txListener, this, self.blockchain)
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, self.executionContext)
var context = domTerminalFeatures(self, scopedCommands, self.blockchain)
try {
var cmds = vm.createContext(Object.assign(self._jsSandboxContext, context, self._jsSandboxRegistered))
var result = vm.runInContext(script, cmds)
@ -680,12 +680,12 @@ class Terminal extends Plugin {
}
}
function domTerminalFeatures (self, scopedCommands, executionContext) {
function domTerminalFeatures (self, scopedCommands, blockchain) {
return {
swarmgw,
ethers,
remix: self._components.cmdInterpreter,
web3: new Web3(executionContext.web3().currentProvider),
web3: new Web3(blockchain.web3().currentProvider),
console: {
log: function () { scopedCommands.log.apply(scopedCommands, arguments) },
info: function () { scopedCommands.info.apply(scopedCommands, arguments) },

@ -209,8 +209,8 @@ class ContractDropdownUI {
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)
const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice)
modalDialog('Confirm transaction', content,
{ label: 'Confirm',
@ -220,7 +220,7 @@ class ContractDropdownUI {
if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct')
} else {
var gasPrice = this.dropdownLogic.toWei(content.querySelector('#gasprice').value, 'gwei')
var gasPrice = this.blockchain.toWei(content.querySelector('#gasprice').value, 'gwei')
continueTxExecution(gasPrice)
}
}}, {

@ -2,13 +2,47 @@ const remixLib = require('remix-lib')
const txFormat = remixLib.execution.txFormat
const txExecution = remixLib.execution.txExecution
const typeConversion = remixLib.execution.typeConversion
const Txlistener = remixLib.execution.txListener
const EventManager = remixLib.EventManager
const ethJSUtil = require('ethereumjs-util')
const Personal = require('web3-eth-personal')
const Web3 = require('web3')
class Blockchain {
constructor (executionContext, udapp) {
this.event = new EventManager()
this.executionContext = executionContext
this.udapp = udapp
this.networkcallid = 0
this.setupEvents()
}
setupEvents () {
this.executionContext.event.register('contextChanged', (context, silent) => {
this.event.trigger('contextChanged', [context, silent])
})
this.executionContext.event.register('addProvider', (network) => {
this.event.trigger('addProvider', [network])
})
this.executionContext.event.register('removeProvider', (name) => {
this.event.trigger('removeProvider', [name])
})
this.udapp.event.register('initiatingTransaction', (timestamp, tx, payLoad) => {
this.event.trigger('initiatingTransaction', [timestamp, tx, payLoad])
})
this.udapp.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => {
this.event.trigger('transactionExecuted', [error, from, to, data, call, txResult, timestamp])
})
this.udapp.event.register('transactionBroadcasted', (txhash, networkName) => {
this.event.trigger('transactionBroadcasted', [txhash, networkName])
})
}
async deployContract (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) {
@ -93,6 +127,144 @@ class Blockchain {
return Web3.utils.fromWei(value.toString(10), unit || 'ether')
}
toWei (value, unit) {
return Web3.utils.toWei(value, unit || 'gwei')
}
calculateFee (gas, gasPrice, unit) {
return Web3.utils.toBN(gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10), unit || 'gwei')))
}
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
}
cb(txFeeText, priceStatus)
}
return determineGasFeesCb
}
getAddressFromTransactionResult (txResult) {
return this.executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress
}
changeExecutionContext (context, confirmCb, infoCb, cb) {
return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb)
}
setProviderFromEndpoint (target, context, cb) {
return this.executionContext.setProviderFromEndpoint(target, context, cb)
}
getProvider () {
return this.executionContext.getProvider()
}
getAccountBalanceForAddress (address, cb) {
return this.udapp.getBalanceInEther(address, cb)
}
updateNetwork (cb) {
this.networkcallid++
((callid) => {
this.executionContext.detectNetwork((err, { id, name } = {}) => {
if (this.networkcallid > callid) return
this.networkcallid++
if (err) {
return cb(err)
}
cb(null, {id, name})
})
})(this.networkcallid)
}
detectNetwork (cb) {
return this.executionContext.detectNetwork(cb)
}
newAccount (passphraseCb, cb) {
return this.udapp.newAccount('', passphraseCb, cb)
}
getAccounts (cb) {
return this.udapp.getAccounts(cb)
}
isWeb3Provider () {
var isVM = this.executionContext.isVM()
var isInjected = this.executionContext.getProvider() === 'injected'
return (!isVM && !isInjected)
}
isInjectedWeb3 () {
return this.executionContext.getProvider() === 'injected'
}
signMessage (message, account, passphrase, cb) {
var isVM = this.executionContext.isVM()
var isInjected = this.executionContext.getProvider() === 'injected'
if (isVM) {
const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message))
var privKey = this.udapp.accounts[account].privateKey
try {
var rsv = ethJSUtil.ecsign(personalMsg, privKey)
var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s)
cb(null, '0x' + personalMsg.toString('hex'), signedData)
} catch (e) {
cb(e.message)
}
return
}
if (isInjected) {
const hashedMsg = Web3.utils.sha3(message)
try {
this.executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => {
cb(error.message, hashedMsg, signedData)
})
} catch (e) {
cb(e.message)
}
return
}
const hashedMsg = Web3.utils.sha3(message)
try {
var personal = new Personal(this.executionContext.web3().currentProvider)
personal.sign(hashedMsg, account, passphrase, (error, signedData) => {
cb(error.message, hashedMsg, signedData)
})
} catch (e) {
cb(e.message)
}
}
web3 () {
return this.executionContext.web3()
}
getTxListener (opts) {
opts.event = {
udapp: this.udapp.event
}
const txlistener = new Txlistener(opts, this.executionContext)
return txlistener
}
startListening (txlistener) {
this.udapp.startListening(txlistener)
}
}
module.exports = Blockchain

@ -1,10 +1,8 @@
var ethJSUtil = require('ethereumjs-util')
var remixLib = require('remix-lib')
var txHelper = remixLib.execution.txHelper
var typeConversion = remixLib.execution.typeConversion
var CompilerAbstract = require('../../../compiler/compiler-abstract')
var EventManager = remixLib.EventManager
var Web3 = require('web3')
class DropdownLogic {
constructor (compilersArtefacts, config, editor, runView) {
@ -96,40 +94,6 @@ class DropdownLogic {
}
}
fromWei (value, doTypeConversion, unit) {
if (doTypeConversion) {
return Web3.utils.fromWei(typeConversion.toInt(value), unit || 'ether')
}
return Web3.utils.fromWei(value.toString(10), unit || 'ether')
}
toWei (value, unit) {
return Web3.utils.toWei(value, unit || 'gwei')
}
calculateFee (gas, gasPrice, unit) {
return Web3.utils.toBN(gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10), unit || 'gwei')))
}
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
}
cb(txFeeText, priceStatus)
}
return determineGasFeesCb
}
getCompilerContracts () {
return this.compilersArtefacts['__last'].getData().contracts
}

@ -4,9 +4,7 @@ var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var format = remixLib.execution.txFormat
var txHelper = remixLib.execution.txHelper
var typeConversion = remixLib.execution.typeConversion
var helper = require('../../../../lib/helper.js')
var Web3 = require('web3')
/**
* Record transaction as long as the user create them.
@ -14,16 +12,15 @@ var Web3 = require('web3')
*
*/
class Recorder {
constructor (executionContext, udapp, fileManager, config) {
constructor (blockchain, fileManager, config) {
var self = this
self.event = new EventManager()
self.executionContext = executionContext
self.blockchain = blockchain
self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} }
this.udapp = udapp
this.fileManager = fileManager
this.config = config
this.udapp.event.register('initiatingTransaction', (timestamp, tx, payLoad) => {
this.blockchain.event.register('initiatingTransaction', (timestamp, tx, payLoad) => {
if (tx.useCall) return
var { from, to, value } = tx
@ -61,7 +58,7 @@ class Recorder {
if (thistimestamp) record.parameters[p] = `created{${thistimestamp}}`
}
this.udapp.getAccounts((error, accounts) => {
this.blockchain.getAccounts((error, accounts) => {
if (error) return console.log(error)
record.from = `account{${accounts.indexOf(from)}}`
self.data._usedAccounts[record.from] = from
@ -70,11 +67,11 @@ class Recorder {
}
})
this.udapp.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => {
this.blockchain.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => {
if (error) return console.log(error)
if (call) return
const rawAddress = this.executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress
const rawAddress = this.blockchain.getAddressFromTransactionResult(txResult)
if (!rawAddress) return // not a contract creation
const stringAddress = this.addressToString(rawAddress)
const address = ethutil.toChecksumAddress(stringAddress)
@ -82,7 +79,7 @@ class Recorder {
this.data._createdContracts[address] = timestamp
this.data._createdContractsReverse[timestamp] = address
})
this.executionContext.event.register('contextChanged', this.clearAll.bind(this))
this.blockchain.event.register('contextChanged', this.clearAll.bind(this))
this.event.register('newTxRecorded', (count) => {
this.event.trigger('recorderCountChange', [count])
})
@ -185,7 +182,6 @@ class Recorder {
* @param {Object} accounts
* @param {Object} options
* @param {Object} abis
* @param {Object} udapp
* @param {Function} newContractFn
*
*/
@ -197,8 +193,7 @@ class Recorder {
var record = self.resolveAddress(tx.record, accounts, options)
var abi = abis[tx.record.abi]
if (!abi) {
alertCb('cannot find ABI for ' + tx.record.abi + '. Execution stopped at ' + index)
return
return alertCb('cannot find ABI for ' + tx.record.abi + '. Execution stopped at ' + index)
}
/* Resolve Library */
if (record.linkReferences && Object.keys(record.linkReferences).length) {
@ -222,8 +217,7 @@ class Recorder {
}
if (!fnABI) {
alertCb('cannot resolve abi of ' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index)
cb('cannot resolve abi')
return
return cb('cannot resolve abi')
}
if (tx.record.parameters) {
/* check if we have some params to resolve */
@ -241,35 +235,32 @@ class Recorder {
tx.record.parameters[index] = value
})
} catch (e) {
alertCb('cannot resolve input parameters ' + JSON.stringify(tx.record.parameters) + '. Execution stopped at ' + index)
return
return alertCb('cannot resolve input parameters ' + JSON.stringify(tx.record.parameters) + '. Execution stopped at ' + index)
}
}
var data = format.encodeData(fnABI, tx.record.parameters, tx.record.bytecode)
if (data.error) {
alertCb(data.error + '. Record:' + JSON.stringify(record, null, '\t') + '. Execution stopped at ' + index)
cb(data.error)
return
} else {
logCallBack(`(${index}) ${JSON.stringify(record, null, '\t')}`)
logCallBack(`(${index}) data: ${data.data}`)
record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName, timestamp: tx.timestamp }
return cb(data.error)
}
self.udapp.runTx(record, confirmationCb, continueCb, promptCb,
logCallBack(`(${index}) ${JSON.stringify(record, null, '\t')}`)
logCallBack(`(${index}) data: ${data.data}`)
record.data = { dataHex: data.data, funArgs: tx.record.parameters, funAbi: fnABI, contractBytecode: tx.record.bytecode, contractName: tx.record.contractName, timestamp: tx.timestamp }
self.blockchain.runTransaction(record, continueCb, promptCb, confirmationCb,
function (err, txResult) {
if (err) {
console.error(err)
logCallBack(err + '. Execution failed at ' + index)
} else {
const rawAddress = self.executionContext.isVM() ? txResult.result.createdAddress : txResult.result.contractAddress
if (rawAddress) {
const stringAddress = self.addressToString(rawAddress)
const address = ethutil.toChecksumAddress(stringAddress)
// save back created addresses for the convertion from tokens to real adresses
self.data._createdContracts[address] = tx.timestamp
self.data._createdContractsReverse[tx.timestamp] = address
newContractFn(abi, address, record.contractName)
}
return logCallBack(err + '. Execution failed at ' + index)
}
const rawAddress = self.blockchain.getAddressFromTransactionResult(txResult)
if (rawAddress) {
const stringAddress = self.addressToString(rawAddress)
const address = ethutil.toChecksumAddress(stringAddress)
// save back created addresses for the convertion from tokens to real adresses
self.data._createdContracts[address] = tx.timestamp
self.data._createdContractsReverse[tx.timestamp] = address
newContractFn(abi, address, record.contractName)
}
cb(err)
}
@ -288,7 +279,7 @@ class Recorder {
return address
}
runScenario (continueCb, promptCb, alertCb, confirmDialog, modalDialog, logCallBack, cb) {
runScenario (continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) {
var currentFile = this.config.get('currentFile')
this.fileManager.fileProviderOf(currentFile).get(currentFile, (error, json) => {
if (error) {
@ -312,62 +303,6 @@ class Recorder {
return
}
var confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
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) => {
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)
}
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.')
}
})
}
this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, (abi, address, contractName) => {
cb(null, abi, address, contractName)
})

@ -1,124 +0,0 @@
var ethJSUtil = require('ethereumjs-util')
var Personal = require('web3-eth-personal')
var remixLib = require('remix-lib')
var Web3 = require('web3')
const addTooltip = require('../../../ui/tooltip')
var EventManager = remixLib.EventManager
class Settings {
constructor (executionContext, udapp) {
this.executionContext = executionContext
this.udapp = udapp
this.event = new EventManager()
this.udapp.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
this.event.trigger('transactionExecuted', [error, from, to, data, lookupOnly, txResult])
})
this.executionContext.event.register('contextChanged', (context, silent) => {
this.event.trigger('contextChanged', [context, silent])
})
this.executionContext.event.register('addProvider', (network) => {
this.event.trigger('addProvider', [network])
})
this.executionContext.event.register('removeProvider', (name) => {
this.event.trigger('removeProvider', [name])
})
this.networkcallid = 0
}
changeExecutionContext (context, confirmCb, infoCb, cb) {
return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb)
}
setProviderFromEndpoint (target, context, cb) {
return this.executionContext.setProviderFromEndpoint(target, context, cb)
}
getProvider () {
return this.executionContext.getProvider()
}
getAccountBalanceForAddress (address, cb) {
return this.udapp.getBalanceInEther(address, cb)
}
updateNetwork (cb) {
this.networkcallid++
((callid) => {
this.executionContext.detectNetwork((err, { id, name } = {}) => {
if (this.networkcallid > callid) return
this.networkcallid++
if (err) {
return cb(err)
}
cb(null, {id, name})
})
})(this.networkcallid)
}
newAccount (passphraseCb, cb) {
return this.udapp.newAccount('', passphraseCb, cb)
}
getAccounts (cb) {
return this.udapp.getAccounts(cb)
}
isWeb3Provider () {
var isVM = this.executionContext.isVM()
var isInjected = this.executionContext.getProvider() === 'injected'
return (!isVM && !isInjected)
}
isInjectedWeb3 () {
return this.executionContext.getProvider() === 'injected'
}
signMessage (message, account, passphrase, cb) {
var isVM = this.executionContext.isVM()
var isInjected = this.executionContext.getProvider() === 'injected'
if (isVM) {
const personalMsg = ethJSUtil.hashPersonalMessage(Buffer.from(message))
var privKey = this.udapp.accounts[account].privateKey
try {
var rsv = ethJSUtil.ecsign(personalMsg, privKey)
var signedData = ethJSUtil.toRpcSig(rsv.v, rsv.r, rsv.s)
cb(null, '0x' + personalMsg.toString('hex'), signedData)
} catch (e) {
cb(e.message)
}
return
}
if (isInjected) {
const hashedMsg = Web3.utils.sha3(message)
try {
addTooltip('Please check your provider to approve')
this.executionContext.web3().eth.sign(account, hashedMsg, (error, signedData) => {
cb(error.message, hashedMsg, signedData)
})
} catch (e) {
cb(e.message)
}
return
}
const hashedMsg = Web3.utils.sha3(message)
try {
var personal = new Personal(this.executionContext.web3().currentProvider)
personal.sign(hashedMsg, account, passphrase, (error, signedData) => {
cb(error.message, hashedMsg, signedData)
})
} catch (e) {
cb(e.message)
}
}
}
module.exports = Settings

@ -10,9 +10,11 @@ var confirmDialog = require('../../ui/confirmDialog')
class RecorderUI {
constructor (recorder, logCallBack) {
constructor (blockchain, recorder, logCallBack, config) {
this.blockchain = blockchain
this.recorder = recorder
this.logCallBack = logCallBack
this.config = config
this.event = new EventManager()
}
@ -63,8 +65,10 @@ class RecorderUI {
modalDialogCustom.alert(msg)
}
const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog)
// TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily
this.recorder.runScenario(continueCb, promptCb, alertCb, confirmDialog, modalDialog, this.logCallBack, (error, abi, address, contractName) => {
this.recorder.runScenario(continueCb, promptCb, alertCb, confirmationCb, this.logCallBack, (error, abi, address, contractName) => {
if (error) {
return modalDialogCustom.alert(error)
}
@ -73,6 +77,37 @@ class RecorderUI {
})
}
getConfirmationCb (modalDialog, confirmDialog) {
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, amount, gasEstimation, null, this.blockchain.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.blockchain.toWei(content.querySelector('#gasprice').value, 'gwei')
continueTxExecution(gasPrice)
}
}}, {
label: 'Cancel',
fn: () => {
return cancelCb('Transaction canceled by user.')
}
}
)
}
return confirmationCb
}
triggerRecordButton () {
this.recorder.saveScenario(
(path, cb) => {

@ -11,12 +11,12 @@ const globalRegistry = require('../../../global/registry')
class SettingsUI {
constructor (settings, networkModule) {
this.settings = settings
constructor (blockchain, networkModule) {
this.blockchain = blockchain
this.event = new EventManager()
this._components = {}
this.settings.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
this.blockchain.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
if (error) return
if (!lookupOnly) this.el.querySelector('#value').value = '0'
this.updateAccountBalances()
@ -44,7 +44,7 @@ class SettingsUI {
if (!this.el) return
var accounts = $(this.el.querySelector('#txorigin')).children('option')
accounts.each((index, account) => {
this.settings.getAccountBalanceForAddress(account.value, (err, balance) => {
this.blockchain.getAccountBalanceForAddress(account.value, (err, balance) => {
if (err) return
account.innerText = helper.shortenAddress(account.value, balance)
})
@ -139,7 +139,7 @@ class SettingsUI {
var selectExEnv = environmentEl.querySelector('#selectExEnvOptions')
this.setDropdown(selectExEnv)
this.settings.event.register('contextChanged', (context, silent) => {
this.blockchain.event.register('contextChanged', (context, silent) => {
this.setFinalContext()
})
@ -156,7 +156,7 @@ class SettingsUI {
setDropdown (selectExEnv) {
this.selectExEnv = selectExEnv
this.settings.event.register('addProvider', (network) => {
this.blockchain.event.register('addProvider', (network) => {
selectExEnv.appendChild(yo`<option
title="Manually added environment: ${network.url}"
value="${network.name}"
@ -167,7 +167,7 @@ class SettingsUI {
addTooltip(`${network.name} [${network.url}] added`)
})
this.settings.event.register('removeProvider', (name) => {
this.blockchain.event.register('removeProvider', (name) => {
var env = selectExEnv.querySelector(`option[value="${name}"]`)
if (env) {
selectExEnv.removeChild(env)
@ -177,9 +177,9 @@ class SettingsUI {
selectExEnv.addEventListener('change', (event) => {
let context = selectExEnv.options[selectExEnv.selectedIndex].value
this.settings.changeExecutionContext(context, () => {
this.blockchain.changeExecutionContext(context, () => {
modalDialogCustom.prompt('External node request', this.web3ProviderDialogBody(), 'http://localhost:8545', (target) => {
this.settings.setProviderFromEndpoint(target, context, (alertMsg) => {
this.blockchain.setProviderFromEndpoint(target, context, (alertMsg) => {
if (alertMsg) addTooltip(alertMsg)
this.setFinalContext()
})
@ -189,7 +189,7 @@ class SettingsUI {
}, this.setFinalContext.bind(this))
})
selectExEnv.value = this.settings.getProvider()
selectExEnv.value = this.blockchain.getProvider()
}
web3ProviderDialogBody () {
@ -208,7 +208,7 @@ class SettingsUI {
setFinalContext () {
// set the final context. Cause it is possible that this is not the one we've originaly selected
this.selectExEnv.value = this.settings.getProvider()
this.selectExEnv.value = this.blockchain.getProvider()
this.event.trigger('clearInstance', [])
this.updateNetwork()
this.updatePlusButton()
@ -250,7 +250,7 @@ class SettingsUI {
}
newAccount () {
this.settings.newAccount(
this.blockchain.newAccount(
(cb) => {
modalDialogCustom.promptPassphraseCreation((error, passphrase) => {
if (error) {
@ -269,14 +269,14 @@ class SettingsUI {
}
signMessage () {
this.settings.getAccounts((err, accounts) => {
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.settings.isInjectedWeb3() || this.settings.isWeb3Provider())) {
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`)
}
@ -284,7 +284,7 @@ class SettingsUI {
var promptCb = (passphrase) => {
const modal = modalDialogCustom.promptMulti(signMessageDialog, (message) => {
this.settings.signMessage(message, account, passphrase, (err, msgHash, signedData) => {
this.blockchain.signMessage(message, account, passphrase, (err, msgHash, signedData) => {
if (err) {
return addTooltip(err)
}
@ -301,7 +301,7 @@ class SettingsUI {
}, false)
}
if (this.settings.isWeb3Provider()) {
if (this.blockchain.isWeb3Provider()) {
return modalDialogCustom.promptPassphrase(
'Passphrase to sign a message',
'Enter your passphrase for this account to sign the message',
@ -315,7 +315,7 @@ class SettingsUI {
}
updateNetwork () {
this.settings.updateNetwork((err, {id, name} = {}) => {
this.blockchain.updateNetwork((err, {id, name} = {}) => {
if (err) {
this.netUI.innerHTML = 'can\'t detect network '
return
@ -331,7 +331,7 @@ class SettingsUI {
this.accountListCallId++
var callid = this.accountListCallId
var txOrigin = this.el.querySelector('#txorigin')
this.settings.getAccounts((err, accounts) => {
this.blockchain.getAccounts((err, accounts) => {
if (this.accountListCallId > callid) return
this.accountListCallId++
if (err) { addTooltip(`Cannot get account list: ${err}`) }

@ -1,22 +1,35 @@
var registry = require('../../global/registry')
var remixLib = require('remix-lib')
var yo = require('yo-yo')
var Txlistener = remixLib.execution.txListener
var EventsDecoder = remixLib.execution.EventsDecoder
var TransactionReceiptResolver = require('../../lib/transactionReceiptResolver')
export function makeUdapp (udapp, executionContext, compilersArtefacts, logHtmlCallback) {
const transactionDetailsLinks = {
'Main': 'https://www.etherscan.io/tx/',
'Rinkeby': 'https://rinkeby.etherscan.io/tx/',
'Ropsten': 'https://ropsten.etherscan.io/tx/',
'Kovan': 'https://kovan.etherscan.io/tx/',
'Goerli': 'https://goerli.etherscan.io/tx/'
}
function txDetailsLink (network, hash) {
if (transactionDetailsLinks[network]) {
return transactionDetailsLinks[network] + hash
}
}
export function makeUdapp (blockchain, compilersArtefacts, logHtmlCallback) {
// ----------------- UniversalDApp -----------------
// TODO: to remove when possible
udapp.event.register('transactionBroadcasted', (txhash, networkName) => {
var txLink = executionContext.txDetailsLink(networkName, txhash)
blockchain.event.register('transactionBroadcasted', (txhash, networkName) => {
var txLink = txDetailsLink(networkName, txhash)
if (txLink && logHtmlCallback) logHtmlCallback(yo`<a href="${txLink}" target="_blank">${txLink}</a>`)
})
// ----------------- Tx listener -----------------
const transactionReceiptResolver = new TransactionReceiptResolver(executionContext)
const transactionReceiptResolver = new TransactionReceiptResolver(blockchain)
const txlistener = new Txlistener({
const txlistener = blockchain.getTxListener({
api: {
contracts: function () {
if (compilersArtefacts['__last']) return compilersArtefacts['__last'].getContracts()
@ -25,12 +38,11 @@ export function makeUdapp (udapp, executionContext, compilersArtefacts, logHtmlC
resolveReceipt: function (tx, cb) {
transactionReceiptResolver.resolve(tx, cb)
}
},
event: {
udapp: udapp.event
}}, executionContext)
}
})
registry.put({api: txlistener, name: 'txlistener'})
udapp.startListening(txlistener)
blockchain.startListening(txlistener)
const eventsDecoder = new EventsDecoder({
api: {
@ -41,5 +53,5 @@ export function makeUdapp (udapp, executionContext, compilersArtefacts, logHtmlC
})
txlistener.startListening()
return {udapp, txlistener, eventsDecoder}
return {txlistener, eventsDecoder}
}

@ -9,13 +9,11 @@ const EventManager = require('../../lib/events')
const Card = require('../ui/card')
const css = require('../tabs/styles/run-tab-styles')
const Settings = require('../tabs/runTab/model/settings.js')
const SettingsUI = require('../tabs/runTab/settings.js')
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')
@ -35,12 +33,13 @@ const profile = {
export class RunTab extends LibraryPlugin {
constructor (udapp, executionContext, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, mainView) {
constructor (blockchain, 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.blockchain = blockchain
this.fileManager = fileManager
this.editor = editor
this.logCallback = (msg) => { mainView.getTerminal().logHtml(msg) }
@ -122,19 +121,17 @@ export class RunTab extends LibraryPlugin {
this.instanceContainer.appendChild(this.noInstancesText)
}
renderSettings (udapp) {
var settings = new Settings(this.executionContext, udapp)
this.settingsUI = new SettingsUI(settings, this.networkModule)
renderSettings () {
this.settingsUI = new SettingsUI(this.blockchain, this.networkModule)
this.settingsUI.event.register('clearInstance', () => {
this.event.trigger('clearInstance', [])
})
}
renderDropdown (udappUI, fileManager, compilersArtefacts, config, editor, udapp, filePanel, logCallback) {
renderDropdown (udappUI, fileManager, compilersArtefacts, config, editor, logCallback) {
const dropdownLogic = new DropdownLogic(compilersArtefacts, config, editor, this)
const blockchain = new Blockchain(this.executionContext, udapp)
this.contractDropdownUI = new ContractDropdownUI(blockchain, dropdownLogic, logCallback, this)
this.contractDropdownUI = new ContractDropdownUI(this.blockchain, dropdownLogic, logCallback, this)
fileManager.events.on('currentFileChanged', this.contractDropdownUI.changeCurrentFile.bind(this.contractDropdownUI))
@ -150,16 +147,16 @@ export class RunTab extends LibraryPlugin {
})
}
renderRecorder (udapp, udappUI, fileManager, config, logCallback) {
renderRecorder (udappUI, fileManager, config, logCallback) {
this.recorderCount = yo`<span>0</span>`
const recorder = new Recorder(this.executionContext, udapp, fileManager, config)
const recorder = new Recorder(this.blockchain, fileManager, config)
recorder.event.register('recorderCountChange', (count) => {
this.recorderCount.innerText = count
})
this.event.register('clearInstance', recorder.clearAll.bind(recorder))
this.recorderInterface = new RecorderUI(recorder, logCallback)
this.recorderInterface = new RecorderUI(this.blockchain, recorder, logCallback, config)
this.recorderInterface.event.register('newScenario', (abi, address, contractName) => {
var noInstancesText = this.noInstancesText
@ -209,9 +206,9 @@ export class RunTab extends LibraryPlugin {
this.executionContext.listenOnLastBlock()
this.udapp.resetEnvironment()
this.renderInstanceContainer()
this.renderSettings(this.udapp)
this.renderDropdown(this.udappUI, this.fileManager, this.compilersArtefacts, this.config, this.editor, this.udapp, this.filePanel, this.logCallback)
this.renderRecorder(this.udapp, this.udappUI, this.fileManager, this.config, this.logCallback)
this.renderSettings()
this.renderDropdown(this.udappUI, this.fileManager, this.compilersArtefacts, this.config, this.editor, this.logCallback)
this.renderRecorder(this.udappUI, this.fileManager, this.config, this.logCallback)
this.renderRecorderCard()
return this.renderContainer()
}

@ -116,7 +116,7 @@ var css = csjs`
*
*/
class TxLogger {
constructor (eventsDecoder, txListener, terminal, executionContext) {
constructor (eventsDecoder, txListener, terminal, blockchain) {
this.event = new EventManager()
this.seen = {}
function filterTx (value, query) {
@ -139,7 +139,7 @@ class TxLogger {
if (data.tx.isCall) {
el = renderCall(this, data)
} else {
el = renderKnownTransaction(this, data, executionContext)
el = renderKnownTransaction(this, data, blockchain)
}
this.seen[data.tx.hash] = el
append(el)
@ -148,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, executionContext)
var el = renderUnknownTransaction(this, data, blockchain)
append(el)
}, { activate: false, filterFn: filterTx })
@ -204,7 +204,7 @@ function log (self, tx, receipt) {
}
}
function renderKnownTransaction (self, data, executionContext) {
function renderKnownTransaction (self, data, blockchain) {
var from = data.tx.from
var to = data.resolvedData.contractName + '.' + data.resolvedData.fn
var obj = {from, to}
@ -213,7 +213,7 @@ function renderKnownTransaction (self, data, executionContext) {
<span id="tx${data.tx.hash}">
<div class="${css.log}" onclick=${e => txDetails(e, tx, data, obj)}>
${checkTxStatus(data.receipt, txType)}
${context(self, {from, to, data}, executionContext)}
${context(self, {from, to, data}, blockchain)}
<div class=${css.buttons}>
<button class="${css.debug} btn btn-primary btn-sm" onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
@ -250,7 +250,7 @@ function renderCall (self, data) {
return tx
}
function renderUnknownTransaction (self, data, executionContext) {
function renderUnknownTransaction (self, data, blockchain) {
var from = data.tx.from
var to = data.tx.to
var obj = {from, to}
@ -259,7 +259,7 @@ function renderUnknownTransaction (self, data, executionContext) {
<span id="tx${data.tx.hash}">
<div class="${css.log}" onclick=${e => txDetails(e, tx, data, obj)}>
${checkTxStatus(data.receipt || data.tx, txType)}
${context(self, {from, to, data}, executionContext)}
${context(self, {from, to, data}, blockchain)}
<div class=${css.buttons}>
<div class="${css.debug} btn btn-primary btn-sm" onclick=${(e) => debug(e, data, self)}>Debug</div>
</div>
@ -290,7 +290,7 @@ function checkTxStatus (tx, type) {
}
}
function context (self, opts, executionContext) {
function context (self, opts, blockchain) {
var data = opts.data || ''
var from = opts.from ? helper.shortenHexData(opts.from) : ''
var to = opts.to
@ -302,7 +302,7 @@ function context (self, opts, executionContext) {
var block = data.tx.blockNumber || ''
var i = data.tx.transactionIndex
var value = val ? typeConversion.toInt(val) : 0
if (executionContext.getProvider() === 'vm') {
if (blockchain.getProvider() === 'vm') {
return yo`
<div>
<span class=${css.txLog}>
@ -315,7 +315,7 @@ function context (self, opts, executionContext) {
<div class=${css.txItem}><span class=${css.txItemTitle}>hash:</span> ${hash}</div>
</span>
</div>`
} else if (executionContext.getProvider() !== 'vm' && data.resolvedData) {
} else if (blockchain.getProvider() !== 'vm' && data.resolvedData) {
return yo`
<div>
<span class=${css.txLog}>

@ -14,10 +14,10 @@ var solidityTypeFormatter = require('../app/tabs/debugger/debuggerUI/vmDebugger/
var GistHandler = require('./gist-handler')
class CmdInterpreterAPI {
constructor (terminal, localRegistry, executionContext) {
constructor (terminal, localRegistry, blockchain) {
const self = this
self.event = new EventManager()
self.executionContext = executionContext
self.blockchain = blockchain
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
self.executionContext.web3().eth.getTransaction(hash, (error, tx) => {
self.blockchain.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', self.executionContext.web3())
debugSession.addProvider('web3', self.blockchain.web3())
debugSession.switchProvider('web3')
debugSession.debug(tx)
self.d = debugSession
@ -180,7 +180,7 @@ class CmdInterpreterAPI {
})
}
setproviderurl (url, cb) {
this.executionContext.setProviderFromEndpoint(url, 'web3', (error) => {
this.blockchain.setProviderFromEndpoint(url, 'web3', (error) => {
if (error) toolTip(error)
if (cb) cb()
})

@ -1,16 +1,16 @@
'use strict'
module.exports = class TransactionReceiptResolver {
constructor (executionContext) {
constructor (blockchain) {
this._transactionReceipts = {}
this.executionContext = executionContext
this.blockchain = blockchain
}
resolve (tx, cb) {
if (this._transactionReceipts[tx.hash]) {
return cb(null, this._transactionReceipts[tx.hash])
}
this.executionContext.web3().eth.getTransactionReceipt(tx.hash, (error, receipt) => {
this.blockchain.web3().eth.getTransactionReceipt(tx.hash, (error, receipt) => {
if (!error) {
this._transactionReceipts[tx.hash] = receipt
cb(null, receipt)

Loading…
Cancel
Save