diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js
index 346d8ded69..fef939a3d4 100644
--- a/apps/remix-ide/src/blockchain/blockchain.js
+++ b/apps/remix-ide/src/blockchain/blockchain.js
@@ -11,8 +11,7 @@ import InjectedProvider from './providers/injected.js'
import NodeProvider from './providers/node.js'
import { execution, EventManager, helpers } from '@remix-project/remix-lib'
import { etherScanLink } from './helper'
-import { logBuilder, confirmProxyMsg } from "@remix-ui/helper"
-import { cancelProxyMsg } from '@remix-ui/helper'
+import { logBuilder, cancelUpgradeMsg, cancelProxyMsg } from "@remix-ui/helper"
const { txFormat, txExecution, typeConversion, txListener: Txlistener, TxRunner, TxRunnerWeb3, txHelper } = execution
const { txResultHelper: resultToRemixTx } = helpers
const packageJson = require('../../../../package.json')
@@ -183,7 +182,26 @@ export class Blockchain extends Plugin {
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
}
- async upgradeProxy(proxyAddress, data, newImplementationContractObject) {
+ async upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject) {
+ const upgradeModal = {
+ id: 'confirmProxyDeployment',
+ title: 'ERC1967',
+ message: `Confirm you want to upgrade your contract to new implementation ${newImplAddress}.`,
+ modalType: 'modal',
+ okLabel: 'OK',
+ cancelLabel: 'Cancel',
+ okFn: () => {
+ this.runUpgradeTx(proxyAddress, data, newImplementationContractObject)
+ },
+ cancelFn: () => {
+ this.call('notification', 'toast', cancelUpgradeMsg())
+ },
+ hideFn: () => null
+ }
+ this.call('notification', 'modal', upgradeModal)
+ }
+
+ async runUpgradeTx (proxyAddress, data, newImplementationContractObject) {
const args = { useCall: false, data, to: proxyAddress }
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
// continue using original authorization given by user
diff --git a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
index 9f9e8780d5..af0c7bebaa 100644
--- a/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
+++ b/libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
@@ -1,12 +1,12 @@
import { Plugin } from '@remixproject/engine';
-import { ContractABI, ContractAST, DeployOption } from '../types/contract';
+import { ContractABI, ContractAST, DeployOptions } from '../types/contract';
import { UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups';
const proxyProfile = {
name: 'openzeppelin-proxy',
displayName: 'openzeppelin-proxy',
description: 'openzeppelin-proxy',
- methods: ['isConcerned', 'executeUUPSProxy', 'executeUUPSContractUpgrade', 'getDeployOptions', 'getUpgradeOptions']
+ methods: ['isConcerned', 'executeUUPSProxy', 'executeUUPSContractUpgrade', 'getProxyOptions', 'getUpgradeOptions']
};
export class OpenZeppelinProxy extends Plugin {
blockchain: any
@@ -28,7 +28,7 @@ export class OpenZeppelinProxy extends Plugin {
return false
}
- async getDeployOptions (contracts: ContractABI): Promise<{ [name: string]: DeployOption }> {
+ async getProxyOptions (contracts: ContractABI): Promise<{ [name: string]: DeployOptions }> {
const inputs = {}
if (this.kind === 'UUPS') {
@@ -36,8 +36,9 @@ export class OpenZeppelinProxy extends Plugin {
const abi = contracts[name].abi
const initializeInput = abi.find(node => node.name === 'initialize')
- if (initializeInput) {
- inputs[name] = {
+ inputs[name] = {
+ options: [{ title: 'Deploy with Proxy', active: false }, { title: 'Upgrade Contract', active: false }],
+ initializeOptions: {
inputs: initializeInput,
initializeInputs: this.blockchain.getInputs(initializeInput)
}
@@ -95,6 +96,6 @@ export class OpenZeppelinProxy extends Plugin {
}
// re-use implementation contract's ABI for UI display in udapp and change name to proxy name.
newImplementationContractObject.name = proxyName
- this.blockchain.upgradeProxy(proxyAddress, data, newImplementationContractObject)
+ this.blockchain.upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject)
}
}
diff --git a/libs/remix-core-plugin/src/types/contract.ts b/libs/remix-core-plugin/src/types/contract.ts
index 92c32653b5..bd0c9075d0 100644
--- a/libs/remix-core-plugin/src/types/contract.ts
+++ b/libs/remix-core-plugin/src/types/contract.ts
@@ -136,19 +136,25 @@ export interface ContractABI {
};
}
+export type DeployMode = 'Deploy with Proxy' | 'Upgrade Contract'
+
export type DeployOption = {
- initializeInputs: string,
+ initializeInputs: string,
+ inputs: {
inputs: {
- inputs: {
- internalType?: string,
- name: string,
- type: string
- }[],
- name: "initialize",
- outputs?: any[],
- stateMutability: string,
- type: string,
- payable?: boolean,
- constant?: any
- }
+ internalType?: string,
+ name: string,
+ type: string
+ }[],
+ name: "initialize",
+ outputs?: any[],
+ stateMutability: string,
+ type: string,
+ payable?: boolean,
+ constant?: any
}
+}
+export interface DeployOptions {
+ initializeOptions: DeployOption,
+ options: { title: DeployMode, active: boolean }[]
+}
diff --git a/libs/remix-ui/helper/src/lib/helper-components.tsx b/libs/remix-ui/helper/src/lib/helper-components.tsx
index 206fbec926..aa2928f691 100644
--- a/libs/remix-ui/helper/src/lib/helper-components.tsx
+++ b/libs/remix-ui/helper/src/lib/helper-components.tsx
@@ -89,3 +89,9 @@ export const cancelProxyMsg = () => (
Proxy deployment cancelled.
)
+
+export const cancelUpgradeMsg = () => (
+
+ Upgrade with proxy cancelled.
+
+)
diff --git a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts
index 25847a562b..b43065468b 100644
--- a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts
+++ b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts
@@ -179,7 +179,7 @@ export const createInstance = async (
if (selectedContract.isOverSizeLimit()) {
return dispatch(displayNotification('Contract code size over limit', isOverSizePrompt(), 'Force Send', 'Cancel', () => {
- deployContract(plugin, selectedContract, !isProxyDeployment ? args : '', contractMetadata, compilerContracts, {
+ deployContract(plugin, selectedContract, !isProxyDeployment && !isContractUpgrade ? args : '', contractMetadata, compilerContracts, {
continueCb: (error, continueTxExecution, cancelCb) => {
continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb)
},
diff --git a/libs/remix-ui/run-tab/src/lib/actions/events.ts b/libs/remix-ui/run-tab/src/lib/actions/events.ts
index 13eacd809d..447da599fc 100644
--- a/libs/remix-ui/run-tab/src/lib/actions/events.ts
+++ b/libs/remix-ui/run-tab/src/lib/actions/events.ts
@@ -2,7 +2,7 @@ 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, setCurrentContract, setCurrentFile, setDeployOptions, setLoadType, setProxyEnvAddress, setRecorderCount, setSendValue } from "./payload"
+import { addDeployOption, clearAllInstances, clearRecorderCount, fetchContractListSuccess, resetUdapp, setCurrentContract, setCurrentFile, setLoadType, setProxyEnvAddress, setRecorderCount, setSendValue } from "./payload"
import { CompilerAbstract } from '@remix-project/remix-solidity'
import * as ethJSUtil from 'ethereumjs-util'
import Web3 from 'web3'
@@ -108,11 +108,12 @@ const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispat
const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file] ? data.sources[file].ast : {})
if (isUpgradeable) {
- const options = await plugin.call('openzeppelin-proxy', 'getDeployOptions', data.contracts[file])
+ const options = await plugin.call('openzeppelin-proxy', 'getProxyOptions', data.contracts[file])
- dispatch(setDeployOptions({ options: [{ title: 'Deploy with Proxy', active: false }, { title: 'Upgrade Contract', active: false }], initializeOptions: options }))
+ dispatch(addDeployOption({ [file]: options }))
+ } else {
+ dispatch(addDeployOption({ [file]: {} }))
}
- else dispatch(setDeployOptions({} as any))
dispatch(fetchContractListSuccess({ [file]: contracts }))
dispatch(setCurrentFile(file))
// TODO: set current contract
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 f48461a459..bb0fc91a41 100644
--- a/libs/remix-ui/run-tab/src/lib/actions/payload.ts
+++ b/libs/remix-ui/run-tab/src/lib/actions/payload.ts
@@ -280,21 +280,21 @@ export const resetUdapp = () => {
}
}
-export const addDeployOption = (deployOption: DeployOptions) => {
+export const addDeployOption = (deployOption: { [file: string]: { [name: string]: DeployOptions } }) => {
return {
payload: deployOption,
type: ADD_DEPLOY_OPTION
}
}
-export const removeDeployOption = (title: DeployMode) => {
+export const removeDeployOption = (file: string) => {
return {
- payload: title,
+ payload: file,
type: REMOVE_DEPLOY_OPTION
}
}
-export const setDeployOptions = (deployOptions: DeployOptions) => {
+export const setDeployOptions = (deployOptions: { [file: string]: { [name: string]: 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 fc8196aea0..da13ce63d6 100644
--- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
+++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
@@ -27,7 +27,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
const [constructorInterface, setConstructorInterface] = useState(null)
const [constructorInputs, setConstructorInputs] = useState(null)
const contractsRef = useRef(null)
- const { contractList, loadType, currentFile, currentContract, compilationCount, deployOptions } = props.contracts
+ const { contractList, loadType, currentFile, currentContract, compilationCount, deployOptions, proxyKey } = props.contracts
useEffect(() => {
enableAtAddress(false)
@@ -155,6 +155,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', () => {})
}
+ if ((selectedContract.name !== currentContract) && (selectedContract.name === 'ERC1967Proxy')) selectedContract.name = currentContract
props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode)
}
@@ -232,15 +233,15 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
- Upgrade Contract
+ Upgrade With Proxy
@@ -343,12 +345,12 @@ export function ContractGUI (props: ContractGUIProps) {
{
- !useLastProxy ?
+ !useLastProxy ?
:
- { proxyAddress ? shortenAddress(proxyAddress) : 'No proxy address available' }
+ { proxyAddress || 'No proxy address available' }
}
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 fd690e7f64..5299b748ab 100644
--- a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
+++ b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
@@ -1,8 +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_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, SET_PROXY_ENV_ADDRESS } from '../constants'
-import Web3 from 'web3'
+import { DeployOptions } from '../types'
+import { 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_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, SET_PROXY_ENV_ADDRESS, ADD_DEPLOY_OPTION, REMOVE_DEPLOY_OPTION } from '../constants'
declare const window: any
interface Action {
@@ -67,7 +66,8 @@ export interface RunTabState {
compiler: CompilerAbstract
}[]
},
- deployOptions: DeployOptions
+ deployOptions: { [file: string]: { [name: string]: DeployOptions } },
+ proxyKey: string,
loadType: 'abi' | 'sol' | 'other'
currentFile: string,
currentContract: string,
@@ -155,6 +155,7 @@ export const runTabInitialState: RunTabState = {
contracts: {
contractList: {},
deployOptions: {} as any,
+ proxyKey: '',
loadType: 'other',
currentFile: '',
currentContract: '',
@@ -680,39 +681,33 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A
}
case ADD_DEPLOY_OPTION: {
- const payload: { title: DeployMode, active: boolean } = action.payload
+ const payload: { [file: string]: { [name: string]: DeployOptions } } = action.payload
return {
...state,
contracts: {
...state.contracts,
- deployOptions: {
- ...state.contracts.deployOptions,
- options: [...state.contracts.deployOptions.options, payload]
- }
+ deployOptions: {...state.contracts.deployOptions, ...payload }
}
}
}
case REMOVE_DEPLOY_OPTION: {
const payload: string = action.payload
- const options = state.contracts.deployOptions.options.filter(val => val.title !== payload)
+ const options = state.contracts.deployOptions
-
+ delete options[payload]
return {
...state,
contracts: {
...state.contracts,
- deployOptions: {
- ...state.contracts.deployOptions,
- options
- }
+ deployOptions: options
}
}
}
case SET_DEPLOY_OPTIONS: {
- const payload: DeployOptions = action.payload
+ const payload: { [file: string]: { [name: string]: DeployOptions } } = action.payload
return {
...state,
@@ -730,10 +725,7 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A
...state,
contracts: {
...state.contracts,
- deployOptions: {
- ...state.contracts.deployOptions,
proxyKey: payload
- }
}
}
}
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 27b5085833..0595ef69dc 100644
--- a/libs/remix-ui/run-tab/src/lib/types/index.ts
+++ b/libs/remix-ui/run-tab/src/lib/types/index.ts
@@ -128,7 +128,8 @@ export interface ContractDropdownProps {
exEnvironment: string,
contracts: {
contractList: ContractList,
- deployOptions: DeployOptions,
+ deployOptions: { [file: string]: { [name: string]: DeployOptions } },
+ proxyKey: string,
loadType: 'abi' | 'sol' | 'other',
currentFile: string,
currentContract: string,
@@ -238,11 +239,8 @@ export type DeployOption = {
}
}
export interface DeployOptions {
- initializeOptions: {
- [key: string]: DeployOption
- },
- options: { title: DeployMode, active: boolean }[],
- proxyKey?: string
+ initializeOptions: DeployOption,
+ options: { title: DeployMode, active: boolean }[]
}
export interface ContractGUIProps {