Udapp recorder module

pull/5370/head
David Disu 3 years ago committed by yann300
parent 74e47426cb
commit 9c74c56ff7
  1. 103
      apps/remix-ide/src/app/udapp/run-tab.js
  2. 116
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  3. 22
      libs/remix-ui/run-tab/src/lib/actions/payload.ts
  4. 21
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  5. 2
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  6. 17
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  7. 22
      libs/remix-ui/run-tab/src/lib/components/scenario.tsx
  8. 24
      libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx
  9. 29
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  10. 51
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  11. 20
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  12. 23
      libs/remix-ui/run-tab/src/lib/types/index.ts
  13. 15
      libs/remix-ui/run-tab/src/lib/types/recorder.d.ts
  14. 2
      libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts

@ -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
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, '<at 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`<span>0</span>`
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`
<div class="d-flex flex-column">
<div class="ml-2 badge badge-pill badge-primary" title="The number of recorded transactions">${this.recorderCount}</div>
</div>`
const expandedView = yo`
<div class="d-flex flex-column">
<div class="${css.recorderDescription} mt-2">
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.
</div>
<div class="${css.transactionActions}">
${this.recorderInterface.recordButton}
${this.recorderInterface.runButton}
</div>
</div>
</div>`
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 () {

@ -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))
}

@ -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'
}
}

@ -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

@ -24,6 +24,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
{ instanceList.length > 0
? <div> { props.instances.instanceList.map((instance, index) => {
return <UniversalDappUI
key={index}
instance={instance}
context={props.getContext()}
removeInstance={props.removeInstance}
@ -33,7 +34,6 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
passphrasePrompt={props.passphrasePrompt}
mainnetPrompt={props.mainnetPrompt}
runTransactions={props.runTransactions}
decodedResponse={instance.decodedResponse || {}}
sendValue={props.sendValue}
/>
}) }

@ -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 (
<div className="udapp_cardContainer list-group-item border-0">
<TreeView>
<TreeViewItem label={card('Transactions recorded', 0)} showIcon={false} labelClass="ml-n1">
<TreeViewItem label={card('Transactions recorded', props.count)} showIcon={false} labelClass="ml-n1">
<div className="d-flex flex-column">
<div className="udapp_recorderDescription mt-2">
All transactions (deployed contracts and function executions) in this environment can be saved and replayed in

@ -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 (
<div> { props.message }
<div>
<input id="prompt_text" type="text" name='prompt_text' className="form-control" style={{ width: '100%' }} onInput={handleScenarioPath} data-id='modalDialogCustomPromptText' defaultValue={props.defaultValue} />
</div>
</div>
)
}

@ -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 (
<div className="udapp_settings">
<EnvironmentUI setWeb3Endpoint={props.setWeb3Endpoint} selectedEnv={props.selectExEnv} providers={props.providers} setExecutionContext={props.setExecutionContext} externalEndpoint={props.externalEndpoint} />

@ -18,16 +18,18 @@ export function UniversalDappUI (props: UdappProps) {
const [expandPath, setExpandPath] = useState<string[]>([])
const [llIError, setLlIError] = useState<string>('')
const [calldataValue, setCalldataValue] = useState<string>('')
const [inputs, setInputs] = useState<string>(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 <ContractGUI funcABI={funcABI} clickCallBack={(valArray: { name: string, type: string }[], inputsValues: string) => runTransaction(lookupOnly, funcABI, valArray, inputsValues)} inputs={props.instance.contractData.getConstructorInputs()} evmBC={props.instance.contractData.bytecodeObject} lookupOnly={lookupOnly} />
return <ContractGUI
funcABI={funcABI}
clickCallBack={(valArray: { name: string, type: string }[], inputsValues: string) => {
runTransaction(lookupOnly, funcABI, valArray, inputsValues)
}}
inputs={inputs}
evmBC={evmBC}
lookupOnly={lookupOnly}
/>
})
}
<div className="udapp_value">
<TreeView id="treeView">
{
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)
})
}
</TreeView>

@ -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
}

@ -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 <PassphrasePrompt message={message} setPassphrase={setPassphrasePrompt} defaultValue={runTab.passphrase} />
}
const scenarioPrompt = (message: string) => {
return <ScenarioPrompt message={message} setScenarioPath={updateScenarioPath} defaultValue={runTab.recorder.pathToScenario} />
}
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 <MainnetPrompt
init={determineGasPrice}
@ -227,7 +234,16 @@ export function RunTabUI (props: RunTabProps) {
tooltip={toast}
loadAddress={loadAddress}
/>
<RecorderUI />
<RecorderUI
gasEstimationPrompt={gasEstimationPrompt}
logBuilder={logBuilder}
passphrasePrompt={passphrasePrompt}
mainnetPrompt={mainnetPrompt}
storeScenario={storeScenario}
runCurrentScenario={runCurrentScenario}
scenarioPrompt={scenarioPrompt}
count={runTab.recorder.transactionCount}
/>
<InstanceContainerUI
instances={runTab.instances}
clearInstances={clearInstances}

@ -1,4 +1,3 @@
import { type } from 'os'
import { RunTab } from './run-tab'
export interface RunTabProps {
plugin: RunTab
@ -186,16 +185,24 @@ export interface ContractDropdownProps {
}
export interface RecorderProps {
storeScenario: (prompt: (msg: string) => 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
}

@ -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";

@ -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";

Loading…
Cancel
Save