implement isConcerned openzeppelin api

pull/2260/head
David Disu 3 years ago
parent d3fb693bbd
commit 56f3c745e4
  1. 6
      libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
  2. 32
      libs/remix-core-plugin/src/types/contract.ts
  3. 12
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  4. 9
      libs/remix-ui/run-tab/src/lib/actions/payload.ts
  5. 4
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  6. 4
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  7. 3
      libs/remix-ui/run-tab/src/lib/constants/index.ts
  8. 11
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  9. 1
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  10. 6
      libs/remix-ui/run-tab/src/lib/types/index.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<boolean> {
async isConcerned(ast: ContractAST): Promise<boolean> {
// check in the AST if it's an upgradable contract
if (ast.nodes.find(node => node.absolutePath === UUPS)) return true
return false
}

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

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

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

@ -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) {
<div className="udapp_deployDropdown">
{ ((contractList[currentFile] && contractList[currentFile].filter(contract => contract)) || []).length <= 0 ? 'No compiled contracts'
: loadedContractData ? <div>
<ContractGUI title='Deploy' isDeploy={true} funcABI={constructorInterface} clickCallBack={clickCallback} inputs={constructorInputs} widthClass='w-50' evmBC={loadedContractData.bytecodeObject} lookupOnly={false} />
<ContractGUI title='Deploy' isDeploy={true} deployOptions={props.deployOptions} funcABI={constructorInterface} clickCallBack={clickCallback} inputs={constructorInputs} widthClass='w-50' evmBC={loadedContractData.bytecodeObject} lookupOnly={false} />
<div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input
id="deployAndRunPublishToIPFS"

@ -183,13 +183,13 @@ export function ContractGUI (props: ContractGUIProps) {
<div className={`udapp_contractProperty ${(props.funcABI.inputs && props.funcABI.inputs.length > 0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive') ? 'udapp_hasArgs' : ''}`}>
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: toggleContainer ? 'none' : 'flex' }}>
{
props.isDeploy ?
props.isDeploy && (props.deployOptions || []).length > 0?
<Dropdown as={ButtonGroup} show={showOptions}>
<button onClick={handleActionClick} title={buttonOptions.title} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} data-id={buttonOptions.dataId}>Deploy</button>
<Dropdown.Toggle split id="dropdown-split-basic" className={`btn btn-sm dropdown-toggle dropdown-toggle-split ${buttonOptions.classList}`} style={{ maxWidth: 25, minWidth: 0, height: 32 }} onClick={toggleOptions} />
<Dropdown.Menu className="deploy-items border-0">
{
(props.deployOptions || []).map(({ title, active }, index) => <Dropdown.Item onClick={() => setSelectedDeploy(index)}> { selectedDeployIndex.includes(index) ? <span>&#10003; {title} </span> : <span className="pl-3">{title}</span> }</Dropdown.Item>)
(props.deployOptions).map(({ title, active }, index) => <Dropdown.Item onClick={() => setSelectedDeploy(index)}> { selectedDeployIndex.includes(index) ? <span>&#10003; {title} </span> : <span className="pl-3">{title}</span> }</Dropdown.Item>)
}
</Dropdown.Menu>
</Dropdown> : <button onClick={handleActionClick} title={buttonOptions.title} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} data-id={buttonOptions.dataId}>{title}</button>

@ -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'
export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION'
export const SET_DEPLOY_OPTIONS = 'SET_DEPLOY_OPTIONS'

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

@ -245,6 +245,7 @@ export function RunTabUI (props: RunTabProps) {
loadAddress={loadAddress}
networkName={runTab.networkName}
setNetworkName={setNetworkNameFromProvider}
deployOptions={runTab.deployOptions}
/>
<RecorderUI
gasEstimationPrompt={gasEstimationPrompt}

@ -148,7 +148,8 @@ export interface ContractDropdownProps {
contract: ContractData) => 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 {

Loading…
Cancel
Save