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 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 // check in the AST if it's an upgradable contract
if (ast.nodes.find(node => node.absolutePath === UUPS)) { if (ast.nodes.find(node => node.absolutePath === UUPS)) {
this.kind = 'UUPS' const inputs = Object.keys(contracts).map(name => {
Object.keys(contracts).map(name => {
const abi = contracts[name].abi 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 = '') { 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 // eslint-disable-next-line no-unused-vars
import React from 'react' 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 { RunTab } from '../types/run-tab'
import { CompilerAbstract } from '@remix-project/remix-solidity' import { resetAndInit, setupEvents } from './events'
import * as remixLib from '@remix-project/remix-lib' import { createNewBlockchainAccount, fillAccountsList, setExecutionContext, signMessageWithAddress } from './account'
import { DeployMode, MainnetPrompt } from '../types' import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, setNetworkNameFromProvider, setPassphrasePrompt, setSendTransactionValue, setUnit, updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath, updateTxFeeContent } from './actions'
import { ContractData, FuncABI, } from '@remix-project/core-plugin' import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions } from './deploy'
import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts' 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 { declare global {
interface Window { interface Window {
_paq: any _paq: any
@ -19,718 +17,44 @@ declare global {
} }
const _paq = window._paq = window._paq || [] //eslint-disable-line const _paq = window._paq = window._paq || [] //eslint-disable-line
const txHelper = remixLib.execution.txHelper
let plugin: RunTab, dispatch: React.Dispatch<any> let plugin: RunTab, dispatch: React.Dispatch<any>
export const initRunTab = (udapp: RunTab) => async (reducerDispatch: React.Dispatch<any>) => { export const initRunTab = (udapp: RunTab) => async (reducerDispatch: React.Dispatch<any>) => {
plugin = udapp plugin = udapp
dispatch = reducerDispatch dispatch = reducerDispatch
resetAndInit() resetAndInit(plugin)
setupEvents() setupEvents(plugin, dispatch)
setInterval(() => { setInterval(() => {
fillAccountsList() fillAccountsList(plugin, dispatch)
}, 1000) }, 1000)
} }
const setupEvents = () => { export const setAccountAddress = (account: string) => setAccount(dispatch, account)
plugin.blockchain.events.on('newTransaction', (tx, receipt) => { export const setUnitValue = (unit: 'ether' | 'finney' | 'gwei' | 'wei') => setUnit(dispatch, unit)
plugin.emit('newTransaction', tx, receipt) 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)
plugin.blockchain.event.register('transactionExecuted', (error, from, to, data, lookupOnly, txResult) => { export const createNewAddress = (cbMessage: JSX.Element) => createNewBlockchainAccount(plugin, dispatch, cbMessage)
if (!lookupOnly) dispatch(setSendValue('0')) export const setPassphraseModal = (passphrase: string) => setPassphrasePrompt(dispatch, passphrase)
if (error) return export const setMatchPassphraseModal = (passphrase: string) => setMatchPassphrasePrompt(dispatch, passphrase)
updateAccountBalances() 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)
plugin.blockchain.event.register('contextChanged', (context, silent) => { export const setSendValue = (value: string) => setSendTransactionValue(dispatch, value)
setFinalContext() export const setBaseFeePerGas = (baseFee: string) => updateBaseFeePerGas(dispatch, baseFee)
}) export const setConfirmSettings = (confirmation: boolean) => updateConfirmSettings(dispatch, confirmation)
export const setGasPrice = (price: string) => updateGasPrice(dispatch, price)
plugin.blockchain.event.register('networkStatus', ({ error, network }) => { export const setGasPriceStatus = (status: boolean) => updateGasPriceStatus(dispatch, status)
if (error) { export const setMaxFee = (fee: string) => updateMaxFee(dispatch, fee)
const netUI = 'can\'t detect network ' export const setMaxPriorityFee = (fee: string) => updateMaxPriorityFee(dispatch, fee)
setNetworkNameFromProvider(netUI) export const setTxFeeContent = (content: string) => updateTxFeeContent(dispatch, content)
export const removeInstances = () => clearInstances(dispatch)
return export const removeSingleInstance = (index: number) => removeInstance(dispatch, index)
} export const getExecutionContext = () => getContext(plugin)
const networkProvider = plugin.networkModule.getNetworkProvider.bind(plugin.networkModule) 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)
const netUI = (networkProvider() !== 'vm') ? `${network.name} (${network.id || '-'}) network` : 'VM' 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)
setNetworkNameFromProvider(netUI) 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)
plugin.blockchain.event.register('addProvider', provider => addExternalProvider(provider)) export const setNetworkName = (networkName: string) => setNetworkNameFromProvider(dispatch, networkName)
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)
}
}
})
}

@ -1,6 +1,6 @@
import { ContractList } from '../reducers/runTab' import { ContractList } from '../reducers/runTab'
import { ContractData } from '@remix-project/core-plugin' 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' import { DeployMode, DeployOptions } from '../types'
export const fetchAccountsListRequest = () => { export const fetchAccountsListRequest = () => {
@ -300,3 +300,10 @@ export const setDeployOptions = (deployOptions: DeployOptions[]) => {
type: SET_DEPLOY_OPTIONS 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 ADD_DEPLOY_OPTION = 'ADD_DEPLOY_OPTION'
export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION' export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION'
export const SET_DEPLOY_OPTIONS = 'SET_DEPLOY_OPTIONS' 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 { CompilerAbstract } from '@remix-project/remix-solidity-ts'
import { ContractData } from '@remix-project/core-plugin' import { ContractData } from '@remix-project/core-plugin'
import { DeployMode, DeployOptions } from '../types' 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 { interface Action {
type: string type: string
payload: any payload: any
@ -66,6 +66,7 @@ export interface RunTabState {
}, },
loadType: 'abi' | 'sol' | 'other' loadType: 'abi' | 'sol' | 'other'
currentFile: string, currentFile: string,
currentContract: string,
compilationCount: number, compilationCount: number,
isRequesting: boolean, isRequesting: boolean,
isSuccessful: boolean, isSuccessful: boolean,
@ -159,6 +160,7 @@ export const runTabInitialState: RunTabState = {
contractList: {}, contractList: {},
loadType: 'other', loadType: 'other',
currentFile: '', currentFile: '',
currentContract: '',
compilationCount: 0, compilationCount: 0,
isRequesting: false, isRequesting: false,
isSuccessful: 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: { case SET_LOAD_TYPE: {
const payload: 'abi' | 'sol' | 'other' = action.payload 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 { ContractData } from '@remix-project/core-plugin'
import { runTabInitialState, runTabReducer } from './reducers/runTab' import { runTabInitialState, runTabReducer } from './reducers/runTab'
import { import {
initRunTab, setAccount, initRunTab, setAccountAddress,
setUnit, setGasFee, setUnitValue, setGasFeeAmount,
setExecutionContext, setWeb3Endpoint, setExecutionEnvironment,
clearPopUp, createNewBlockchainAccount, hideToaster, createNewAddress,
setPassphrasePrompt, setMatchPassphrasePrompt, setPassphraseModal, setMatchPassphraseModal,
signMessageWithAddress, getSelectedContract, signMessage, fetchSelectedContract,
createInstance, setSendTransactionValue, createNewInstance, setSendValue,
updateBaseFeePerGas, updateConfirmSettings, setBaseFeePerGas, setConfirmSettings,
updateGasPrice, updateGasPriceStatus, setGasPrice, setGasPriceStatus,
updateMaxFee, updateMaxPriorityFee, setMaxFee, setMaxPriorityFee,
updateTxFeeContent, clearInstances, setTxFeeContent, removeInstances,
removeInstance, getContext, removeSingleInstance, getExecutionContext,
runTransactions, loadAddress, executeTransactions, loadFromAddress,
storeScenario, runCurrentScenario, storeNewScenario, runScenario,
updateScenarioPath, getFuncABIInputs, setScenarioPath, getFuncABIValues,
setNetworkNameFromProvider setNetworkName
} from './actions' } from './actions'
import './css/run-tab.css' import './css/run-tab.css'
import { PublishToStorage } from '@remix-ui/publish-to-storage' import { PublishToStorage } from '@remix-ui/publish-to-storage'
@ -134,7 +134,7 @@ export function RunTabUI (props: RunTabProps) {
const handleToaster = () => { const handleToaster = () => {
setFocusToaster('') setFocusToaster('')
clearPopUp() hideToaster()
} }
const toast = (toasterMsg: string) => { const toast = (toasterMsg: string) => {
@ -171,11 +171,11 @@ export function RunTabUI (props: RunTabProps) {
} }
const passphrasePrompt = (message: string) => { 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) => { 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) => { 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} amount={amount}
gasEstimation={gasEstimation} gasEstimation={gasEstimation}
setNewGasPrice={gasFees} setNewGasPrice={gasFees}
updateBaseFeePerGas={updateBaseFeePerGas} updateBaseFeePerGas={setBaseFeePerGas}
updateConfirmSettings={updateConfirmSettings} updateConfirmSettings={setConfirmSettings}
updateGasPrice={updateGasPrice} updateGasPrice={setGasPrice}
updateGasPriceStatus={updateGasPriceStatus} updateGasPriceStatus={setGasPriceStatus}
updateMaxFee={updateMaxFee} updateMaxFee={setMaxFee}
updateMaxPriorityFee={updateMaxPriorityFee} updateMaxPriorityFee={setMaxPriorityFee}
setTxFeeContent={updateTxFeeContent} setTxFeeContent={setTxFeeContent}
txFeeContent={runTab.txFeeContent} txFeeContent={runTab.txFeeContent}
maxFee={runTab.maxFee} maxFee={runTab.maxFee}
maxPriorityFee={runTab.maxPriorityFee} maxPriorityFee={runTab.maxPriorityFee}
@ -207,33 +207,32 @@ export function RunTabUI (props: RunTabProps) {
networkName={runTab.networkName} networkName={runTab.networkName}
personalMode={runTab.personalMode} personalMode={runTab.personalMode}
selectExEnv={runTab.selectExEnv} selectExEnv={runTab.selectExEnv}
setWeb3Endpoint={setWeb3Endpoint}
accounts={runTab.accounts} accounts={runTab.accounts}
setAccount={setAccount} setAccount={setAccountAddress}
setUnit={setUnit} setUnit={setUnitValue}
sendValue={runTab.sendValue} sendValue={runTab.sendValue}
setSendValue={setSendTransactionValue} setSendValue={setSendValue}
sendUnit={runTab.sendUnit} sendUnit={runTab.sendUnit}
gasLimit={runTab.gasLimit} gasLimit={runTab.gasLimit}
setGasFee={setGasFee} setGasFee={setGasFeeAmount}
providers={runTab.providers} providers={runTab.providers}
setExecutionContext={setExecutionContext} setExecutionContext={setExecutionEnvironment}
createNewBlockchainAccount={createNewBlockchainAccount} createNewBlockchainAccount={createNewAddress}
setPassphrase={setPassphrasePrompt} setPassphrase={setPassphraseModal}
setMatchPassphrase={setMatchPassphrasePrompt} setMatchPassphrase={setMatchPassphraseModal}
modal={modal} modal={modal}
tooltip={toast} tooltip={toast}
signMessageWithAddress={signMessageWithAddress} signMessageWithAddress={signMessage}
passphrase={runTab.passphrase} passphrase={runTab.passphrase}
/> />
<ContractDropdownUI <ContractDropdownUI
exEnvironment={runTab.selectExEnv} exEnvironment={runTab.selectExEnv}
contracts={runTab.contracts} contracts={runTab.contracts}
getSelectedContract={getSelectedContract} getSelectedContract={fetchSelectedContract}
modal={modal} modal={modal}
passphrase={runTab.passphrase} passphrase={runTab.passphrase}
setPassphrase={setPassphrasePrompt} setPassphrase={setPassphraseModal}
createInstance={createInstance} createInstance={createNewInstance}
ipfsCheckedState={runTab.ipfsChecked} ipfsCheckedState={runTab.ipfsChecked}
setIpfsCheckedState={setCheckIpfs} setIpfsCheckedState={setCheckIpfs}
publishToStorage={publishToStorage} publishToStorage={publishToStorage}
@ -242,9 +241,9 @@ export function RunTabUI (props: RunTabProps) {
passphrasePrompt={passphrasePrompt} passphrasePrompt={passphrasePrompt}
mainnetPrompt={mainnetPrompt} mainnetPrompt={mainnetPrompt}
tooltip={toast} tooltip={toast}
loadAddress={loadAddress} loadAddress={loadFromAddress}
networkName={runTab.networkName} networkName={runTab.networkName}
setNetworkName={setNetworkNameFromProvider} setNetworkName={setNetworkName}
deployOptions={runTab.deployOptions} deployOptions={runTab.deployOptions}
/> />
<RecorderUI <RecorderUI
@ -252,23 +251,23 @@ export function RunTabUI (props: RunTabProps) {
logBuilder={logBuilder} logBuilder={logBuilder}
passphrasePrompt={passphrasePrompt} passphrasePrompt={passphrasePrompt}
mainnetPrompt={mainnetPrompt} mainnetPrompt={mainnetPrompt}
storeScenario={storeScenario} storeScenario={storeNewScenario}
runCurrentScenario={runCurrentScenario} runCurrentScenario={runScenario}
scenarioPrompt={scenarioPrompt} scenarioPrompt={scenarioPrompt}
count={runTab.recorder.transactionCount} count={runTab.recorder.transactionCount}
/> />
<InstanceContainerUI <InstanceContainerUI
instances={runTab.instances} instances={runTab.instances}
clearInstances={clearInstances} clearInstances={removeInstances}
removeInstance={removeInstance} removeInstance={removeSingleInstance}
getContext={getContext} getContext={getExecutionContext}
gasEstimationPrompt={gasEstimationPrompt} gasEstimationPrompt={gasEstimationPrompt}
logBuilder={logBuilder} logBuilder={logBuilder}
passphrasePrompt={passphrasePrompt} passphrasePrompt={passphrasePrompt}
mainnetPrompt={mainnetPrompt} mainnetPrompt={mainnetPrompt}
runTransactions={runTransactions} runTransactions={executeTransactions}
sendValue={runTab.sendValue} sendValue={runTab.sendValue}
getFuncABIInputs={getFuncABIInputs} getFuncABIInputs={getFuncABIValues}
/> />
</div> </div>
</div> </div>

@ -21,7 +21,6 @@ export interface SettingsProps {
sendUnit: string, sendUnit: string,
gasLimit: number, gasLimit: number,
setGasFee: (value: number) => void, setGasFee: (value: number) => void,
setWeb3Endpoint: (endpoint: string) => void,
personalMode: boolean, personalMode: boolean,
networkName: string, networkName: string,
providers: { providers: {
@ -225,7 +224,22 @@ export interface Modal {
export type DeployMode = 'Deploy with Proxy' export type DeployMode = 'Deploy with Proxy'
export interface DeployOptions { export interface DeployOptions {
title: DeployMode, 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 { export interface ContractGUIProps {

Loading…
Cancel
Save