diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index 73d5fd6c2f..297f2996c5 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -234,4 +234,12 @@ export class RunTab extends ViewPlugin { onReady (api) { this.REACT_API = api } + + writeFile (fileName, content) { + return this.call('fileManager', 'writeFile', fileName, content) + } + + readFile (fileName) { + return this.call('fileManager', 'readFile', fileName) + } } diff --git a/libs/remix-ui/publish-to-storage/src/lib/types/index.ts b/libs/remix-ui/publish-to-storage/src/lib/types/index.ts index 53a351baee..7995d5a538 100644 --- a/libs/remix-ui/publish-to-storage/src/lib/types/index.ts +++ b/libs/remix-ui/publish-to-storage/src/lib/types/index.ts @@ -1,6 +1,6 @@ export interface RemixUiPublishToStorageProps { api: any, - storage: string, + storage: 'swarm' | 'ipfs', contract: any, resetStorage: () => void } 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 76a03d15d6..af87629b30 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -3,10 +3,11 @@ 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, setCurrentFile, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setLoadType, setMatchPassphrase, setNetworkName, setPassphrase, setSelectedAccount, setSendUnit, setSendValue } from './payload' +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 { RunTab } from '../types/run-tab' import { CompilerAbstract } from '@remix-project/remix-solidity' import * as remixLib from '@remix-project/remix-lib' +import { ContractData } from '../types' declare global { interface Window { _paq: any @@ -386,8 +387,33 @@ const terminalLogger = (view: JSX.Element) => { plugin.call('terminal', 'logHtml', view) } +const getConfirmationCb = () => { + // 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 = plugin.blockchain.fromWei(tx.value, true, 'ether') + const content = confirmDialog(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', 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) + } + }, () => { + return cancelCb('Transaction canceled by user.') + })) + } + + return confirmationCb +} + // eslint-disable-next-line no-undef -export const createInstance = (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, logBuilder: (msg: string) => JSX.Element) => { +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) => { const continueCb = (error, continueTxExecution, cancelCb) => { if (error) { const msg = typeof error !== 'string' ? error.message : error @@ -424,23 +450,23 @@ export const createInstance = (gasEstimationPrompt: (msg: string) => JSX.Element const data = plugin.compilersArtefacts.getCompilerAbstract(contractObject.contract.file) plugin.compilersArtefacts.addResolvedContract(addressToString(address), data) - if (self.ipfsCheckedState) { - _paq.push(['trackEvent', 'udapp', 'DeployAndPublish', this.networkName]) - publishToStorage('ipfs', self.runView.fileProvider, self.runView.fileManager, selectedContract) + 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]) } } - // 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}`) - // } + 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 = this.dropdownLogic.getCompilerContracts() - // const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog) + const compilerContracts = getCompilerContracts() + const confirmationCb = getConfirmationCb(mainnetPrompt) // 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.
@@ -460,3 +486,45 @@ export const createInstance = (gasEstimationPrompt: (msg: string) => JSX.Element // } // this.deployContract(selectedContract, args, contractMetadata, compilerContracts, { continueCb, promptCb, statusCb, finalCb }, confirmationCb) } + +const deployContract = (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) => { + _paq.push(['trackEvent', 'udapp', 'DeployContractTo', this.networkName + '_' + this.networkId]) + 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) +} + +export const setCheckIpfs = (value: boolean) => { + dispatch(setIpfsCheckedState(value)) +} + +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)) +} 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 302cbdebcf..56cfd0d022 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/payload.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/payload.ts @@ -169,3 +169,52 @@ export const setIpfsCheckedState = (state: boolean) => { payload: state } } + +export const setGasPriceStatus = (status: boolean) => { + return { + type: 'SET_GAS_PRICE_STATUS', + payload: status + } +} + +export const setConfirmSettings = (confirmation: boolean) => { + return { + type: 'SET_CONFIRM_SETTINGS', + payload: confirmation + } +} + +export const setMaxFee = (fee: string) => { + return { + type: 'SET_MAX_FEE', + payload: fee + } +} + +export const setMaxPriorityFee = (fee: string) => { + return { + type: 'SET_MAX_PRIORITY_FEE', + payload: fee + } +} + +export const setBaseFeePerGas = (baseFee: string) => { + return { + type: 'SET_BASE_FEE_PER_GAS', + payload: baseFee + } +} + +export const setGasPrice = (price: string) => { + return { + type: 'SET_GAS_PRICE', + payload: price + } +} + +export const setTxFeeContent = (content: string) => { + return { + type: 'SET_TX_FEE_CONTENT', + payload: content + } +} 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 867c298802..d55b94c180 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -1,6 +1,6 @@ // eslint-disable-next-line no-use-before-define import React, { useEffect, useState } from 'react' -import { ContractDropdownProps } from '../types' +import { ContractData, ContractDropdownProps } from '../types' import * as ethJSUtil from 'ethereumjs-util' import { ContractGUI } from './contractGUI' import { PassphrasePrompt } from './passphrase' @@ -14,7 +14,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) { display: '', content: '' }) - const [ipfsCheckedState, setIpfsCheckedState] = useState(false) const [atAddressOptions, setAtAddressOptions] = useState<{title: string, disabled: boolean}>({ title: 'address of contract', disabled: true @@ -26,19 +25,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { }) const [selectedContract, setSelectedContract] = useState('') const [compFails, setCompFails] = useState<'none' | 'block'>('none') - const [loadedContractData, setLoadedContractData] = useState<{ - name: string, - contract: string, - compiler: any, - abi: any, - bytecodeObject: any, - bytecodeLinkReferences: any, - object: any, - deployedBytecode: any, - getConstructorInterface: () => any, - getConstructorInputs: () => any, - isOverSizeLimit: () => boolean, - metadata: any}>(null) + const [loadedContractData, setLoadedContractData] = useState(null) const { contractList, loadType, currentFile } = props.contracts useEffect(() => { @@ -46,7 +33,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { const savedConfig = window.localStorage.getItem(`ipfs/${props.exEnvironment}/${networkName}`) const isCheckedIPFS = savedConfig === 'true' ? true : false // eslint-disable-line - if (isCheckedIPFS) setIpfsCheckedState(true) + if (isCheckedIPFS) props.setIpfsCheckedState(true) setAbiLabel({ display: 'none', content: 'ABI file selected' @@ -134,7 +121,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { if (selectedContract.bytecodeObject.length === 0) { return props.modal('Alert', 'This contract may be abstract, not implement an abstract parent\'s methods completely or not invoke an inherited contract\'s constructor correctly.', 'OK', () => {}) } - props.createInstance(gasEstimationPrompt, passphrasePrompt, logBuilder) + props.createInstance(loadedContractData, gasEstimationPrompt, passphrasePrompt, logBuilder, props.publishToStorage) } // listenToContextChange () { @@ -285,22 +272,6 @@ export function ContractDropdownUI (props: ContractDropdownProps) { // return confirmationCb // } - const gasEstimationPrompt = (msg: string) => { - return ( -
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
- ${msg} -
- ) - } - - const logBuilder = (msg: string) => { - return
{msg}
- } - - const passphrasePrompt = (message: string) => { - return - } - const atAddressChanged = (event) => { const value = event.target.value @@ -349,8 +320,8 @@ export function ContractDropdownUI (props: ContractDropdownProps) { } const handleCheckedIPFS = () => { - setIpfsCheckedState(!ipfsCheckedState) - window.localStorage.setItem(`ipfs/${props.exEnvironment}/${networkName}`, ipfsCheckedState.toString()) + props.setIpfsCheckedState(!props.ipfsCheckedState) + window.localStorage.setItem(`ipfs/${props.exEnvironment}/${networkName}`, props.ipfsCheckedState.toString()) } const handleContractChange = (e) => { @@ -359,6 +330,26 @@ export function ContractDropdownUI (props: ContractDropdownProps) { setSelectedContract(value) } + const gasEstimationPrompt = (msg: string) => { + return ( +
Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
+ ${msg} +
+ ) + } + + const logBuilder = (msg: string) => { + return
{msg}
+ } + + const passphrasePrompt = (message: string) => { + return + } + + const mainnetPrompt = () => { + + } + return (
@@ -383,7 +374,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { className="form-check-input custom-control-input" type="checkbox" onChange={handleCheckedIPFS} - checked={ipfsCheckedState} + checked={props.ipfsCheckedState} />