From 0d8285d7fc2112cfe7c7c7b8c2fade2b80b73d17 Mon Sep 17 00:00:00 2001 From: David Disu Date: Tue, 28 Dec 2021 15:02:00 +0100 Subject: [PATCH] Udapp recorder module --- apps/remix-ide/src/app/udapp/run-tab.js | 107 +--------------- .../remix-ui/run-tab/src/lib/actions/index.ts | 116 +++++++++++++++--- .../run-tab/src/lib/actions/payload.ts | 22 +++- .../src/lib/components/contractDropdownUI.tsx | 21 ---- .../lib/components/instanceContainerUI.tsx | 2 +- .../src/lib/components/recorderCardUI.tsx | 17 +-- .../run-tab/src/lib/components/scenario.tsx | 22 ++++ .../run-tab/src/lib/components/settingsUI.tsx | 24 ---- .../src/lib/components/universalDappUI.tsx | 29 ++++- .../run-tab/src/lib/reducers/runTab.ts | 51 +++++++- libs/remix-ui/run-tab/src/lib/run-tab.tsx | 20 ++- libs/remix-ui/run-tab/src/lib/types/index.ts | 23 ++-- .../run-tab/src/lib/types/recorder.d.ts | 15 +++ .../run-tab/src/lib/types/run-tab.d.ts | 2 + 14 files changed, 269 insertions(+), 202 deletions(-) create mode 100644 libs/remix-ui/run-tab/src/lib/components/scenario.tsx create mode 100644 libs/remix-ui/run-tab/src/lib/types/recorder.d.ts diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index b0859cbbb6..40db3bdb2b 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -6,19 +6,10 @@ import * as packageJson from '../../../../../package.json' const yo = require('yo-yo') const EventManager = require('../../lib/events') -const Card = require('../ui/card') - -const css = require('../tabs/styles/run-tab-styles') -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 toaster = require('../ui/tooltip') const _paq = window._paq = window._paq || [] -const UniversalDAppUI = require('../ui/universal-dapp-ui') - const profile = { name: 'udapp', displayName: 'Deploy & run transactions', @@ -45,6 +36,7 @@ export class RunTab extends ViewPlugin { this.compilersArtefacts = compilersArtefacts this.networkModule = networkModule this.fileProvider = fileProvider + this.recorder = new Recorder(blockchain) this.REACT_API = {} this.setupEvents() this.el = document.createElement('div') @@ -62,14 +54,11 @@ export class RunTab extends ViewPlugin { getSettings () { return new Promise((resolve, reject) => { - if (!this.container) reject(new Error('UI not ready')) - else { - resolve({ - selectedAccount: this.settingsUI.getSelectedAccount(), - selectedEnvMode: this.blockchain.getProvider(), - networkEnvironment: this.container.querySelector('*[data-id="settingsNetworkEnv"]').textContent - }) - } + resolve({ + selectedAccount: this.REACT_API.accounts.selectedAccount, + selectedEnvMode: this.REACT_API.selectExEnv, + networkEnvironment: this.REACT_API.networkName + }) }) } @@ -108,92 +97,8 @@ export class RunTab extends ViewPlugin { return this.blockchain.pendingTransactionsCount() } - renderSettings () { - this.settingsUI = new SettingsUI(this.blockchain, this.networkModule) - - this.settingsUI.event.register('clearInstance', () => { - this.event.trigger('clearInstance', []) - }) - } - - renderDropdown (udappUI, fileManager, compilersArtefacts, config, editor, logCallback) { - const dropdownLogic = new DropdownLogic(compilersArtefacts, config, editor, this) - this.contractDropdownUI = new ContractDropdownUI(this.blockchain, dropdownLogic, logCallback, this) - - fileManager.events.on('currentFileChanged', this.contractDropdownUI.changeCurrentFile.bind(this.contractDropdownUI)) - - this.contractDropdownUI.event.register('clearInstance', () => { - const noInstancesText = this.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - }) - this.contractDropdownUI.event.register('newContractABIAdded', (abi, address) => { - this.instanceContainer.appendChild(udappUI.renderInstanceFromABI(abi, address, '')) - }) - this.contractDropdownUI.event.register('newContractInstanceAdded', (contractObject, address, value) => { - this.instanceContainer.appendChild(udappUI.renderInstance(contractObject, address, value)) - }) - } - - renderRecorder (udappUI, fileManager, config, logCallback) { - this.recorderCount = yo`0` - - const recorder = new Recorder(this.blockchain) - recorder.event.register('recorderCountChange', (count) => { - this.recorderCount.innerText = count - }) - this.event.register('clearInstance', recorder.clearAll.bind(recorder)) - - this.recorderInterface = new RecorderUI(this.blockchain, fileManager, recorder, logCallback, config) - - this.recorderInterface.event.register('newScenario', (abi, address, contractName) => { - var noInstancesText = this.noInstancesText - if (noInstancesText.parentNode) { noInstancesText.parentNode.removeChild(noInstancesText) } - this.instanceContainer.appendChild(udappUI.renderInstanceFromABI(abi, address, contractName)) - }) - - this.recorderInterface.render() - } - - renderRecorderCard () { - const collapsedView = yo` -
-
${this.recorderCount}
-
` - - const expandedView = yo` -
-
- All transactions (deployed contracts and function executions) in this environment can be saved and replayed in - another environment. e.g Transactions created in Javascript VM can be replayed in the Injected Web3. -
-
- ${this.recorderInterface.recordButton} - ${this.recorderInterface.runButton} -
-
- ` - - this.recorderCard = new Card({}, {}, { title: 'Transactions recorded', collapsedView: collapsedView }) - this.recorderCard.event.register('expandCollapseCard', (arrow, body, status) => { - body.innerHTML = '' - status.innerHTML = '' - if (arrow === 'down') { - status.appendChild(collapsedView) - body.appendChild(expandedView) - } else if (arrow === 'up') { - status.appendChild(collapsedView) - } - }) - } - render () { return this.el - this.udappUI = new UniversalDAppUI(this.blockchain, 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() } renderComponent () { diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts index 26b7c29cf0..abd2f3be6e 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -2,8 +2,8 @@ import React from 'react' import * as ethJSUtil from 'ethereumjs-util' import Web3 from 'web3' -import { addressToString, shortenAddress } from '@remix-ui/helper' -import { addNewInstance, addProvider, clearAllInstances, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setDecodedResponse, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setIpfsCheckedState, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent } from './payload' +import { addressToString, createNonClashingNameAsync, shortenAddress } from '@remix-ui/helper' +import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setDecodedResponse, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setIpfsCheckedState, 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' @@ -92,21 +92,16 @@ const setupEvents = () => { plugin.on('manager', 'pluginDeactivated', removePluginProvider.bind(plugin)) - plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => - broadcastCompilationResult(file, source, languageVersion, data) - ) - 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('optimism-compiler', 'compilationFinished', (file, source, languageVersion, data) => - broadcastCompilationResult(file, source, languageVersion, data) - ) + plugin.on('solidity', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data)) + + 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('optimism-compiler', 'compilationFinished', (file, source, languageVersion, data) => broadcastCompilationResult(file, source, languageVersion, data)) + plugin.fileManager.events.on('currentFileChanged', (currentFile: string) => { if (/.(.abi)$/.exec(currentFile)) { dispatch(setLoadType('abi')) @@ -119,6 +114,14 @@ const setupEvents = () => { dispatch(setLoadType('other')) } }) + + plugin.recorder.event.register('recorderCountChange', (count) => { + dispatch(setRecorderCount(count)) + }) + + plugin.event.register('cleared', () => { + dispatch(clearRecorderCount()) + }) } const updateAccountBalances = () => { @@ -540,7 +543,7 @@ export const updateTxFeeContent = (content: string) => { dispatch(setTxFeeContent(content)) } -const addInstance = (instance: { contractData: ContractData, address: string, name: string }) => { +const addInstance = (instance: { contractData?: ContractData, address: string, name: string, abi?: any, decodedResponse?: any }) => { dispatch(addNewInstance(instance)) } @@ -550,6 +553,7 @@ export const removeInstance = (index: number) => { export const clearInstances = () => { dispatch(clearAllInstances()) + dispatch(clearRecorderCount()) } export const loadAddress = (contract: ContractData, address: string) => { @@ -595,10 +599,9 @@ export const runTransactions = ( 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 : '' + const params = funcABI.type !== 'fallback' ? inputsValues : '' plugin.blockchain.runOrCallContractMethod( contractName, contractABI, @@ -630,3 +633,76 @@ export const runTransactions = ( } ) } + +const saveScenario = (promptCb, cb) => { + const txJSON = JSON.stringify(plugin.recorder.getAll(), null, 2) + const path = plugin.fileManager.currentPath() + + promptCb(path, async () => { + const fileProvider = plugin.fileManager.fileProviderOf(path) + + if (!fileProvider) return + const newFile = path + '/' + plugin.REACT_API.recorder.pathToScenario + try { + console.log('newFile: ', newFile) + const newPath = await createNonClashingNameAsync(newFile, plugin.fileManager) + console.log('newPath: ', newPath) + // eslint-disable-next-line standard/no-callback-literal + if (!fileProvider.set(newPath, txJSON)) return cb('Failed to create file ' + newFile) + plugin.fileManager.open(newFile) + } catch (error) { + // eslint-disable-next-line standard/no-callback-literal + if (error) return cb('Failed to create file. ' + newFile + ' ' + error) + } + }) +} + +export const storeScenario = (prompt: (msg: string) => JSX.Element) => { + saveScenario( + (path, cb) => { + dispatch(displayNotification('Save transactions as scenario', prompt('Transactions will be saved in a file under ' + path), '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)) +} diff --git a/libs/remix-ui/run-tab/src/lib/actions/payload.ts b/libs/remix-ui/run-tab/src/lib/actions/payload.ts index c15579e08c..781da041ec 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/payload.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/payload.ts @@ -220,7 +220,7 @@ export const setTxFeeContent = (content: string) => { } } -export const addNewInstance = (instance: { contractData: ContractData, address: string, name: string }) => { +export const addNewInstance = (instance: { contractData?: ContractData, address: string, name: string, abi?: any }) => { return { type: 'ADD_INSTANCE', payload: instance @@ -249,3 +249,23 @@ export const setDecodedResponse = (index: number, decodedResponse) => { } } } + +export const setPathToScenario = (path: string) => { + return { + type: 'SET_PATH_TO_SCENARIO', + payload: path + } +} + +export const setRecorderCount = (count: number) => { + return { + type: 'SET_RECORDER_COUNT', + payload: count + } +} + +export const clearRecorderCount = () => { + return { + type: 'CLEAR_RECORDER_COUNT' + } +} diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx index c4a7f04190..269c55f09c 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -78,7 +78,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) { useEffect(() => { if (selectedContract) { - console.log('contractList: ', contractList) const contract = contractList.find(contract => contract.alias === selectedContract) setLoadedContractData(props.getSelectedContract(selectedContract, contract.name)) @@ -124,26 +123,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) { props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.logBuilder, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args) } - // listenToContextChange () { - // this.blockchain.event.register('networkStatus', ({ error, network }) => { - // if (error) { - // console.log('can\'t detect network') - // return - // } - // this.exEnvironment = this.blockchain.getProvider() - // this.networkName = network.name - - // const savedConfig = window.localStorage.getItem(`ipfs/${this.exEnvironment}/${this.networkName}`) - - // // check if an already selected option exist else use default workflow - // if (savedConfig !== null) { - // this.setCheckedState(savedConfig) - // } else { - // this.setCheckedState(this.networkName === 'Main') - // } - // }) - // } - const atAddressChanged = (event) => { const value = event.target.value diff --git a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx index 92024045eb..764b5febb4 100644 --- a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx @@ -24,6 +24,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) { { instanceList.length > 0 ?
{ props.instances.instanceList.map((instance, index) => { return }) } diff --git a/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx b/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx index 8ac3080797..2af2f05d3a 100644 --- a/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx @@ -23,28 +23,17 @@ export function RecorderUI (props: RecorderProps) { } const triggerRecordButton = () => { - // dispatch saveScenario() - // this.saveScenario( - // (path, cb) => { - // modalDialogCustom.prompt('Save transactions as scenario', 'Transactions will be saved in a file under ' + path, 'scenario.json', cb) - // }, - // (error) => { - // if (error) return modalDialogCustom.alert(error) - // } - // ) + props.storeScenario(props.scenarioPrompt) } const handleClickRunButton = () => { - // dispatchRunButtonClickHandler - // const file = this.config.get('currentFile') - // if (!file) return modalDialogCustom.alert('A scenario file has to be selected') - // this.runScenario(file) + props.runCurrentScenario(props.gasEstimationPrompt, props.passphrasePrompt, props.mainnetPrompt, props.logBuilder) } return (
- +
All transactions (deployed contracts and function executions) in this environment can be saved and replayed in diff --git a/libs/remix-ui/run-tab/src/lib/components/scenario.tsx b/libs/remix-ui/run-tab/src/lib/components/scenario.tsx new file mode 100644 index 0000000000..689fd116b3 --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/scenario.tsx @@ -0,0 +1,22 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' + +interface ScenarioProps { + message: string, + setScenarioPath: (path: string) => void, + defaultValue?: string +} + +export function ScenarioPrompt (props: ScenarioProps) { + const handleScenarioPath = (e) => { + props.setScenarioPath(e.target.value) + } + + return ( +
{ props.message } +
+ +
+
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx b/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx index bbd2f7840c..1d812ed6a3 100644 --- a/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx @@ -8,32 +8,8 @@ import { GasPriceUI } from './gasPrice' import { ValueUI } from './value' export function SettingsUI (props: SettingsProps) { - // constructor () { - // this.blockchain = blockchain - // this.event = new EventManager() - // this._components = {} - - // this._components = { - // registry: globalRegistry, - // networkModule: networkModule - // } - // this._components.registry = globalRegistry - // this._deps = { - // config: this._components.registry.get('config').api - // } - // this._deps.config.events.on('settings/personal-mode_changed', this.onPersonalChange.bind(this)) - // /** - // * generate a value used by the env dropdown list. - // * @return {String} - can return 'vm-berlin, 'vm-london', 'injected' or 'web3' - // */ - // _getProviderDropdownValue () { - // const provider = this.blockchain.getProvider() - // const fork = this.blockchain.getCurrentFork() - // return provider === 'vm' ? provider + '-' + fork : provider - // } - return (
diff --git a/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx index 291e71eb43..de6014711c 100644 --- a/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx @@ -18,16 +18,18 @@ export function UniversalDappUI (props: UdappProps) { const [expandPath, setExpandPath] = useState([]) const [llIError, setLlIError] = useState('') const [calldataValue, setCalldataValue] = useState('') + const [inputs, setInputs] = useState(null) + const [evmBC, setEvmBC] = useState(null) useEffect(() => { - if (!props.abi) { + if (!props.instance.abi) { const abi = txHelper.sortAbiFunction(props.instance.contractData.abi) setContractABI(abi) } else { - setContractABI(props.abi) + setContractABI(props.instance.abi) } - }, [props.abi]) + }, [props.instance.abi]) useEffect(() => { if (props.instance.address) { @@ -39,6 +41,13 @@ export function UniversalDappUI (props: UdappProps) { } }, [props.instance.address]) + useEffect(() => { + if (props.instance.contractData) { + setInputs(props.instance.contractData.getConstructorInputs()) + setEvmBC(props.instance.contractData.bytecodeObject) + } + }, [props.instance.contractData]) + const sendData = () => { setLlIError('') const fallback = txHelper.getFallbackInterface(contractABI) @@ -227,14 +236,22 @@ export function UniversalDappUI (props: UdappProps) { const isConstant = funcABI.constant !== undefined ? funcABI.constant : false const lookupOnly = funcABI.stateMutability === 'view' || funcABI.stateMutability === 'pure' || isConstant - return runTransaction(lookupOnly, funcABI, valArray, inputsValues)} inputs={props.instance.contractData.getConstructorInputs()} evmBC={props.instance.contractData.bytecodeObject} lookupOnly={lookupOnly} /> + return { + runTransaction(lookupOnly, funcABI, valArray, inputsValues) + }} + inputs={inputs} + evmBC={evmBC} + lookupOnly={lookupOnly} + /> }) }
{ - Object.keys(props.decodedResponse).map((innerkey) => { - return renderData(props.decodedResponse[innerkey], props.decodedResponse, innerkey, innerkey) + Object.keys(props.instance.decodedResponse || {}).map((innerkey) => { + return renderData(props.instance.decodedResponse[innerkey], props.instance.decodedResponse, innerkey, innerkey) }) } diff --git a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts index f799deb707..6031b2177f 100644 --- a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts +++ b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts @@ -66,12 +66,17 @@ export interface RunTabState { gasPrice: string, instances: { instanceList: { - contractData: ContractData, + contractData?: ContractData, address: string, name: string, - decodedResponse?: any + decodedResponse?: any, + abi?: any }[], error: string + }, + recorder: { + pathToScenario: string, + transactionCount: number } } @@ -153,6 +158,10 @@ export const runTabInitialState: RunTabState = { instances: { instanceList: [], error: null + }, + recorder: { + pathToScenario: 'scenario.json', + transactionCount: 0 } } @@ -534,7 +543,7 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A } case 'ADD_INSTANCE': { - const payload: { contractData: ContractData, address: string, name: string } = action.payload + const payload: { contractData: ContractData, address: string, name: string, abi?: any, decodedResponse?: any } = action.payload return { ...state, @@ -552,7 +561,7 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A ...state, instances: { ...state.instances, - instanceList: state.instances.instanceList.filter((instance, index) => index !== payload) + instanceList: state.instances.instanceList.filter((_, index) => index !== payload) } } } @@ -582,6 +591,40 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A } } + case 'SET_PATH_TO_SCENARIO': { + const payload: string = action.payload + + return { + ...state, + recorder: { + ...state.recorder, + pathToScenario: payload + } + } + } + + case 'SET_RECORDER_COUNT': { + const payload: number = action.payload + + return { + ...state, + recorder: { + ...state.recorder, + transactionCount: payload + } + } + } + + case 'CLEAR_RECORDER_COUNT': { + return { + ...state, + recorder: { + ...state.recorder, + transactionCount: 0 + } + } + } + default: return state } diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index c0505a202f..a974b2aab5 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -22,12 +22,15 @@ import { updateMaxFee, updateMaxPriorityFee, updateTxFeeContent, clearInstances, removeInstance, getContext, - runTransactions, loadAddress + runTransactions, loadAddress, + storeScenario, runCurrentScenario, + updateScenarioPath } from './actions' import './css/run-tab.css' import { PublishToStorage } from '@remix-ui/publish-to-storage' import { PassphrasePrompt } from './components/passphrase' import { MainnetPrompt } from './components/mainnet' +import { ScenarioPrompt } from './components/scenario' export function RunTabUI (props: RunTabProps) { const { plugin } = props @@ -161,6 +164,10 @@ export function RunTabUI (props: RunTabProps) { return } + const scenarioPrompt = (message: string) => { + return + } + 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) => { return - + JSX.Element) => void, + runCurrentScenario: (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt, logBuilder: (msg: string) => JSX.Element) => void, + logBuilder: (msg: string) => JSX.Element, + mainnetPrompt: MainnetPrompt, + gasEstimationPrompt: (msg: string) => JSX.Element, + passphrasePrompt: (msg: string) => JSX.Element, + scenarioPrompt: (msg: string) => JSX.Element, + count: number } export interface InstanceContainerProps { instances: { instanceList: { - contractData: ContractData, + contractData?: ContractData, address: string, name: string, - decodedResponse?: any + decodedResponse?: any, + abi?: any }[], error: string }, @@ -263,12 +270,13 @@ export interface MainnetProps { export interface UdappProps { instance: { - contractData: ContractData, + contractData?: ContractData, address: string, - name: string + name: string, + decodedResponse?: any, + abi?: any }, context: 'memory' | 'blockchain', - abi?: FuncABI[], removeInstance: (index: number) => void, index: number, gasEstimationPrompt: (msg: string) => JSX.Element, @@ -288,6 +296,5 @@ export interface UdappProps { mainnetPrompt: MainnetPrompt, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element) => void, - decodedResponse: any, sendValue: string } diff --git a/libs/remix-ui/run-tab/src/lib/types/recorder.d.ts b/libs/remix-ui/run-tab/src/lib/types/recorder.d.ts new file mode 100644 index 0000000000..44f79bda4f --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/types/recorder.d.ts @@ -0,0 +1,15 @@ +export class Recorder { + constructor(blockchain: Blockchain); + event: any; + data: { _listen: boolean, _replay: boolean, journal: any[], _createdContracts: any, _createdContractsReverse: any, _usedAccounts: any, _abis: any, _contractABIReferences: any, _linkReferences: any }; + setListen: (listen) => void; + extractTimestamp: (value) => any; + resolveAddress: (record, accounts, options) => any; + append: (timestamp, record) => any; + getAll: () => void; + clearAll: () => void; + run: (records, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, newContractFn) => void + runScenario: (json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) => void +} +import { Blockchain } from "./blockchain"; + diff --git a/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts b/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts index a1c50e6495..37783c2119 100644 --- a/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts +++ b/libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts @@ -34,8 +34,10 @@ export class RunTab extends ViewPlugin { udappUI: any; renderComponent(): void; onReady(api: any): void; + recorder: Recorder; } import { ViewPlugin } from "@remixproject/engine-web/lib/view"; import { Blockchain } from "./blockchain"; import { RunTabState } from "../reducers/runTab"; +import { Recorder } from "./recorder";