From e3cbd65bc56c00984db6de9454b6d2b8a1efb13c Mon Sep 17 00:00:00 2001 From: David Disu Date: Mon, 4 Apr 2022 06:23:15 +0100 Subject: [PATCH] implement isConcerned openzeppelin api --- .../src/lib/openzeppelin-proxy.ts | 6 ++-- libs/remix-core-plugin/src/types/contract.ts | 32 +++++++++++++++++++ .../remix-ui/run-tab/src/lib/actions/index.ts | 12 ++++--- .../run-tab/src/lib/actions/payload.ts | 9 +++++- .../src/lib/components/contractDropdownUI.tsx | 4 +-- .../src/lib/components/contractGUI.tsx | 4 +-- .../run-tab/src/lib/constants/index.ts | 3 +- .../run-tab/src/lib/reducers/runTab.ts | 11 ++++++- libs/remix-ui/run-tab/src/lib/run-tab.tsx | 1 + libs/remix-ui/run-tab/src/lib/types/index.ts | 6 ++-- 10 files changed, 73 insertions(+), 15 deletions(-) diff --git a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts index 13d5075670..28b6346c2f 100644 --- a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts +++ b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts @@ -1,5 +1,5 @@ import { Plugin } from '@remixproject/engine'; -import { ContractData } from '../types/contract'; +import { ContractAST, ContractData } from '../types/contract'; const proxyProfile = { name: 'openzeppelin-proxy', @@ -7,6 +7,7 @@ const proxyProfile = { description: 'openzeppelin-proxy', methods: ['isConcerned', 'execute'] }; +const UUPS = '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol' export class OpenZeppelinProxy extends Plugin { blockchain: any @@ -15,8 +16,9 @@ export class OpenZeppelinProxy extends Plugin { this.blockchain = blockchain } - async isConcerned(contractData: ContractData): Promise { + async isConcerned(ast: ContractAST): Promise { // check in the AST if it's an upgradable contract + if (ast.nodes.find(node => node.absolutePath === UUPS)) return true return false } diff --git a/libs/remix-core-plugin/src/types/contract.ts b/libs/remix-core-plugin/src/types/contract.ts index 9903f92b98..0a2c32ca89 100644 --- a/libs/remix-core-plugin/src/types/contract.ts +++ b/libs/remix-core-plugin/src/types/contract.ts @@ -20,4 +20,36 @@ export interface ContractData { getConstructorInputs: () => any, isOverSizeLimit: () => boolean, metadata: any +} + +export interface ContractAST { + id: number, + absolutePath: string, + exportedSymbols: { + [key: string]: number[] + }, + license: string, + nodeType: string, + src: string, + nodes: { + id: number, + literals: string[], + nodeType: string, + src: string, + absolutePath?: string, + file?: string, + nameLocation?: string, + scope?: number, + srcUnit?: number, + unitAlias?: string, + symbolAliases?: any[], + abstract?: boolean, + baseContracts?: any[], + contractDependencies?: any[], + contractKind?: string, + fullyImplemented?: boolean, + linearizedBaseContracts?: number[], + name?: string, + usedErrors?: any[] + }[] } \ No newline at end of file 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 7109a80885..d112d2a6f1 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -3,11 +3,11 @@ 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, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setLoadType, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setRecorderCount, setSelectedAccount, setSendUnit, setSendValue, setTxFeeContent } from './payload' +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 { CompilerAbstract } from '@remix-project/remix-solidity' import * as remixLib from '@remix-project/remix-lib' -import { MainnetPrompt } from '../types' +import { DeployMode, MainnetPrompt } from '../types' import { ContractData, FuncABI, } from '@remix-project/core-plugin' import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts' @@ -287,7 +287,7 @@ export const signMessageWithAddress = (account: string, message: string, modalCo }) } -const broadcastCompilationResult = (file, source, languageVersion, data, input?) => { +const broadcastCompilationResult = async (file, source, languageVersion, data, input?) => { // TODO check whether the tab is configured const compiler = new CompilerAbstract(languageVersion, data, source, input) @@ -297,7 +297,10 @@ const broadcastCompilationResult = (file, source, languageVersion, data, input?) 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) + if (isUpgradeable) dispatch(setDeployOptions([{ title: 'Deploy with Proxy', active: false }])) + else dispatch(setDeployOptions([])) dispatch(fetchContractListSuccess({ [file]: contracts })) dispatch(setCurrentFile(file)) } @@ -425,7 +428,8 @@ export const createInstance = async ( contract: ContractData) => void, mainnetPrompt: MainnetPrompt, isOverSizePrompt: () => JSX.Element, - args) => { + args, + deployMode: DeployMode[]) => { const statusCb = (msg: string) => { const log = logBuilder(msg) 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 b6ce6bc61f..e73bd1b1d0 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/payload.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/payload.ts @@ -1,6 +1,6 @@ import { ContractList } from '../reducers/runTab' 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_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_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' export const fetchAccountsListRequest = () => { @@ -293,3 +293,10 @@ export const removeDeployOption = (title: DeployMode) => { type: REMOVE_DEPLOY_OPTION } } + +export const setDeployOptions = (deployOptions: DeployOptions[]) => { + return { + payload: deployOptions, + type: SET_DEPLOY_OPTIONS + } +} 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 7fe5085be7..cf6c0e361a 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -151,7 +151,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) { if (selectedContract.bytecodeObject.length === 0) { return props.modal('Alert', 'This contract may be abstract, it may not implement an abstract parent\'s methods completely or it may not invoke an inherited contract\'s constructor correctly.', 'OK', () => {}) } - props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.logBuilder, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args) + props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.logBuilder, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode) } const atAddressChanged = (event) => { @@ -226,7 +226,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
{ ((contractList[currentFile] && contractList[currentFile].filter(contract => contract)) || []).length <= 0 ? 'No compiled contracts' : loadedContractData ?
- +
0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive') ? 'udapp_hasArgs' : ''}`}>
{ - props.isDeploy ? + props.isDeploy && (props.deployOptions || []).length > 0? { - (props.deployOptions || []).map(({ title, active }, index) => setSelectedDeploy(index)}> { selectedDeployIndex.includes(index) ? ✓ {title} : {title} }) + (props.deployOptions).map(({ title, active }, index) => setSelectedDeploy(index)}> { selectedDeployIndex.includes(index) ? ✓ {title} : {title} }) } : diff --git a/libs/remix-ui/run-tab/src/lib/constants/index.ts b/libs/remix-ui/run-tab/src/lib/constants/index.ts index 598d58d6fb..bd05b38d51 100644 --- a/libs/remix-ui/run-tab/src/lib/constants/index.ts +++ b/libs/remix-ui/run-tab/src/lib/constants/index.ts @@ -42,4 +42,5 @@ export const SET_RECORDER_COUNT = 'SET_RECORDER_COUNT' export const CLEAR_RECORDER_COUNT = 'CLEAR_RECORDER_COUNT' export const RESET_STATE = 'RESET_STATE' export const ADD_DEPLOY_OPTION = 'ADD_DEPLOY_OPTION' -export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION' \ No newline at end of file +export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION' +export const SET_DEPLOY_OPTIONS = 'SET_DEPLOY_OPTIONS' \ No newline at end of file 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 1a6c84f3e1..5fd9eab4fc 100644 --- a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts +++ b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts @@ -1,7 +1,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity-ts' import { ContractData } from '@remix-project/core-plugin' 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_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_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 { type: string payload: any @@ -687,6 +687,15 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A } } + case SET_DEPLOY_OPTIONS: { + const payload: DeployOptions[] = action.payload + + return { + ...state, + deployOptions: payload + } + } + 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 45e343fdbc..279286222c 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -245,6 +245,7 @@ export function RunTabUI (props: RunTabProps) { loadAddress={loadAddress} networkName={runTab.networkName} setNetworkName={setNetworkNameFromProvider} + deployOptions={runTab.deployOptions} /> void, mainnetPrompt: MainnetPrompt, isOverSizePrompt: () => JSX.Element, - args) => void, + args, + deployMode: DeployMode[]) => void, ipfsCheckedState: boolean, setIpfsCheckedState: (value: boolean) => void, publishToStorage: (storage: 'ipfs' | 'swarm', contract: ContractData) => void, @@ -159,7 +160,8 @@ export interface ContractDropdownProps { tooltip: (toasterMsg: string | JSX.Element) => void, loadAddress: (contract: ContractData, address: string) => void, networkName: string, - setNetworkName: (name: string) => void + setNetworkName: (name: string) => void, + deployOptions: DeployOptions[] } export interface RecorderProps {