diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index fdb3184231..b0859cbbb6 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -108,33 +108,6 @@ export class RunTab extends ViewPlugin { return this.blockchain.pendingTransactionsCount() } - renderInstanceContainer () { - this.instanceContainer = yo`
` - - const instanceContainerTitle = yo` -
- Deployed Contracts - this.event.trigger('clearInstance', [])} - title="Clear instances list and reset recorder" aria-hidden="true"> - -
` - - this.noInstancesText = yo` - - Currently you have no contract instances to interact with. - ` - - this.event.register('clearInstance', () => { // setFinalContext calls this - this.instanceContainer.innerHTML = '' // clear the instances list - this.instanceContainer.appendChild(instanceContainerTitle) - this.instanceContainer.appendChild(this.noInstancesText) - }) - - this.instanceContainer.appendChild(instanceContainerTitle) - this.instanceContainer.appendChild(this.noInstancesText) - } - renderSettings () { this.settingsUI = new SettingsUI(this.blockchain, this.networkModule) @@ -216,7 +189,6 @@ export class RunTab extends ViewPlugin { render () { return this.el this.udappUI = new UniversalDAppUI(this.blockchain, this.logCallback) - this.renderInstanceContainer() 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) diff --git a/libs/remix-ui/run-tab/src/lib/actions/index.ts b/libs/remix-ui/run-tab/src/lib/actions/index.ts index e683c296a7..48980ce50f 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -3,7 +3,7 @@ import React from 'react' import * as ethJSUtil from 'ethereumjs-util' import Web3 from 'web3' import { addressToString, shortenAddress } from '@remix-ui/helper' -import { addProvider, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setIpfsCheckedState, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent } from './payload' +import { addNewInstance, addProvider, clearAllInstances, displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, fetchContractListSuccess, hidePopUp, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentFile, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setIpfsCheckedState, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, 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' @@ -201,7 +201,7 @@ const setFinalContext = () => { const value = _getProviderDropdownValue() setExecEnv(value) - // this.event.trigger('clearInstance', []) //check cleaIinstance event in run-tab.js + clearInstances() } const _getProviderDropdownValue = (): string => { @@ -303,15 +303,6 @@ const broadcastCompilationResult = (file, source, languageVersion, data) => { dispatch(fetchContractListSuccess(contracts)) dispatch(setCurrentFile(file)) - // this.enableAtAddress(success) - // this.enableContractNames(success) - // this.setInputParamsPlaceHolder() - - // if (success) { - // this.compFails.style.display = 'none' - // } else { - // this.compFails.style.display = 'block' - // } } const loadContractFromAddress = (address, confirmCb, cb) => { @@ -455,13 +446,12 @@ export const createInstance = async ( } const finalCb = (error, contractObject, address) => { - plugin.event.trigger('clearInstance') if (error) { const log = logBuilder(error) return terminalLogger(log) } - plugin.event.trigger('newContractInstanceAdded', [contractObject, address, contractObject.name]) + addInstance({ contractData: contractObject, address, name: contractObject.name }) const data = plugin.compilersArtefacts.getCompilerAbstract(contractObject.contract.file) @@ -538,3 +528,45 @@ export const updateGasPrice = (price: string) => { export const updateTxFeeContent = (content: string) => { dispatch(setTxFeeContent(content)) } + +const addInstance = (instance: { contractData: ContractData, address: string, name: string }) => { + dispatch(addNewInstance(instance)) +} + +export const removeInstance = (index: number) => { + dispatch(removeExistingInstance(index)) +} + +export const clearInstances = () => { + dispatch(clearAllInstances()) +} + +const loadAddress = () => { + clearInstances() + + // let address = this.atAddressButtonInput.value + // if (!ethJSUtil.isValidChecksumAddress(address)) { + // addTooltip(yo` + // + // It seems you are not using a checksumed address. + //
A checksummed address is an address that contains uppercase letters, as specified in EIP-55. + //
Checksummed addresses are meant to help prevent users from sending transactions to the wrong address. + //
`) + // address = ethJSUtil.toChecksumAddress(address) + // } + // this.dropdownLogic.loadContractFromAddress(address, + // (cb) => { + // modalDialogCustom.confirm('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, cb) + // }, + // (error, loadType, abi) => { + // if (error) { + // return modalDialogCustom.alert(error) + // } + // if (loadType === 'abi') { + // return this.event.trigger('newContractABIAdded', [abi, address]) + // } + // var selectedContract = this.getSelectedContract() + // addInstance({ contractData: selectedContract.object, address, name: contractObject.name }) + // } + // ) +} diff --git a/libs/remix-ui/run-tab/src/lib/actions/payload.ts b/libs/remix-ui/run-tab/src/lib/actions/payload.ts index c1c1169bc6..e35f072ea3 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/payload.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/payload.ts @@ -1,3 +1,5 @@ +import { ContractData } from '../types' + export const fetchAccountsListRequest = () => { return { type: 'FETCH_ACCOUNTS_LIST_REQUEST', @@ -217,3 +219,23 @@ export const setTxFeeContent = (content: string) => { payload: content } } + +export const addNewInstance = (instance: { contractData: ContractData, address: string, name: string }) => { + return { + type: 'ADD_INSTANCE', + payload: instance + } +} + +export const removeExistingInstance = (index: number) => { + return { + type: 'REMOVE_INSTANCE', + payload: index + } +} + +export const clearAllInstances = () => { + return { + type: 'CLEAR_INSTANCES' + } +} diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx index 549b1be60f..c1eca2ca52 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -145,134 +145,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) { // }) // } - // async createInstance (selectedContract, args) { - // if (selectedContract.bytecodeObject.length === 0) { - // return modalDialogCustom.alert('This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.') - // } - - // var continueCb = (error, continueTxExecution, cancelCb) => { - // if (error) { - // var msg = typeof error !== 'string' ? error.message : error - // modalDialog('Gas estimation failed', yo`
Gas estimation errored with the following message (see below). - // The transaction execution will likely fail. Do you want to force sending?
- // ${msg} - //
`, - // { - // label: 'Send Transaction', - // fn: () => { - // continueTxExecution() - // } - // }, { - // label: 'Cancel Transaction', - // fn: () => { - // cancelCb() - // } - // }) - // } else { - // continueTxExecution() - // } - // } - - // const self = this - - // var promptCb = (okCb, cancelCb) => { - // modalDialogCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb) - // } - - // var statusCb = (msg) => { - // return this.logCallback(msg) - // } - - // var finalCb = (error, contractObject, address) => { - // self.event.trigger('clearInstance') - - // if (error) { - // return this.logCallback(error) - // } - // self.event.trigger('newContractInstanceAdded', [contractObject, address, contractObject.name]) - - // const data = self.runView.compilersArtefacts.getCompilerAbstract(contractObject.contract.file) - // self.runView.compilersArtefacts.addResolvedContract(helper.addressToString(address), data) - // if (self.ipfsCheckedState) { - // _paq.push(['trackEvent', 'udapp', 'DeployAndPublish', this.networkName]) - // publishToStorage('ipfs', self.runView.fileProvider, self.runView.fileManager, selectedContract) - // } else { - // _paq.push(['trackEvent', 'udapp', 'DeployOnly', this.networkName]) - // } - // } - - // let contractMetadata - // try { - // contractMetadata = await this.runView.call('compilerMetadata', 'deployMetadataOf', selectedContract.name, selectedContract.contract.file) - // } catch (error) { - // return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) - // } - - // const compilerContracts = this.dropdownLogic.getCompilerContracts() - // const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog) - - // if (selectedContract.isOverSizeLimit()) { - // return modalDialog('Contract code size over limit', yo`
Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails.
- // More info: eip-170 - //
`, - // { - // label: 'Force Send', - // fn: () => { - // this.deployContract(selectedContract, args, contractMetadata, compilerContracts, { continueCb, promptCb, statusCb, finalCb }, confirmationCb) - // } - // }, { - // label: 'Cancel', - // fn: () => { - // this.logCallback(`creation of ${selectedContract.name} canceled by user.`) - // } - // }) - // } - // this.deployContract(selectedContract, args, contractMetadata, compilerContracts, { continueCb, promptCb, statusCb, finalCb }, confirmationCb) - // } - - // deployContract (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { - // _paq.push(['trackEvent', 'udapp', 'DeployContractTo', this.networkName]) - // const { statusCb } = callbacks - // if (!contractMetadata || (contractMetadata && contractMetadata.autoDeployLib)) { - // return this.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')}`) - // this.blockchain.deployContractWithLibrary(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) - // } - - // getConfirmationCb (modalDialog, confirmDialog) { - // // this code is the same as in recorder.js. TODO need to be refactored out - // const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => { - // if (network.name !== 'Main') { - // return continueTxExecution(null) - // } - // const amount = this.blockchain.fromWei(tx.value, true, 'ether') - // const content = confirmDialog(tx, network, amount, gasEstimation, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain)) - - // modalDialog('Confirm transaction', content, - // { - // label: 'Confirm', - // fn: () => { - // this.blockchain.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - // // TODO: check if this is check is still valid given the refactor - // if (!content.gasPriceStatus) { - // cancelCb('Given transaction fee is not correct') - // } else { - // continueTxExecution(content.txFee) - // } - // } - // }, { - // label: 'Cancel', - // fn: () => { - // return cancelCb('Transaction canceled by user.') - // } - // } - // ) - // } - - // return confirmationCb - // } - const atAddressChanged = (event) => { const value = event.target.value @@ -291,33 +163,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) { const loadFromAddress = () => { // trigger dispatchLoadAddress - // this.event.trigger('clearInstance') - - // let address = this.atAddressButtonInput.value - // if (!ethJSUtil.isValidChecksumAddress(address)) { - // addTooltip(yo` - // - // It seems you are not using a checksumed address. - //
A checksummed address is an address that contains uppercase letters, as specified in EIP-55. - //
Checksummed addresses are meant to help prevent users from sending transactions to the wrong address. - //
`) - // address = ethJSUtil.toChecksumAddress(address) - // } - // this.dropdownLogic.loadContractFromAddress(address, - // (cb) => { - // modalDialogCustom.confirm('At Address', `Do you really want to interact with ${address} using the current ABI definition?`, cb) - // }, - // (error, loadType, abi) => { - // if (error) { - // return modalDialogCustom.alert(error) - // } - // if (loadType === 'abi') { - // return this.event.trigger('newContractABIAdded', [abi, address]) - // } - // var selectedContract = this.getSelectedContract() - // this.event.trigger('newContractInstanceAdded', [selectedContract.object, address, this.selectContractNames.value]) - // } - // ) } const handleCheckedIPFS = () => { @@ -427,7 +272,5 @@ export function ContractDropdownUI (props: ContractDropdownProps) { - // this.selectContractNames.addEventListener('change', this.setInputParamsPlaceHolder.bind(this)) - // this.setInputParamsPlaceHolder() ) } diff --git a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx index 1f3cba0406..d7a155c4ea 100644 --- a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx @@ -3,10 +3,10 @@ import React from 'react' import { InstanceContainerProps } from '../types' export function InstanceContainerUI (props: InstanceContainerProps) { + const { instanceList } = props.instances + const clearInstance = () => { - // this.instanceContainer.innerHTML = '' // clear the instances list - // this.instanceContainer.appendChild(instanceContainerTitle) - // this.instanceContainer.appendChild(this.noInstancesText) + props.clearInstances() } return ( @@ -14,13 +14,18 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
Deployed Contracts - + { instanceList.length > 0 + ? : null + }
- - Currently you have no contract instances to interact with. - + { instanceList.length > 0 + ?
+ : + Currently you have no contract instances to interact with. + + } ) } diff --git a/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx new file mode 100644 index 0000000000..49cd33aebd --- /dev/null +++ b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx @@ -0,0 +1,190 @@ +// eslint-disable-next-line no-use-before-define +import React, { useState } from 'react' +import { shortenAddress } from 'apps/remix-ide/src/lib/helper' +import { UdappProps } from '../types' + +export function UniversalDappUI (props: UdappProps) { + const [toggleExpander, setToggleExpander] = useState(false) + // const self = this + // address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex') + // address = ethJSUtil.toChecksumAddress(address) + // var instance = yo`
` + // const context = this.blockchain.context() + + // var shortAddress = helper.shortenAddress(address) + // var title = yo` + //
+ // + //
+ //
+ // + // ${contractName} at ${shortAddress} (${context}) + // + //
+ //
+ // + //
+ //
+ //
+ // ` + + // var close = yo` + // ` + // title.querySelector('.btn-group').appendChild(close) + + // var contractActionsWrapper = yo` + //
+ //
+ // ` + + // function remove () { + // instance.remove() + // // @TODO perhaps add a callack here to warn the caller that the instance has been removed + // } + + // function toggleClass (e) { + // $(instance).toggleClass(`${css.hidesub} bg-light`) + // // e.currentTarget.querySelector('i') + // e.currentTarget.querySelector('i').classList.toggle('fa-angle-right') + // e.currentTarget.querySelector('i').classList.toggle('fa-angle-down') + // } + + // instance.appendChild(title) + // instance.appendChild(contractActionsWrapper) + + // $.each(contractABI, (i, funABI) => { + // if (funABI.type !== 'function') { + // return + // } + // // @todo getData cannot be used with overloaded functions + // contractActionsWrapper.appendChild(this.getCallButton({ + // funABI: funABI, + // address: address, + // contractABI: contractABI, + // contractName: contractName, + // contract + // })) + // }) + + // const calldataInput = yo` + // + // ` + // const llIError = yo` + // + // ` + // // constract LLInteractions elements + // const lowLevelInteracions = yo` + //
+ //
+ //
+ // Low level interactions + //
+ // + // + // + //
+ //
+ // + //
+ // ${calldataInput} + // + //
+ //
+ //
+ // ${llIError} + //
+ //
+ // ` + + // function sendData () { + // function setLLIError (text) { + // llIError.innerText = text + // } + + // setLLIError('') + // const fallback = txHelper.getFallbackInterface(contractABI) + // const receive = txHelper.getReceiveInterface(contractABI) + // const args = { + // funABI: fallback || receive, + // address: address, + // contractName: contractName, + // contractABI: contractABI + // } + // const amount = document.querySelector('#value').value + // if (amount !== '0') { + // // check for numeric and receive/fallback + // if (!helper.isNumeric(amount)) { + // return setLLIError('Value to send should be a number') + // } else if (!receive && !(fallback && fallback.stateMutability === 'payable')) { + // return setLLIError("In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function") + // } + // } + // let calldata = calldataInput.value + // if (calldata) { + // if (calldata.length < 4 && helper.is0XPrefixed(calldata)) { + // return setLLIError('The calldata should be a valid hexadecimal value with size of at least one byte.') + // } else { + // if (helper.is0XPrefixed(calldata)) { + // calldata = calldata.substr(2, calldata.length) + // } + // if (!helper.isHexadecimal(calldata)) { + // return setLLIError('The calldata should be a valid hexadecimal value.') + // } + // } + // if (!fallback) { + // return setLLIError("'Fallback' function is not defined") + // } + // } + + // if (!receive && !fallback) return setLLIError('Both \'receive\' and \'fallback\' functions are not defined') + + // // we have to put the right function ABI: + // // if receive is defined and that there is no calldata => receive function is called + // // if fallback is defined => fallback function is called + // if (receive && !calldata) args.funABI = receive + // else if (fallback) args.funABI = fallback + + // if (!args.funABI) return setLLIError('Please define a \'Fallback\' function to send calldata and a either \'Receive\' or payable \'Fallback\' to send ethers') + // self.runTransaction(false, args, null, calldataInput.value, null) + // } + + // contractActionsWrapper.appendChild(lowLevelInteracions) + // return instance + + const toggleClass = () => { + setToggleExpander(!toggleExpander) + } + + return ( +
+
+ +
+
+ + {props.instance.name} at {shortenAddress(props.instance)} ({context}) + +
+
+ +
+
+
+
+ ) +} diff --git a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts index edd353bff2..0f0a6473a9 100644 --- a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts +++ b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts @@ -1,3 +1,5 @@ +import { ContractData } from '../types' + interface Action { type: string payload: any @@ -61,7 +63,15 @@ export interface RunTabState { maxPriorityFee: string, baseFeePerGas: string, txFeeContent: string, - gasPrice: string + gasPrice: string, + instances: { + instanceList: { + contractData: ContractData, + address: string, + name: string + }[], + error: string + } } export const runTabInitialState: RunTabState = { @@ -138,7 +148,11 @@ export const runTabInitialState: RunTabState = { maxPriorityFee: '1', baseFeePerGas: '', txFeeContent: '', - gasPrice: '' + gasPrice: '', + instances: { + instanceList: [], + error: null + } } export const runTabReducer = (state: RunTabState = runTabInitialState, action: Action) => { @@ -518,6 +532,40 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A } } + case 'ADD_INSTANCE': { + const payload: { contractData: ContractData, address: string, name: string } = action.payload + + return { + ...state, + instances: { + ...state.instances, + instanceList: [...state.instances.instanceList, payload] + } + } + } + + case 'REMOVE_INSTANCE': { + const payload: number = action.payload + + return { + ...state, + instances: { + ...state.instances, + instanceList: state.instances.instanceList.filter((instance, index) => index !== payload) + } + } + } + + case 'CLEAR_INSTANCES': { + return { + ...state, + instances: { + instanceList: [], + error: null + } + } + } + default: return state } diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index c7c8c3938d..c7b4bbcf1b 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -20,7 +20,7 @@ import { updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, - updateTxFeeContent + updateTxFeeContent, clearInstances } from './actions' import './css/run-tab.css' import { PublishToStorage } from '@remix-ui/publish-to-storage' @@ -191,7 +191,7 @@ export function RunTabUI (props: RunTabProps) { maxPriorityFee={runTab.maxPriorityFee} /> - + diff --git a/libs/remix-ui/run-tab/src/lib/types/index.ts b/libs/remix-ui/run-tab/src/lib/types/index.ts index 787e11c66d..325bfa334f 100644 --- a/libs/remix-ui/run-tab/src/lib/types/index.ts +++ b/libs/remix-ui/run-tab/src/lib/types/index.ts @@ -182,7 +182,15 @@ export interface RecorderProps { } export interface InstanceContainerProps { - + instances: { + instanceList: { + contractData: ContractData, + address: string, + name: string + }[], + error: string + }, + clearInstances: () => void } export interface Modal { @@ -229,3 +237,11 @@ export interface MainnetProps { maxFee: string, maxPriorityFee: string } + +export interface UdappProps { + instance: { + contractData: ContractData, + address: string, + name: string + } +}