Refactor runTab actions

pull/5370/head
David Disu 3 years ago
parent 3c2655ba53
commit 8b29483f4d
  1. 18
      libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
  2. 135
      libs/remix-ui/run-tab/src/lib/actions/account.ts
  3. 96
      libs/remix-ui/run-tab/src/lib/actions/actions.ts
  4. 301
      libs/remix-ui/run-tab/src/lib/actions/deploy.ts
  5. 143
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  6. 754
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  7. 9
      libs/remix-ui/run-tab/src/lib/actions/payload.ts
  8. 72
      libs/remix-ui/run-tab/src/lib/actions/recorder.ts
  9. 1
      libs/remix-ui/run-tab/src/lib/constants/index.ts
  10. 16
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  11. 95
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  12. 18
      libs/remix-ui/run-tab/src/lib/types/index.ts

@ -107,16 +107,24 @@ export class OpenZeppelinProxy extends Plugin {
this.blockchain = blockchain
}
async isConcerned(ast: ContractAST, contracts: ContractABI): Promise<boolean> {
async isConcerned(ast: ContractAST, contracts: ContractABI) {
// check in the AST if it's an upgradable contract
if (ast.nodes.find(node => node.absolutePath === UUPS)) {
this.kind = 'UUPS'
Object.keys(contracts).map(name => {
const inputs = Object.keys(contracts).map(name => {
const abi = contracts[name].abi
const initializeInput = abi.find(node => node.name === 'initialize')
if (initializeInput) {
return {
[name]: initializeInput
}
}
})
return true
if (inputs.length > 0) {
this.kind = 'UUPS'
return { inputs }
}
}
return false
}
async execute(implAddress: string, _data: string = '') {

@ -0,0 +1,135 @@
import { shortenAddress, web3Dialog } from "@remix-ui/helper"
import { RunTab } from "../types/run-tab"
import { clearInstances, setAccount, setExecEnv } from "./actions"
import { displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, setExternalEndpoint, setMatchPassphrase, setPassphrase } from "./payload"
export const updateAccountBalances = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
const accounts = plugin.REACT_API.accounts.loadedAccounts
Object.keys(accounts).map((value) => {
plugin.blockchain.getBalanceInEther(value, (err, balance) => {
if (err) return
const updated = shortenAddress(value, balance)
accounts[value] = updated
})
})
dispatch(fetchAccountsListSuccess(accounts))
}
export const fillAccountsList = async (plugin: RunTab, dispatch: React.Dispatch<any>) => {
try {
dispatch(fetchAccountsListRequest())
const promise = plugin.blockchain.getAccounts()
promise.then(async (accounts: string[]) => {
const loadedAccounts = {}
if (!accounts) accounts = []
// allSettled is undefined..
// so the current promise (all) will finish when:
// - all the promises resolve
// - at least one reject
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
await (Promise as any).all(accounts.map((account) => {
return new Promise((resolve, reject) => {
plugin.blockchain.getBalanceInEther(account, (err, balance) => {
if (err) return reject(err)
const updated = shortenAddress(account, balance)
loadedAccounts[account] = updated
resolve(account)
})
})
}))
const provider = plugin.blockchain.getProvider()
if (provider === 'injected') {
const selectedAddress = plugin.blockchain.getInjectedWeb3Address()
if (!(Object.keys(loadedAccounts).includes(selectedAddress))) setAccount(dispatch, null)
}
dispatch(fetchAccountsListSuccess(loadedAccounts))
}).catch((e) => {
dispatch(fetchAccountsListFailed(e.message))
})
} catch (e) {
dispatch(displayPopUp(`Cannot get account list: ${e}`))
}
}
export const setFinalContext = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
// set the final context. Cause it is possible that this is not the one we've originaly selected
const value = _getProviderDropdownValue(plugin)
setExecEnv(dispatch, value)
clearInstances(dispatch)
}
const _getProviderDropdownValue = (plugin: RunTab): string => {
const provider = plugin.blockchain.getProvider()
const fork = plugin.blockchain.getCurrentFork()
return provider === 'vm' ? provider + '-' + fork : provider
}
export const setExecutionContext = (plugin: RunTab, dispatch: React.Dispatch<any>, executionContext: { context: string, fork: string }) => {
const displayContent = web3Dialog(plugin.REACT_API.externalEndpoint, (endpoint: string) => {
dispatch(setExternalEndpoint(endpoint))
})
plugin.blockchain.changeExecutionContext(executionContext, () => {
plugin.call('notification', 'modal', {
id: 'envNotification',
title: 'External node request',
message: displayContent,
okLabel: 'OK',
cancelLabel: 'Cancel',
okFn: () => {
plugin.blockchain.setProviderFromEndpoint(plugin.REACT_API.externalEndpoint, executionContext, (alertMsg) => {
if (alertMsg) plugin.call('notification', 'toast', alertMsg)
setFinalContext(plugin, dispatch)
})
},
cancelFn: () => {
setFinalContext(plugin, dispatch)
}
})
}, (alertMsg) => {
plugin.call('notification', 'toast', alertMsg)
}, () => { setFinalContext(plugin, dispatch) })
}
export const createNewBlockchainAccount = async (plugin: RunTab, dispatch: React.Dispatch<any>, cbMessage: JSX.Element) => {
plugin.blockchain.newAccount(
'',
(cb) => {
dispatch(displayNotification('Enter Passphrase', cbMessage, 'OK', 'Cancel', async () => {
if (plugin.REACT_API.passphrase === plugin.REACT_API.matchPassphrase) {
cb(plugin.REACT_API.passphrase)
} else {
dispatch(displayNotification('Error', 'Passphase does not match', 'OK', null))
}
setPassphrase('')
setMatchPassphrase('')
}, () => {}))
},
async (error, address) => {
if (error) {
return dispatch(displayPopUp('Cannot create an account: ' + error))
}
dispatch(displayPopUp(`account ${address} created`))
await fillAccountsList(plugin, dispatch)
}
)
}
export const signMessageWithAddress = (plugin: RunTab, dispatch: React.Dispatch<any>, account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => {
plugin.blockchain.signMessage(message, account, passphrase, (err, msgHash, signedData) => {
if (err) {
return displayPopUp(err)
}
dispatch(displayNotification('Signed Message', modalContent(msgHash, signedData), 'OK', null, () => {}, null))
})
}

@ -0,0 +1,96 @@
import { ContractData } from "@remix-project/core-plugin"
import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, hidePopUp, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentContract, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent } from "./payload"
export const setAccount = (dispatch: React.Dispatch<any>, account: string) => {
dispatch(setSelectedAccount(account))
}
export const setUnit = (dispatch: React.Dispatch<any>, unit: 'ether' | 'finney' | 'gwei' | 'wei') => {
dispatch(setSendUnit(unit))
}
export const setGasFee = (dispatch: React.Dispatch<any>, value: number) => {
dispatch(setGasLimit(value))
}
export const setExecEnv = (dispatch: React.Dispatch<any>, env: string) => {
dispatch(setExecutionEnvironment(env))
}
export const setNetworkNameFromProvider = (dispatch: React.Dispatch<any>, networkName: string) => {
dispatch(setNetworkName(networkName))
}
export const addExternalProvider = (dispatch: React.Dispatch<any>, network) => {
dispatch(addProvider(network))
}
export const removeExternalProvider = (dispatch: React.Dispatch<any>, name) => {
dispatch(removeProvider(name))
}
export const clearPopUp = async (dispatch: React.Dispatch<any>) => {
dispatch(hidePopUp())
}
export const setPassphrasePrompt = (dispatch: React.Dispatch<any>, passphrase: string) => {
dispatch(setPassphrase(passphrase))
}
export const setMatchPassphrasePrompt = (dispatch: React.Dispatch<any>, passphrase: string) => {
dispatch(setMatchPassphrase(passphrase))
}
export const updateGasPriceStatus = (dispatch: React.Dispatch<any>, status: boolean) => {
dispatch(setGasPriceStatus(status))
}
export const updateConfirmSettings = (dispatch: React.Dispatch<any>, confirmation: boolean) => {
dispatch(setConfirmSettings(confirmation))
}
export const updateMaxFee = (dispatch: React.Dispatch<any>, fee: string) => {
dispatch(setMaxFee(fee))
}
export const updateMaxPriorityFee = (dispatch: React.Dispatch<any>, fee: string) => {
dispatch(setMaxPriorityFee(fee))
}
export const updateBaseFeePerGas = (dispatch: React.Dispatch<any>, baseFee: string) => {
dispatch(setBaseFeePerGas(baseFee))
}
export const updateGasPrice = (dispatch: React.Dispatch<any>, price: string) => {
dispatch(setGasPrice(price))
}
export const updateTxFeeContent = (dispatch: React.Dispatch<any>, content: string) => {
dispatch(setTxFeeContent(content))
}
export const addInstance = (dispatch: React.Dispatch<any>, instance: { contractData?: ContractData, address: string, name: string, abi?: any, decodedResponse?: Record<number, any> }) => {
instance.decodedResponse = {}
dispatch(addNewInstance(instance))
}
export const removeInstance = (dispatch: React.Dispatch<any>, index: number) => {
dispatch(removeExistingInstance(index))
}
export const clearInstances = (dispatch: React.Dispatch<any>) => {
dispatch(clearAllInstances())
dispatch(clearRecorderCount())
}
export const setSelectedContract = (dispatch: React.Dispatch<any>, contractName: string) => {
dispatch(setCurrentContract(contractName))
}
export const updateScenarioPath = (dispatch: React.Dispatch<any>, path: string) => {
dispatch(setPathToScenario(path))
}
export const setSendTransactionValue = (dispatch: React.Dispatch<any>, value: string) => {
dispatch(setSendValue(value))
}

@ -0,0 +1,301 @@
import { ContractData, FuncABI } from "@remix-project/core-plugin"
import { RunTab } from "../types/run-tab"
import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts'
import * as remixLib from '@remix-project/remix-lib'
import { DeployMode, MainnetPrompt } from "../types"
import { displayNotification, displayPopUp, setDecodedResponse } from "./payload"
import { addInstance } from "./actions"
import { addressToString } from "@remix-ui/helper"
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || [] //eslint-disable-line
const txHelper = remixLib.execution.txHelper
const txFormat = remixLib.execution.txFormat
const loadContractFromAddress = (plugin: RunTab, address, confirmCb, cb) => {
if (/.(.abi)$/.exec(plugin.config.get('currentFile'))) {
confirmCb(() => {
let abi
try {
abi = JSON.parse(plugin.editor.currentContent())
} catch (e) {
return cb('Failed to parse the current file as JSON ABI.')
}
_paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithABI'])
cb(null, 'abi', abi)
})
} else {
_paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithArtifacts'])
cb(null, 'instance')
}
}
export const getSelectedContract = (contractName: string, compiler: CompilerAbstractType): ContractData => {
if (!contractName) return null
// const compiler = plugin.compilersArtefacts[compilerAtributeName]
if (!compiler) return null
const contract = compiler.getContract(contractName)
return {
name: contractName,
contract: contract,
compiler: compiler,
abi: contract.object.abi,
bytecodeObject: contract.object.evm.bytecode.object,
bytecodeLinkReferences: contract.object.evm.bytecode.linkReferences,
object: contract.object,
deployedBytecode: contract.object.evm.deployedBytecode,
getConstructorInterface: () => {
return txHelper.getConstructorInterface(contract.object.abi)
},
getConstructorInputs: () => {
const constructorInteface = txHelper.getConstructorInterface(contract.object.abi)
return txHelper.inputParametersDeclarationToString(constructorInteface.inputs)
},
isOverSizeLimit: () => {
const deployedBytecode = contract.object.evm.deployedBytecode
return (deployedBytecode && deployedBytecode.object.length / 2 > 24576)
},
metadata: contract.object.metadata
}
}
const getCompilerContracts = (plugin: RunTab) => {
return plugin.compilersArtefacts.__last.getData().contracts
}
export const terminalLogger = (plugin: RunTab, view: JSX.Element) => {
plugin.call('terminal', 'logHtml', view)
}
export const confirmationHandler = (plugin: RunTab, dispatch: React.Dispatch<any>, confirmDialogContent: MainnetPrompt, network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
const amount = plugin.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialogContent(tx, network, amount, gasEstimation, plugin.blockchain.determineGasFees(tx), plugin.blockchain.determineGasPrice.bind(plugin.blockchain))
dispatch(displayNotification('Confirm transaction', content, 'Confirm', 'Cancel', () => {
plugin.blockchain.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', plugin.REACT_API.confirmSettings)
// TODO: check if this is check is still valid given the refactor
if (!plugin.REACT_API.gasPriceStatus) {
cancelCb('Given transaction fee is not correct')
} else {
continueTxExecution({ maxFee: plugin.REACT_API.maxFee, maxPriorityFee: plugin.REACT_API.maxPriorityFee, baseFeePerGas: plugin.REACT_API.baseFeePerGas, gasPrice: plugin.REACT_API.gasPrice })
}
}, () => {
return cancelCb('Transaction canceled by user.')
}))
}
const getConfirmationCb = (plugin: RunTab, dispatch: React.Dispatch<any>, confirmDialogContent: MainnetPrompt) => {
// this code is the same as in recorder.js. TODO need to be refactored out
return (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(plugin, dispatch, confirmDialogContent, network, tx, gasEstimation, continueTxExecution, cancelCb)
}
}
export const continueHandler = (dispatch: React.Dispatch<any>, gasEstimationPrompt: (msg: string) => JSX.Element, error, continueTxExecution, cancelCb) => {
if (error) {
const msg = typeof error !== 'string' ? error.message : error
dispatch(displayNotification('Gas estimation failed', gasEstimationPrompt(msg), 'Send Transaction', 'Cancel Transaction', () => {
continueTxExecution()
}, () => {
cancelCb()
}))
} else {
continueTxExecution()
}
}
export const promptHandler = (dispatch: React.Dispatch<any>, passphrasePrompt, okCb, cancelCb) => {
dispatch(displayNotification('Passphrase requested', passphrasePrompt('Personal mode is enabled. Please provide passphrase of account'), 'OK', 'Cancel', okCb, cancelCb))
}
export const createInstance = async (
plugin: RunTab,
dispatch: React.Dispatch<any>,
selectedContract: ContractData,
gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (msg: string) => JSX.Element,
logBuilder: (msg: string) => JSX.Element,
publishToStorage: (storage: 'ipfs' | 'swarm',
contract: ContractData) => void,
mainnetPrompt: MainnetPrompt,
isOverSizePrompt: () => JSX.Element,
args,
deployMode: DeployMode[]) => {
const statusCb = (msg: string) => {
const log = logBuilder(msg)
return terminalLogger(plugin, log)
}
const finalCb = (error, contractObject, address) => {
if (error) {
const log = logBuilder(error)
return terminalLogger(plugin, log)
}
addInstance(dispatch, { contractData: contractObject, address, name: contractObject.name })
const data = plugin.compilersArtefacts.getCompilerAbstract(contractObject.contract.file)
plugin.compilersArtefacts.addResolvedContract(addressToString(address), data)
if (plugin.REACT_API.ipfsChecked) {
_paq.push(['trackEvent', 'udapp', 'DeployAndPublish', plugin.REACT_API.networkName])
publishToStorage('ipfs', selectedContract)
} else {
_paq.push(['trackEvent', 'udapp', 'DeployOnly', plugin.REACT_API.networkName])
}
deployMode.forEach(async (mode) => {
const owner = plugin.REACT_API.accounts.selectedAccount
if (mode === 'Deploy with Proxy') await plugin.call('openzeppelin-proxy', 'execute', address, owner)
})
}
let contractMetadata
try {
contractMetadata = await plugin.call('compilerMetadata', 'deployMetadataOf', selectedContract.name, selectedContract.contract.file)
} catch (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
}
const compilerContracts = getCompilerContracts(plugin)
const confirmationCb = getConfirmationCb(plugin, dispatch, mainnetPrompt)
if (selectedContract.isOverSizeLimit()) {
return dispatch(displayNotification('Contract code size over limit', isOverSizePrompt(), 'Force Send', 'Cancel', () => {
deployContract(plugin, selectedContract, args, contractMetadata, compilerContracts, {
continueCb: (error, continueTxExecution, cancelCb) => {
continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb)
},
promptCb: (okCb, cancelCb) => {
promptHandler(dispatch, passphrasePrompt, okCb, cancelCb)
},
statusCb,
finalCb
}, confirmationCb)
}, () => {
const log = logBuilder(`creation of ${selectedContract.name} canceled by user.`)
return terminalLogger(plugin, log)
}))
}
deployContract(plugin, selectedContract, args, contractMetadata, compilerContracts, {
continueCb: (error, continueTxExecution, cancelCb) => {
continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb)
},
promptCb: (okCb, cancelCb) => {
promptHandler(dispatch, passphrasePrompt, okCb, cancelCb)
},
statusCb,
finalCb
}, confirmationCb)
}
const deployContract = (plugin: RunTab, selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) => {
_paq.push(['trackEvent', 'udapp', 'DeployContractTo', plugin.REACT_API.networkName])
const { statusCb } = callbacks
if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) {
return plugin.blockchain.deployContractAndLibraries(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb)
}
if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`)
plugin.blockchain.deployContractWithLibrary(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb)
}
export const loadAddress = (plugin: RunTab, dispatch: React.Dispatch<any>, contract: ContractData, address: string) => {
loadContractFromAddress(plugin, address,
(cb) => {
dispatch(displayNotification('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, 'OK', 'Cancel', cb, null))
},
(error, loadType, abi) => {
if (error) {
return dispatch(displayNotification('Alert', error, 'OK', null))
}
if (loadType === 'abi') {
return addInstance(dispatch, { abi, address, name: '<at address>' })
} else if (loadType === 'instance') {
if (!contract) return dispatch(displayPopUp('No compiled contracts found.'))
const currentFile = plugin.REACT_API.contracts.currentFile
const compiler = plugin.REACT_API.contracts.contractList[currentFile].find(item => item.alias === contract.name)
const contractData = getSelectedContract(contract.name, compiler.compiler)
return addInstance(dispatch, { contractData, address, name: contract.name })
}
}
)
}
export const getContext = (plugin: RunTab) => {
return plugin.blockchain.context()
}
export const runTransactions = (
plugin: RunTab,
dispatch: React.Dispatch<any>,
instanceIndex: number,
lookupOnly: boolean,
funcABI: FuncABI,
inputsValues: string,
contractName: string,
contractABI, contract,
address,
logMsg:string,
logBuilder: (msg: string) => JSX.Element,
mainnetPrompt: MainnetPrompt,
gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (msg: string) => JSX.Element,
funcIndex?: number) => {
let callinfo = ''
if (lookupOnly) callinfo = 'call'
else if (funcABI.type === 'fallback' || funcABI.type === 'receive') callinfo = 'lowLevelInteracions'
else callinfo = 'transact'
_paq.push(['trackEvent', 'udapp', callinfo, plugin.blockchain.getCurrentNetworkStatus().network.name])
const params = funcABI.type !== 'fallback' ? inputsValues : ''
plugin.blockchain.runOrCallContractMethod(
contractName,
contractABI,
funcABI,
contract,
inputsValues,
address,
params,
lookupOnly,
logMsg,
(msg) => {
const log = logBuilder(msg)
return terminalLogger(plugin, log)
},
(returnValue) => {
const response = txFormat.decodeResponse(returnValue, funcABI)
dispatch(setDecodedResponse(instanceIndex, response, funcIndex))
},
(network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(plugin, dispatch, mainnetPrompt, network, tx, gasEstimation, continueTxExecution, cancelCb)
},
(error, continueTxExecution, cancelCb) => {
continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb)
},
(okCb, cancelCb) => {
promptHandler(dispatch, passphrasePrompt, okCb, cancelCb)
}
)
}
export const getFuncABIInputs = (plugin: RunTab, funcABI: FuncABI) => {
return plugin.blockchain.getInputs(funcABI)
}

@ -0,0 +1,143 @@
import { envChangeNotification } from "@remix-ui/helper"
import { RunTab } from "../types/run-tab"
import { setExecutionContext, setFinalContext, updateAccountBalances } from "./account"
import { addExternalProvider, addInstance, removeExternalProvider, setNetworkNameFromProvider } from "./actions"
import { clearAllInstances, clearRecorderCount, fetchContractListSuccess, resetUdapp, setCurrentFile, setDeployOptions, setLoadType, setRecorderCount, setSendValue } from "./payload"
import { CompilerAbstract } from '@remix-project/remix-solidity'
import * as ethJSUtil from 'ethereumjs-util'
import Web3 from 'web3'
export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
plugin.blockchain.events.on('newTransaction', (tx, receipt) => {
plugin.emit('newTransaction', tx, receipt)
})
plugin.blockchain.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
if (!lookupOnly) dispatch(setSendValue('0'))
if (error) return
updateAccountBalances(plugin, dispatch)
})
plugin.blockchain.event.register('contextChanged', (context, silent) => {
setFinalContext(plugin, dispatch)
})
plugin.blockchain.event.register('networkStatus', ({ error, network }) => {
if (error) {
const netUI = 'can\'t detect network '
setNetworkNameFromProvider(dispatch, netUI)
return
}
const networkProvider = plugin.networkModule.getNetworkProvider.bind(plugin.networkModule)
const netUI = (networkProvider() !== 'vm') ? `${network.name} (${network.id || '-'}) network` : 'VM'
setNetworkNameFromProvider(dispatch, netUI)
})
plugin.blockchain.event.register('addProvider', provider => addExternalProvider(dispatch, provider))
plugin.blockchain.event.register('removeProvider', name => removeExternalProvider(dispatch, name))
plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data, input))
plugin.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('nahmii-compiler', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(plugin, dispatch, file, source, languageVersion, data))
plugin.on('udapp', 'setEnvironmentModeReducer', (env: { context: string, fork: string }, from: string) => {
plugin.call('notification', 'toast', envChangeNotification(env, from))
setExecutionContext(plugin, dispatch, env)
})
plugin.on('udapp', 'clearAllInstancesReducer', () => {
dispatch(clearAllInstances())
})
plugin.on('udapp', 'addInstanceReducer', (address, abi, name) => {
addInstance(dispatch, { abi, address, name })
})
plugin.on('filePanel', 'setWorkspace', () => {
dispatch(resetUdapp())
resetAndInit(plugin)
})
plugin.fileManager.events.on('currentFileChanged', (currentFile: string) => {
if (/.(.abi)$/.exec(currentFile)) {
dispatch(setLoadType('abi'))
} else if (/.(.sol)$/.exec(currentFile) ||
/.(.vy)$/.exec(currentFile) || // vyper
/.(.lex)$/.exec(currentFile) || // lexon
/.(.contract)$/.exec(currentFile)) {
dispatch(setLoadType('sol'))
} else {
dispatch(setLoadType('other'))
}
dispatch(setCurrentFile(currentFile))
})
plugin.recorder.event.register('recorderCountChange', (count) => {
dispatch(setRecorderCount(count))
})
plugin.event.register('cleared', () => {
dispatch(clearRecorderCount())
})
}
const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispatch<any>, file, source, languageVersion, data, input?) => {
// TODO check whether the tab is configured
const compiler = new CompilerAbstract(languageVersion, data, source, input)
plugin.compilersArtefacts[languageVersion] = compiler
plugin.compilersArtefacts.__last = compiler
const contracts = getCompiledContracts(compiler).map((contract) => {
return { name: languageVersion, alias: contract.name, file: contract.file, compiler }
})
const upgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file].ast, data.contracts[file])
if (upgradeable) dispatch(setDeployOptions([{ title: 'Deploy with Proxy', active: false, inputs: upgradeable.inputs }]))
else dispatch(setDeployOptions([]))
dispatch(fetchContractListSuccess({ [file]: contracts }))
dispatch(setCurrentFile(file))
}
const getCompiledContracts = (compiler) => {
const contracts = []
compiler.visitContracts((contract) => {
contracts.push(contract)
})
return contracts
}
export const resetAndInit = (plugin: RunTab) => {
plugin.blockchain.resetAndInit(plugin.config, {
getAddress: (cb) => {
cb(null, plugin.REACT_API.accounts.selectedAccount)
},
getValue: (cb) => {
try {
const number = plugin.REACT_API.sendValue
const unit = plugin.REACT_API.sendUnit
cb(null, Web3.utils.toWei(number, unit))
} catch (e) {
cb(e)
}
},
getGasLimit: (cb) => {
try {
cb(null, '0x' + new ethJSUtil.BN(plugin.REACT_API.gasLimit, 10).toString(16))
} catch (e) {
cb(e.message)
}
}
})
}

@ -1,17 +1,15 @@
// eslint-disable-next-line no-unused-vars
import React from 'react'
import * as ethJSUtil from 'ethereumjs-util'
import Web3 from 'web3'
import { addressToString, createNonClashingNameAsync, envChangeNotification, extractNameFromKey, shortenAddress, web3Dialog } from '@remix-ui/helper'
import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeExistingInstance, removeProvider, resetUdapp, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setDecodedResponse, setDeployOptions, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setRecorderCount, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent } from './payload'
import { RunTab } from '../types/run-tab'
import { CompilerAbstract } from '@remix-project/remix-solidity'
import * as remixLib from '@remix-project/remix-lib'
import { DeployMode, MainnetPrompt } from '../types'
import { ContractData, FuncABI, } from '@remix-project/core-plugin'
import { resetAndInit, setupEvents } from './events'
import { createNewBlockchainAccount, fillAccountsList, setExecutionContext, signMessageWithAddress } from './account'
import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, setNetworkNameFromProvider, setPassphrasePrompt, setSendTransactionValue, setUnit, updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath, updateTxFeeContent } from './actions'
import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions } from './deploy'
import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts'
import { ContractData, FuncABI } from "@remix-project/core-plugin"
import { DeployMode, MainnetPrompt } from '../types'
import { runCurrentScenario, storeScenario } from './recorder'
const txFormat = remixLib.execution.txFormat
declare global {
interface Window {
_paq: any
@ -19,718 +17,44 @@ declare global {
}
const _paq = window._paq = window._paq || [] //eslint-disable-line
const txHelper = remixLib.execution.txHelper
let plugin: RunTab, dispatch: React.Dispatch<any>
export const initRunTab = (udapp: RunTab) => async (reducerDispatch: React.Dispatch<any>) => {
plugin = udapp
dispatch = reducerDispatch
resetAndInit()
setupEvents()
resetAndInit(plugin)
setupEvents(plugin, dispatch)
setInterval(() => {
fillAccountsList()
fillAccountsList(plugin, dispatch)
}, 1000)
}
const setupEvents = () => {
plugin.blockchain.events.on('newTransaction', (tx, receipt) => {
plugin.emit('newTransaction', tx, receipt)
})
plugin.blockchain.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => {
if (!lookupOnly) dispatch(setSendValue('0'))
if (error) return
updateAccountBalances()
})
plugin.blockchain.event.register('contextChanged', (context, silent) => {
setFinalContext()
})
plugin.blockchain.event.register('networkStatus', ({ error, network }) => {
if (error) {
const netUI = 'can\'t detect network '
setNetworkNameFromProvider(netUI)
return
}
const networkProvider = plugin.networkModule.getNetworkProvider.bind(plugin.networkModule)
const netUI = (networkProvider() !== 'vm') ? `${network.name} (${network.id || '-'}) network` : 'VM'
setNetworkNameFromProvider(netUI)
})
plugin.blockchain.event.register('addProvider', provider => addExternalProvider(provider))
plugin.blockchain.event.register('removeProvider', name => removeExternalProvider(name))
plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => broadcastCompilationResult(file, source, languageVersion, data, input))
plugin.on('vyper', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
plugin.on('lexon', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
plugin.on('yulp', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
plugin.on('nahmii-compiler', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data))
plugin.on('udapp', 'setEnvironmentModeReducer', (env: { context: string, fork: string }, from: string) => {
plugin.call('notification', 'toast', envChangeNotification(env, from))
setExecutionContext(env)
})
plugin.on('udapp', 'clearAllInstancesReducer', () => {
dispatch(clearAllInstances())
})
plugin.on('udapp', 'addInstanceReducer', (address, abi, name) => {
addInstance({ abi, address, name })
})
plugin.on('filePanel', 'setWorkspace', () => {
dispatch(resetUdapp())
resetAndInit()
})
plugin.fileManager.events.on('currentFileChanged', (currentFile: string) => {
if (/.(.abi)$/.exec(currentFile)) {
dispatch(setLoadType('abi'))
} else if (/.(.sol)$/.exec(currentFile) ||
/.(.vy)$/.exec(currentFile) || // vyper
/.(.lex)$/.exec(currentFile) || // lexon
/.(.contract)$/.exec(currentFile)) {
dispatch(setLoadType('sol'))
} else {
dispatch(setLoadType('other'))
}
dispatch(setCurrentFile(currentFile))
})
plugin.recorder.event.register('recorderCountChange', (count) => {
dispatch(setRecorderCount(count))
})
plugin.event.register('cleared', () => {
dispatch(clearRecorderCount())
})
}
const updateAccountBalances = () => {
const accounts = plugin.REACT_API.accounts.loadedAccounts
Object.keys(accounts).map((value) => {
plugin.blockchain.getBalanceInEther(value, (err, balance) => {
if (err) return
const updated = shortenAddress(value, balance)
accounts[value] = updated
})
})
dispatch(fetchAccountsListSuccess(accounts))
}
const fillAccountsList = async () => {
try {
dispatch(fetchAccountsListRequest())
const promise = plugin.blockchain.getAccounts()
promise.then(async (accounts: string[]) => {
const loadedAccounts = {}
if (!accounts) accounts = []
// allSettled is undefined..
// so the current promise (all) will finish when:
// - all the promises resolve
// - at least one reject
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
await (Promise as any).all(accounts.map((account) => {
return new Promise((resolve, reject) => {
plugin.blockchain.getBalanceInEther(account, (err, balance) => {
if (err) return reject(err)
const updated = shortenAddress(account, balance)
loadedAccounts[account] = updated
resolve(account)
})
})
}))
const provider = plugin.blockchain.getProvider()
if (provider === 'injected') {
const selectedAddress = plugin.blockchain.getInjectedWeb3Address()
if (!(Object.keys(loadedAccounts).includes(selectedAddress))) setAccount(null)
}
dispatch(fetchAccountsListSuccess(loadedAccounts))
}).catch((e) => {
dispatch(fetchAccountsListFailed(e.message))
})
} catch (e) {
dispatch(displayPopUp(`Cannot get account list: ${e}`))
}
}
export const setAccount = (account: string) => {
dispatch(setSelectedAccount(account))
}
export const setUnit = (unit: 'ether' | 'finney' | 'gwei' | 'wei') => {
dispatch(setSendUnit(unit))
}
export const setGasFee = (value: number) => {
dispatch(setGasLimit(value))
}
const setFinalContext = () => {
// set the final context. Cause it is possible that this is not the one we've originaly selected
const value = _getProviderDropdownValue()
setExecEnv(value)
clearInstances()
}
const _getProviderDropdownValue = (): string => {
const provider = plugin.blockchain.getProvider()
const fork = plugin.blockchain.getCurrentFork()
return provider === 'vm' ? provider + '-' + fork : provider
}
const setExecEnv = (env: string) => {
dispatch(setExecutionEnvironment(env))
}
export const setNetworkNameFromProvider = (networkName: string) => {
dispatch(setNetworkName(networkName))
}
const addExternalProvider = (network) => {
dispatch(addProvider(network))
}
const removeExternalProvider = (name) => {
dispatch(removeProvider(name))
}
export const setExecutionContext = (executionContext: { context: string, fork: string }) => {
const displayContent = web3Dialog(plugin.REACT_API.externalEndpoint, setWeb3Endpoint)
plugin.blockchain.changeExecutionContext(executionContext, () => {
plugin.call('notification', 'modal', {
id: 'envNotification',
title: 'External node request',
message: displayContent,
okLabel: 'OK',
cancelLabel: 'Cancel',
okFn: () => {
plugin.blockchain.setProviderFromEndpoint(plugin.REACT_API.externalEndpoint, executionContext, (alertMsg) => {
if (alertMsg) plugin.call('notification', 'toast', alertMsg)
setFinalContext()
})
},
cancelFn: () => {
setFinalContext()
}
})
}, (alertMsg) => {
plugin.call('notification', 'toast', alertMsg)
}, () => { setFinalContext() })
}
export const setWeb3Endpoint = (endpoint: string) => {
dispatch(setExternalEndpoint(endpoint))
}
export const clearPopUp = async () => {
dispatch(hidePopUp())
}
export const createNewBlockchainAccount = async (cbMessage: JSX.Element) => {
plugin.blockchain.newAccount(
'',
(cb) => {
dispatch(displayNotification('Enter Passphrase', cbMessage, 'OK', 'Cancel', async () => {
if (plugin.REACT_API.passphrase === plugin.REACT_API.matchPassphrase) {
cb(plugin.REACT_API.passphrase)
} else {
dispatch(displayNotification('Error', 'Passphase does not match', 'OK', null))
}
setPassphrase('')
setMatchPassphrase('')
}, () => {}))
},
async (error, address) => {
if (error) {
return dispatch(displayPopUp('Cannot create an account: ' + error))
}
dispatch(displayPopUp(`account ${address} created`))
await fillAccountsList()
}
)
}
export const setPassphrasePrompt = (passphrase: string) => {
dispatch(setPassphrase(passphrase))
}
export const setMatchPassphrasePrompt = (passphrase: string) => {
dispatch(setMatchPassphrase(passphrase))
}
export const signMessageWithAddress = (account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => {
plugin.blockchain.signMessage(message, account, passphrase, (err, msgHash, signedData) => {
if (err) {
return displayPopUp(err)
}
dispatch(displayNotification('Signed Message', modalContent(msgHash, signedData), 'OK', null, () => {}, null))
})
}
const broadcastCompilationResult = async (file, source, languageVersion, data, input?) => {
// TODO check whether the tab is configured
const compiler = new CompilerAbstract(languageVersion, data, source, input)
plugin.compilersArtefacts[languageVersion] = compiler
plugin.compilersArtefacts.__last = compiler
const contracts = getCompiledContracts(compiler).map((contract) => {
return { name: languageVersion, alias: contract.name, file: contract.file, compiler }
})
const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file].ast)
console.log('data: ', data)
if (isUpgradeable) dispatch(setDeployOptions([{ title: 'Deploy with Proxy', active: false }]))
else dispatch(setDeployOptions([]))
dispatch(fetchContractListSuccess({ [file]: contracts }))
dispatch(setCurrentFile(file))
}
const loadContractFromAddress = (address, confirmCb, cb) => {
if (/.(.abi)$/.exec(plugin.config.get('currentFile'))) {
confirmCb(() => {
let abi
try {
abi = JSON.parse(plugin.editor.currentContent())
} catch (e) {
return cb('Failed to parse the current file as JSON ABI.')
}
_paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithABI'])
cb(null, 'abi', abi)
})
} else {
_paq.push(['trackEvent', 'udapp', 'AtAddressLoadWithArtifacts'])
cb(null, 'instance')
}
}
const getCompiledContracts = (compiler) => {
const contracts = []
compiler.visitContracts((contract) => {
contracts.push(contract)
})
return contracts
}
export const getSelectedContract = (contractName: string, compiler: CompilerAbstractType): ContractData => {
if (!contractName) return null
// const compiler = plugin.compilersArtefacts[compilerAtributeName]
if (!compiler) return null
const contract = compiler.getContract(contractName)
return {
name: contractName,
contract: contract,
compiler: compiler,
abi: contract.object.abi,
bytecodeObject: contract.object.evm.bytecode.object,
bytecodeLinkReferences: contract.object.evm.bytecode.linkReferences,
object: contract.object,
deployedBytecode: contract.object.evm.deployedBytecode,
getConstructorInterface: () => {
return txHelper.getConstructorInterface(contract.object.abi)
},
getConstructorInputs: () => {
const constructorInteface = txHelper.getConstructorInterface(contract.object.abi)
return txHelper.inputParametersDeclarationToString(constructorInteface.inputs)
},
isOverSizeLimit: () => {
const deployedBytecode = contract.object.evm.deployedBytecode
return (deployedBytecode && deployedBytecode.object.length / 2 > 24576)
},
metadata: contract.object.metadata
}
}
const getCompilerContracts = () => {
return plugin.compilersArtefacts.__last.getData().contracts
}
const terminalLogger = (view: JSX.Element) => {
plugin.call('terminal', 'logHtml', view)
}
const confirmationHandler = (confirmDialogContent: MainnetPrompt, network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
const amount = plugin.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialogContent(tx, network, amount, gasEstimation, plugin.blockchain.determineGasFees(tx), plugin.blockchain.determineGasPrice.bind(plugin.blockchain))
dispatch(displayNotification('Confirm transaction', content, 'Confirm', 'Cancel', () => {
plugin.blockchain.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', plugin.REACT_API.confirmSettings)
// TODO: check if this is check is still valid given the refactor
if (!plugin.REACT_API.gasPriceStatus) {
cancelCb('Given transaction fee is not correct')
} else {
continueTxExecution({ maxFee: plugin.REACT_API.maxFee, maxPriorityFee: plugin.REACT_API.maxPriorityFee, baseFeePerGas: plugin.REACT_API.baseFeePerGas, gasPrice: plugin.REACT_API.gasPrice })
}
}, () => {
return cancelCb('Transaction canceled by user.')
}))
}
const getConfirmationCb = (confirmDialogContent: MainnetPrompt) => {
// this code is the same as in recorder.js. TODO need to be refactored out
return (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(confirmDialogContent, network, tx, gasEstimation, continueTxExecution, cancelCb)
}
}
const continueHandler = (gasEstimationPrompt: (msg: string) => JSX.Element, error, continueTxExecution, cancelCb) => {
if (error) {
const msg = typeof error !== 'string' ? error.message : error
dispatch(displayNotification('Gas estimation failed', gasEstimationPrompt(msg), 'Send Transaction', 'Cancel Transaction', () => {
continueTxExecution()
}, () => {
cancelCb()
}))
} else {
continueTxExecution()
}
}
const promptHandler = (passphrasePrompt, okCb, cancelCb) => {
dispatch(displayNotification('Passphrase requested', passphrasePrompt('Personal mode is enabled. Please provide passphrase of account'), 'OK', 'Cancel', okCb, cancelCb))
}
export const createInstance = async (
selectedContract: ContractData,
gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (msg: string) => JSX.Element,
logBuilder: (msg: string) => JSX.Element,
publishToStorage: (storage: 'ipfs' | 'swarm',
contract: ContractData) => void,
mainnetPrompt: MainnetPrompt,
isOverSizePrompt: () => JSX.Element,
args,
deployMode: DeployMode[]) => {
const statusCb = (msg: string) => {
const log = logBuilder(msg)
return terminalLogger(log)
}
const finalCb = (error, contractObject, address) => {
if (error) {
const log = logBuilder(error)
return terminalLogger(log)
}
addInstance({ contractData: contractObject, address, name: contractObject.name })
const data = plugin.compilersArtefacts.getCompilerAbstract(contractObject.contract.file)
plugin.compilersArtefacts.addResolvedContract(addressToString(address), data)
if (plugin.REACT_API.ipfsChecked) {
_paq.push(['trackEvent', 'udapp', 'DeployAndPublish', plugin.REACT_API.networkName])
publishToStorage('ipfs', selectedContract)
} else {
_paq.push(['trackEvent', 'udapp', 'DeployOnly', plugin.REACT_API.networkName])
}
deployMode.forEach(async (mode) => {
const owner = plugin.REACT_API.accounts.selectedAccount
if (mode === 'Deploy with Proxy') await plugin.call('openzeppelin-proxy', 'execute', address, owner)
})
}
let contractMetadata
try {
contractMetadata = await plugin.call('compilerMetadata', 'deployMetadataOf', selectedContract.name, selectedContract.contract.file)
} catch (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
}
const compilerContracts = getCompilerContracts()
const confirmationCb = getConfirmationCb(mainnetPrompt)
if (selectedContract.isOverSizeLimit()) {
return dispatch(displayNotification('Contract code size over limit', isOverSizePrompt(), 'Force Send', 'Cancel', () => {
deployContract(selectedContract, args, contractMetadata, compilerContracts, {
continueCb: (error, continueTxExecution, cancelCb) => {
continueHandler(gasEstimationPrompt, error, continueTxExecution, cancelCb)
},
promptCb: (okCb, cancelCb) => {
promptHandler(passphrasePrompt, okCb, cancelCb)
},
statusCb,
finalCb
}, confirmationCb)
}, () => {
const log = logBuilder(`creation of ${selectedContract.name} canceled by user.`)
return terminalLogger(log)
}))
}
deployContract(selectedContract, args, contractMetadata, compilerContracts, {
continueCb: (error, continueTxExecution, cancelCb) => {
continueHandler(gasEstimationPrompt, error, continueTxExecution, cancelCb)
},
promptCb: (okCb, cancelCb) => {
promptHandler(passphrasePrompt, okCb, cancelCb)
},
statusCb,
finalCb
}, confirmationCb)
}
const deployContract = (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) => {
_paq.push(['trackEvent', 'udapp', 'DeployContractTo', plugin.REACT_API.networkName])
const { statusCb } = callbacks
if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) {
return plugin.blockchain.deployContractAndLibraries(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb)
}
if (Object.keys(selectedContract.bytecodeLinkReferences).length) statusCb(`linking ${JSON.stringify(selectedContract.bytecodeLinkReferences, null, '\t')} using ${JSON.stringify(contractMetadata.linkReferences, null, '\t')}`)
plugin.blockchain.deployContractWithLibrary(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb)
}
export const updateGasPriceStatus = (status: boolean) => {
dispatch(setGasPriceStatus(status))
}
export const updateConfirmSettings = (confirmation: boolean) => {
dispatch(setConfirmSettings(confirmation))
}
export const updateMaxFee = (fee: string) => {
dispatch(setMaxFee(fee))
}
export const updateMaxPriorityFee = (fee: string) => {
dispatch(setMaxPriorityFee(fee))
}
export const updateBaseFeePerGas = (baseFee: string) => {
dispatch(setBaseFeePerGas(baseFee))
}
export const updateGasPrice = (price: string) => {
dispatch(setGasPrice(price))
}
export const updateTxFeeContent = (content: string) => {
dispatch(setTxFeeContent(content))
}
export const addInstance = (instance: { contractData?: ContractData, address: string, name: string, abi?: any, decodedResponse?: Record<number, any> }) => {
instance.decodedResponse = {}
dispatch(addNewInstance(instance))
}
export const removeInstance = (index: number) => {
dispatch(removeExistingInstance(index))
}
export const clearInstances = () => {
dispatch(clearAllInstances())
dispatch(clearRecorderCount())
}
export const loadAddress = (contract: ContractData, address: string) => {
loadContractFromAddress(address,
(cb) => {
dispatch(displayNotification('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, 'OK', 'Cancel', cb, null))
},
(error, loadType, abi) => {
if (error) {
return dispatch(displayNotification('Alert', error, 'OK', null))
}
if (loadType === 'abi') {
return addInstance({ abi, address, name: '<at address>' })
} else if (loadType === 'instance') {
if (!contract) return dispatch(displayPopUp('No compiled contracts found.'))
const currentFile = plugin.REACT_API.contracts.currentFile
const compiler = plugin.REACT_API.contracts.contractList[currentFile].find(item => item.alias === contract.name)
const contractData = getSelectedContract(contract.name, compiler.compiler)
return addInstance({ contractData, address, name: contract.name })
}
}
)
}
export const getContext = () => {
return plugin.blockchain.context()
}
export const runTransactions = (
instanceIndex: number,
lookupOnly: boolean,
funcABI: FuncABI,
inputsValues: string,
contractName: string,
contractABI, contract,
address,
logMsg:string,
logBuilder: (msg: string) => JSX.Element,
mainnetPrompt: MainnetPrompt,
gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (msg: string) => JSX.Element,
funcIndex?: number) => {
let callinfo = ''
if (lookupOnly) callinfo = 'call'
else if (funcABI.type === 'fallback' || funcABI.type === 'receive') callinfo = 'lowLevelInteracions'
else callinfo = 'transact'
_paq.push(['trackEvent', 'udapp', callinfo, plugin.blockchain.getCurrentNetworkStatus().network.name])
const params = funcABI.type !== 'fallback' ? inputsValues : ''
plugin.blockchain.runOrCallContractMethod(
contractName,
contractABI,
funcABI,
contract,
inputsValues,
address,
params,
lookupOnly,
logMsg,
(msg) => {
const log = logBuilder(msg)
return terminalLogger(log)
},
(returnValue) => {
const response = txFormat.decodeResponse(returnValue, funcABI)
dispatch(setDecodedResponse(instanceIndex, response, funcIndex))
},
(network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(mainnetPrompt, network, tx, gasEstimation, continueTxExecution, cancelCb)
},
(error, continueTxExecution, cancelCb) => {
continueHandler(gasEstimationPrompt, error, continueTxExecution, cancelCb)
},
(okCb, cancelCb) => {
promptHandler(passphrasePrompt, okCb, cancelCb)
}
)
}
const saveScenario = async (newPath: string, provider, promptCb, cb) => {
const txJSON = JSON.stringify(plugin.recorder.getAll(), null, 2)
promptCb(async () => {
try {
await provider.set(newPath, txJSON)
await plugin.fileManager.open(newPath)
} catch (error) {
if (error) return cb('Failed to create file. ' + newPath + ' ' + error)
}
})
}
export const storeScenario = async (prompt: (msg: string, defaultValue: string) => JSX.Element) => {
const path = plugin.fileManager.currentPath()
const fileProvider = await plugin.fileManager.fileProviderOf(path)
if (!fileProvider) return displayNotification('Alert', 'Invalid File Provider', 'OK', null)
const newPath = await createNonClashingNameAsync(path + '/' + plugin.REACT_API.recorder.pathToScenario, plugin.fileManager)
const newName = extractNameFromKey(newPath)
saveScenario(newPath, fileProvider,
(cb) => {
dispatch(displayNotification('Save transactions as scenario', prompt('Transactions will be saved in a file under ' + path, newName), 'OK', 'Cancel', cb, null))
},
(error) => {
if (error) return dispatch(displayNotification('Alert', error, 'OK', null))
}
)
}
const runScenario = (file: string, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {
if (!file) return dispatch(displayNotification('Alert', 'Unable to run scenerio, no specified scenario file', 'OK', null))
plugin.fileManager.readFile(file).then((json) => {
// TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily
plugin.recorder.runScenario(
json,
(error, continueTxExecution, cancelCb) => {
continueHandler(gasEstimationPrompt, error, continueTxExecution, cancelCb)
}, (okCb, cancelCb) => {
promptHandler(passphrasePrompt, okCb, cancelCb)
}, (msg) => {
dispatch(displayNotification('Alert', msg, 'OK', null))
}, (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(confirmDialogContent, network, tx, gasEstimation, continueTxExecution, cancelCb)
}, (msg: string) => {
const log = logBuilder(msg)
return terminalLogger(log)
}, (error, abi, address, contractName) => {
if (error) {
return dispatch(displayNotification('Alert', error, 'OK', null))
}
addInstance({ name: contractName, address, abi })
})
}).catch((error) => dispatch(displayNotification('Alert', error, 'OK', null)))
}
export const runCurrentScenario = (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {
const file = plugin.config.get('currentFile')
if (!file) return dispatch(displayNotification('Alert', 'A scenario file has to be selected', 'Ok', null))
runScenario(file, gasEstimationPrompt, passphrasePrompt, confirmDialogContent, logBuilder)
}
export const updateScenarioPath = (path: string) => {
dispatch(setPathToScenario(path))
}
export const getFuncABIInputs = (funcABI: FuncABI) => {
return plugin.blockchain.getInputs(funcABI)
}
export const setSendTransactionValue = (value: string) => {
dispatch(setSendValue(value))
}
const resetAndInit = () => {
plugin.blockchain.resetAndInit(plugin.config, {
getAddress: (cb) => {
cb(null, plugin.REACT_API.accounts.selectedAccount)
},
getValue: (cb) => {
try {
const number = plugin.REACT_API.sendValue
const unit = plugin.REACT_API.sendUnit
cb(null, Web3.utils.toWei(number, unit))
} catch (e) {
cb(e)
}
},
getGasLimit: (cb) => {
try {
cb(null, '0x' + new ethJSUtil.BN(plugin.REACT_API.gasLimit, 10).toString(16))
} catch (e) {
cb(e.message)
}
}
})
}
export const setAccountAddress = (account: string) => setAccount(dispatch, account)
export const setUnitValue = (unit: 'ether' | 'finney' | 'gwei' | 'wei') => setUnit(dispatch, unit)
export const setGasFeeAmount = (value: number) => setGasFee(dispatch, value)
export const setExecutionEnvironment = (executionContext: { context: string, fork: string }) => setExecutionContext(plugin, dispatch, executionContext)
export const hideToaster = () => clearPopUp(dispatch)
export const createNewAddress = (cbMessage: JSX.Element) => createNewBlockchainAccount(plugin, dispatch, cbMessage)
export const setPassphraseModal = (passphrase: string) => setPassphrasePrompt(dispatch, passphrase)
export const setMatchPassphraseModal = (passphrase: string) => setMatchPassphrasePrompt(dispatch, passphrase)
export const signMessage = (account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => signMessageWithAddress(plugin, dispatch, account, message, modalContent, passphrase)
export const fetchSelectedContract = (contractName: string, compiler: CompilerAbstractType) => getSelectedContract(contractName, compiler)
export const createNewInstance = async (selectedContract: ContractData, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, logBuilder: (msg: string) => JSX.Element, publishToStorage: (storage: 'ipfs' | 'swarm', contract: ContractData) => void, mainnetPrompt: MainnetPrompt, isOverSizePrompt: () => JSX.Element, args, deployMode: DeployMode[]) => createInstance(plugin, dispatch, selectedContract, gasEstimationPrompt, passphrasePrompt, logBuilder, publishToStorage, mainnetPrompt, isOverSizePrompt, args, deployMode)
export const setSendValue = (value: string) => setSendTransactionValue(dispatch, value)
export const setBaseFeePerGas = (baseFee: string) => updateBaseFeePerGas(dispatch, baseFee)
export const setConfirmSettings = (confirmation: boolean) => updateConfirmSettings(dispatch, confirmation)
export const setGasPrice = (price: string) => updateGasPrice(dispatch, price)
export const setGasPriceStatus = (status: boolean) => updateGasPriceStatus(dispatch, status)
export const setMaxFee = (fee: string) => updateMaxFee(dispatch, fee)
export const setMaxPriorityFee = (fee: string) => updateMaxPriorityFee(dispatch, fee)
export const setTxFeeContent = (content: string) => updateTxFeeContent(dispatch, content)
export const removeInstances = () => clearInstances(dispatch)
export const removeSingleInstance = (index: number) => removeInstance(dispatch, index)
export const getExecutionContext = () => getContext(plugin)
export const executeTransactions = (instanceIndex: number, lookupOnly: boolean, funcABI: FuncABI, inputsValues: string, contractName: string, contractABI, contract, address, logMsg:string, logBuilder: (msg: string) => JSX.Element, mainnetPrompt: MainnetPrompt, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, funcIndex?: number) => runTransactions(plugin, dispatch, instanceIndex, lookupOnly, funcABI, inputsValues, contractName, contractABI, contract, address, logMsg, logBuilder, mainnetPrompt, gasEstimationPrompt, passphrasePrompt, funcIndex)
export const loadFromAddress = (contract: ContractData, address: string) => loadAddress(plugin, dispatch, contract, address)
export const storeNewScenario = async (prompt: (msg: string, defaultValue: string) => JSX.Element) => storeScenario(plugin, dispatch, prompt)
export const runScenario = (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => runCurrentScenario(plugin, dispatch, gasEstimationPrompt, passphrasePrompt, confirmDialogContent, logBuilder)
export const setScenarioPath = (path: string) => updateScenarioPath(dispatch, path)
export const getFuncABIValues = (funcABI: FuncABI) => getFuncABIInputs(plugin, funcABI)
export const setNetworkName = (networkName: string) => setNetworkNameFromProvider(dispatch, networkName)

@ -1,6 +1,6 @@
import { ContractList } from '../reducers/runTab'
import { ContractData } from '@remix-project/core-plugin'
import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_TX_FEE_CONTENT } from '../constants'
import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_TX_FEE_CONTENT } from '../constants'
import { DeployMode, DeployOptions } from '../types'
export const fetchAccountsListRequest = () => {
@ -300,3 +300,10 @@ export const setDeployOptions = (deployOptions: DeployOptions[]) => {
type: SET_DEPLOY_OPTIONS
}
}
export const setCurrentContract = (contractName: string) => {
return {
payload: contractName,
type: SET_CURRENT_CONTRACT
}
}

@ -0,0 +1,72 @@
import { createNonClashingNameAsync, extractNameFromKey } from "@remix-ui/helper"
import { MainnetPrompt } from "../types"
import { RunTab } from "../types/run-tab"
import { addInstance } from "./actions"
import { confirmationHandler, continueHandler, promptHandler, terminalLogger } from "./deploy"
import { displayNotification } from "./payload"
const saveScenario = async (plugin: RunTab, newPath: string, provider, promptCb, cb) => {
const txJSON = JSON.stringify(plugin.recorder.getAll(), null, 2)
promptCb(async () => {
try {
await provider.set(newPath, txJSON)
await plugin.fileManager.open(newPath)
} catch (error) {
if (error) return cb('Failed to create file. ' + newPath + ' ' + error)
}
})
}
export const storeScenario = async (plugin: RunTab, dispatch: React.Dispatch<any>, prompt: (msg: string, defaultValue: string) => JSX.Element) => {
const path = plugin.fileManager.currentPath()
const fileProvider = await plugin.fileManager.fileProviderOf(path)
if (!fileProvider) return displayNotification('Alert', 'Invalid File Provider', 'OK', null)
const newPath = await createNonClashingNameAsync(path + '/' + plugin.REACT_API.recorder.pathToScenario, plugin.fileManager)
const newName = extractNameFromKey(newPath)
saveScenario(plugin, newPath, fileProvider,
(cb) => {
dispatch(displayNotification('Save transactions as scenario', prompt('Transactions will be saved in a file under ' + path, newName), 'OK', 'Cancel', cb, null))
},
(error) => {
if (error) return dispatch(displayNotification('Alert', error, 'OK', null))
}
)
}
const runScenario = (plugin: RunTab, dispatch: React.Dispatch<any>, file: string, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {
if (!file) return dispatch(displayNotification('Alert', 'Unable to run scenerio, no specified scenario file', 'OK', null))
plugin.fileManager.readFile(file).then((json) => {
// TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily
plugin.recorder.runScenario(
json,
(error, continueTxExecution, cancelCb) => {
continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb)
}, (okCb, cancelCb) => {
promptHandler(dispatch, passphrasePrompt, okCb, cancelCb)
}, (msg) => {
dispatch(displayNotification('Alert', msg, 'OK', null))
}, (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
confirmationHandler(plugin, dispatch, confirmDialogContent, network, tx, gasEstimation, continueTxExecution, cancelCb)
}, (msg: string) => {
const log = logBuilder(msg)
return terminalLogger(plugin, log)
}, (error, abi, address, contractName) => {
if (error) {
return dispatch(displayNotification('Alert', error, 'OK', null))
}
addInstance(dispatch, { name: contractName, address, abi })
})
}).catch((error) => dispatch(displayNotification('Alert', error, 'OK', null)))
}
export const runCurrentScenario = (plugin: RunTab, dispatch: React.Dispatch<any>, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => {
const file = plugin.config.get('currentFile')
if (!file) return dispatch(displayNotification('Alert', 'A scenario file has to be selected', 'Ok', null))
runScenario(plugin, dispatch, file, gasEstimationPrompt, passphrasePrompt, confirmDialogContent, logBuilder)
}

@ -44,3 +44,4 @@ export const RESET_STATE = 'RESET_STATE'
export const ADD_DEPLOY_OPTION = 'ADD_DEPLOY_OPTION'
export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION'
export const SET_DEPLOY_OPTIONS = 'SET_DEPLOY_OPTIONS'
export const SET_CURRENT_CONTRACT = 'SET_CURRENT_CONTRACT'

@ -1,7 +1,7 @@
import { CompilerAbstract } from '@remix-project/remix-solidity-ts'
import { ContractData } from '@remix-project/core-plugin'
import { DeployMode, DeployOptions } from '../types'
import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_TX_FEE_CONTENT } from '../constants'
import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_TX_FEE_CONTENT } from '../constants'
interface Action {
type: string
payload: any
@ -66,6 +66,7 @@ export interface RunTabState {
},
loadType: 'abi' | 'sol' | 'other'
currentFile: string,
currentContract: string,
compilationCount: number,
isRequesting: boolean,
isSuccessful: boolean,
@ -159,6 +160,7 @@ export const runTabInitialState: RunTabState = {
contractList: {},
loadType: 'other',
currentFile: '',
currentContract: '',
compilationCount: 0,
isRequesting: false,
isSuccessful: false,
@ -482,6 +484,18 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A
}
}
case SET_CURRENT_CONTRACT: {
const payload: string = action.payload
return {
...state,
contracts: {
...state.contracts,
currentContract: payload
}
}
}
case SET_LOAD_TYPE: {
const payload: 'abi' | 'sol' | 'other' = action.payload

@ -11,22 +11,22 @@ import { Modal, Network, RunTabProps, Tx } from './types'
import { ContractData } from '@remix-project/core-plugin'
import { runTabInitialState, runTabReducer } from './reducers/runTab'
import {
initRunTab, setAccount,
setUnit, setGasFee,
setExecutionContext, setWeb3Endpoint,
clearPopUp, createNewBlockchainAccount,
setPassphrasePrompt, setMatchPassphrasePrompt,
signMessageWithAddress, getSelectedContract,
createInstance, setSendTransactionValue,
updateBaseFeePerGas, updateConfirmSettings,
updateGasPrice, updateGasPriceStatus,
updateMaxFee, updateMaxPriorityFee,
updateTxFeeContent, clearInstances,
removeInstance, getContext,
runTransactions, loadAddress,
storeScenario, runCurrentScenario,
updateScenarioPath, getFuncABIInputs,
setNetworkNameFromProvider
initRunTab, setAccountAddress,
setUnitValue, setGasFeeAmount,
setExecutionEnvironment,
hideToaster, createNewAddress,
setPassphraseModal, setMatchPassphraseModal,
signMessage, fetchSelectedContract,
createNewInstance, setSendValue,
setBaseFeePerGas, setConfirmSettings,
setGasPrice, setGasPriceStatus,
setMaxFee, setMaxPriorityFee,
setTxFeeContent, removeInstances,
removeSingleInstance, getExecutionContext,
executeTransactions, loadFromAddress,
storeNewScenario, runScenario,
setScenarioPath, getFuncABIValues,
setNetworkName
} from './actions'
import './css/run-tab.css'
import { PublishToStorage } from '@remix-ui/publish-to-storage'
@ -134,7 +134,7 @@ export function RunTabUI (props: RunTabProps) {
const handleToaster = () => {
setFocusToaster('')
clearPopUp()
hideToaster()
}
const toast = (toasterMsg: string) => {
@ -171,11 +171,11 @@ export function RunTabUI (props: RunTabProps) {
}
const passphrasePrompt = (message: string) => {
return <PassphrasePrompt message={message} setPassphrase={setPassphrasePrompt} defaultValue={runTab.passphrase} />
return <PassphrasePrompt message={message} setPassphrase={setPassphraseModal} defaultValue={runTab.passphrase} />
}
const scenarioPrompt = (message: string, defaultValue) => {
return <ScenarioPrompt message={message} setScenarioPath={updateScenarioPath} defaultValue={defaultValue} />
return <ScenarioPrompt message={message} setScenarioPath={setScenarioPath} defaultValue={defaultValue} />
}
const mainnetPrompt = (tx: Tx, network: Network, amount: string, gasEstimation: string, gasFees: (maxFee: string, cb: (txFeeText: string, priceStatus: boolean) => void) => void, determineGasPrice: (cb: (txFeeText: string, gasPriceValue: string, gasPriceStatus: boolean) => void) => void) => {
@ -186,13 +186,13 @@ export function RunTabUI (props: RunTabProps) {
amount={amount}
gasEstimation={gasEstimation}
setNewGasPrice={gasFees}
updateBaseFeePerGas={updateBaseFeePerGas}
updateConfirmSettings={updateConfirmSettings}
updateGasPrice={updateGasPrice}
updateGasPriceStatus={updateGasPriceStatus}
updateMaxFee={updateMaxFee}
updateMaxPriorityFee={updateMaxPriorityFee}
setTxFeeContent={updateTxFeeContent}
updateBaseFeePerGas={setBaseFeePerGas}
updateConfirmSettings={setConfirmSettings}
updateGasPrice={setGasPrice}
updateGasPriceStatus={setGasPriceStatus}
updateMaxFee={setMaxFee}
updateMaxPriorityFee={setMaxPriorityFee}
setTxFeeContent={setTxFeeContent}
txFeeContent={runTab.txFeeContent}
maxFee={runTab.maxFee}
maxPriorityFee={runTab.maxPriorityFee}
@ -207,33 +207,32 @@ export function RunTabUI (props: RunTabProps) {
networkName={runTab.networkName}
personalMode={runTab.personalMode}
selectExEnv={runTab.selectExEnv}
setWeb3Endpoint={setWeb3Endpoint}
accounts={runTab.accounts}
setAccount={setAccount}
setUnit={setUnit}
setAccount={setAccountAddress}
setUnit={setUnitValue}
sendValue={runTab.sendValue}
setSendValue={setSendTransactionValue}
setSendValue={setSendValue}
sendUnit={runTab.sendUnit}
gasLimit={runTab.gasLimit}
setGasFee={setGasFee}
setGasFee={setGasFeeAmount}
providers={runTab.providers}
setExecutionContext={setExecutionContext}
createNewBlockchainAccount={createNewBlockchainAccount}
setPassphrase={setPassphrasePrompt}
setMatchPassphrase={setMatchPassphrasePrompt}
setExecutionContext={setExecutionEnvironment}
createNewBlockchainAccount={createNewAddress}
setPassphrase={setPassphraseModal}
setMatchPassphrase={setMatchPassphraseModal}
modal={modal}
tooltip={toast}
signMessageWithAddress={signMessageWithAddress}
signMessageWithAddress={signMessage}
passphrase={runTab.passphrase}
/>
<ContractDropdownUI
exEnvironment={runTab.selectExEnv}
contracts={runTab.contracts}
getSelectedContract={getSelectedContract}
getSelectedContract={fetchSelectedContract}
modal={modal}
passphrase={runTab.passphrase}
setPassphrase={setPassphrasePrompt}
createInstance={createInstance}
setPassphrase={setPassphraseModal}
createInstance={createNewInstance}
ipfsCheckedState={runTab.ipfsChecked}
setIpfsCheckedState={setCheckIpfs}
publishToStorage={publishToStorage}
@ -242,9 +241,9 @@ export function RunTabUI (props: RunTabProps) {
passphrasePrompt={passphrasePrompt}
mainnetPrompt={mainnetPrompt}
tooltip={toast}
loadAddress={loadAddress}
loadAddress={loadFromAddress}
networkName={runTab.networkName}
setNetworkName={setNetworkNameFromProvider}
setNetworkName={setNetworkName}
deployOptions={runTab.deployOptions}
/>
<RecorderUI
@ -252,23 +251,23 @@ export function RunTabUI (props: RunTabProps) {
logBuilder={logBuilder}
passphrasePrompt={passphrasePrompt}
mainnetPrompt={mainnetPrompt}
storeScenario={storeScenario}
runCurrentScenario={runCurrentScenario}
storeScenario={storeNewScenario}
runCurrentScenario={runScenario}
scenarioPrompt={scenarioPrompt}
count={runTab.recorder.transactionCount}
/>
<InstanceContainerUI
instances={runTab.instances}
clearInstances={clearInstances}
removeInstance={removeInstance}
getContext={getContext}
clearInstances={removeInstances}
removeInstance={removeSingleInstance}
getContext={getExecutionContext}
gasEstimationPrompt={gasEstimationPrompt}
logBuilder={logBuilder}
passphrasePrompt={passphrasePrompt}
mainnetPrompt={mainnetPrompt}
runTransactions={runTransactions}
runTransactions={executeTransactions}
sendValue={runTab.sendValue}
getFuncABIInputs={getFuncABIInputs}
getFuncABIInputs={getFuncABIValues}
/>
</div>
</div>

@ -21,7 +21,6 @@ export interface SettingsProps {
sendUnit: string,
gasLimit: number,
setGasFee: (value: number) => void,
setWeb3Endpoint: (endpoint: string) => void,
personalMode: boolean,
networkName: string,
providers: {
@ -225,7 +224,22 @@ export interface Modal {
export type DeployMode = 'Deploy with Proxy'
export interface DeployOptions {
title: DeployMode,
active: boolean
active: boolean,
inputs: {
[key: string]: {
inputs: [
{
internalType: string,
name: string,
type: string
}
],
name: "initialize",
outputs: any[],
stateMutability: string,
type: string
}
}
}
export interface ContractGUIProps {

Loading…
Cancel
Save