diff --git a/apps/remix-ide/src/app/tabs/locales/en/udapp.json b/apps/remix-ide/src/app/tabs/locales/en/udapp.json index 0c4634c529..8d5e0cf73c 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/en/udapp.json @@ -27,7 +27,8 @@ "udapp.contractOptionsTitle3": "Select and compile *.sol file to deploy or access a contract.", "udapp.contractOptionsTitle4": "When there is a compiled .sol file, choose the contract to deploy or to use with AtAddress.'", "udapp.checkSumWarning": "It seems you are not using a checksumed address.A checksummed address is an address that contains uppercase letters, as specified in {a}.Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.", - "udapp.isOverSizePrompt": "Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. More info: {a}", + "udapp.isOverSizePromptEip170": "Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. More info: {a}", + "udapp.isOverSizePromptEip3860": "Contract creation init code exceeds the allowed max code size of 49152 bytes. The deployment will likely fails. More info: {a}", "udapp.thisContractMayBeAbstract": "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.", "udapp.noCompiledContracts": "No compiled contracts", "udapp.addressOfContract": "Address of contract", diff --git a/apps/remix-ide/src/app/tabs/locales/zh/udapp.json b/apps/remix-ide/src/app/tabs/locales/zh/udapp.json index e60a8303c3..4ffb52bc60 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/zh/udapp.json @@ -27,7 +27,7 @@ "udapp.contractOptionsTitle3": "选择并编译 *.sol 文件以部署或访问合约。", "udapp.contractOptionsTitle4": "当有编译的 .sol 文件时,选择 {br} 合约进行部署或与 AtAddress 一起使用。", "udapp.checkSumWarning": "您似乎没有使用 checksumed address 。{br} checksumed address 是包含大写字母的地址,如 {a} 中所指定。{br} checksumed address 旨在帮助防止用户将交易发送到错误地址。", - "udapp.isOverSizePrompt": "合约创建初始化返回长度超过24576字节的数据。部署可能会失败。 {br}更多信息:{a}", + "udapp.isOverSizePromptEip170": "合约创建初始化返回长度超过24576字节的数据。部署可能会失败。 {br}更多信息:{a}", "udapp.thisContractMayBeAbstract": "这个合约可能是抽象的,它可能没有完全实现抽象父类的方法,或者它可能没有正确调用继承合约的构造函数。", "udapp.noCompiledContracts": "没有已编译的合约", "udapp.addressOfContract": "合约地址", diff --git a/libs/remix-core-plugin/src/types/contract.ts b/libs/remix-core-plugin/src/types/contract.ts index bf6d343038..ba93c138f0 100644 --- a/libs/remix-core-plugin/src/types/contract.ts +++ b/libs/remix-core-plugin/src/types/contract.ts @@ -8,6 +8,11 @@ export interface FuncABI { constant?: any } +export type OverSizeLimit = { + overSizeEip3860: boolean, + overSizeEip170: boolean +} + export interface ContractData { name: string, contract: any, @@ -19,7 +24,7 @@ export interface ContractData { deployedBytecode: any, getConstructorInterface: () => any, getConstructorInputs: () => any, - isOverSizeLimit: () => boolean, + isOverSizeLimit: (args: string) => Promise, metadata: any, contractName?: string } diff --git a/libs/remix-lib/src/execution/txFormat.ts b/libs/remix-lib/src/execution/txFormat.ts index 700cfbc472..e23f8de552 100644 --- a/libs/remix-lib/src/execution/txFormat.ts +++ b/libs/remix-lib/src/execution/txFormat.ts @@ -35,52 +35,59 @@ export function encodeData (funABI, values, contractbyteCode) { * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. * @param {Function} callback - callback */ -export function encodeParams (params, funAbi, callback) { - let data: Buffer | string = '' - let dataHex = '' - let funArgs = [] - if (Array.isArray(params)) { - funArgs = params - if (funArgs.length > 0) { +export function encodeParams (params, funAbi, callback?) { + return new Promise((resolve, reject) => { + let data: Buffer | string = '' + let dataHex = '' + let funArgs = [] + if (Array.isArray(params)) { + funArgs = params + if (funArgs.length > 0) { + try { + data = encodeParamsHelper(funAbi, funArgs) + dataHex = data.toString() + } catch (e) { + reject('Error encoding arguments: ' + e) + return callback && callback('Error encoding arguments: ' + e) + } + } + if (data.slice(0, 9) === 'undefined') { + dataHex = data.slice(9) + } + if (data.slice(0, 2) === '0x') { + dataHex = data.slice(2) + } + } else if (params.indexOf('raw:0x') === 0) { + // in that case we consider that the input is already encoded and *does not* contain the method signature + dataHex = params.replace('raw:0x', '') + data = Buffer.from(dataHex, 'hex') + } else { try { - data = encodeParamsHelper(funAbi, funArgs) - dataHex = data.toString() + funArgs = parseFunctionParams(params) } catch (e) { - return callback('Error encoding arguments: ' + e) + reject('Error encoding arguments: ' + e) + return callback && callback('Error encoding arguments: ' + e) } - } - if (data.slice(0, 9) === 'undefined') { - dataHex = data.slice(9) - } - if (data.slice(0, 2) === '0x') { - dataHex = data.slice(2) - } - } else if (params.indexOf('raw:0x') === 0) { - // in that case we consider that the input is already encoded and *does not* contain the method signature - dataHex = params.replace('raw:0x', '') - data = Buffer.from(dataHex, 'hex') - } else { - try { - funArgs = parseFunctionParams(params) - } catch (e) { - return callback('Error encoding arguments: ' + e) - } - try { - if (funArgs.length > 0) { - data = encodeParamsHelper(funAbi, funArgs) - dataHex = data.toString() + try { + if (funArgs.length > 0) { + data = encodeParamsHelper(funAbi, funArgs) + dataHex = data.toString() + } + } catch (e) { + reject('Error encoding arguments: ' + e) + return callback && callback('Error encoding arguments: ' + e) + } + if (data.slice(0, 9) === 'undefined') { + dataHex = data.slice(9) + } + if (data.slice(0, 2) === '0x') { + dataHex = data.slice(2) } - } catch (e) { - return callback('Error encoding arguments: ' + e) - } - if (data.slice(0, 9) === 'undefined') { - dataHex = data.slice(9) - } - if (data.slice(0, 2) === '0x') { - dataHex = data.slice(2) } - } - callback(null, { data: data, dataHex: dataHex, funArgs: funArgs }) + const result = { data: data, dataHex: dataHex, funArgs: funArgs } + callback && callback(null, result) + resolve(result) + }) } /** 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 d2efbe71c1..761d090f36 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts @@ -1,4 +1,4 @@ -import { ContractData, FuncABI, NetworkDeploymentFile, SolcBuildFile } from "@remix-project/core-plugin" +import { ContractData, FuncABI, NetworkDeploymentFile, SolcBuildFile, OverSizeLimit } from "@remix-project/core-plugin" import { RunTab } from "../types/run-tab" import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity' import * as remixLib from '@remix-project/remix-lib' @@ -64,10 +64,19 @@ export const getSelectedContract = (contractName: string, compiler: CompilerAbst return txHelper.inputParametersDeclarationToString(constructorInteface.inputs) }, - isOverSizeLimit: () => { - const deployedBytecode = contract.object.evm.deployedBytecode + isOverSizeLimit: async (args: string) => { + const encodedParams = await txFormat.encodeParams(args, txHelper.getConstructorInterface(contract.object.abi)) + const bytecode = contract.object.evm.bytecode.object + (encodedParams as any).dataHex + // https://eips.ethereum.org/EIPS/eip-3860 + const initCodeOversize = (bytecode && bytecode.length / 2 > 2 * 24576) - return (deployedBytecode && deployedBytecode.object.length / 2 > 24576) + const deployedBytecode = contract.object.evm.deployedBytecode + // https://eips.ethereum.org/EIPS/eip-170 + const deployedBytecodeOversize = (deployedBytecode && deployedBytecode.object.length / 2 > 24576) + return { + overSizeEip3860: initCodeOversize, + overSizeEip170: deployedBytecodeOversize + } }, metadata: contract.object.metadata } @@ -135,7 +144,7 @@ export const createInstance = async ( publishToStorage: (storage: 'ipfs' | 'swarm', contract: ContractData) => void, mainnetPrompt: MainnetPrompt, - isOverSizePrompt: () => JSX.Element, + isOverSizePrompt: (values: OverSizeLimit) => JSX.Element, args, deployMode: DeployMode[]) => { const isProxyDeployment = (deployMode || []).find(mode => mode === 'Deploy with Proxy') @@ -181,9 +190,11 @@ export const createInstance = async ( const compilerContracts = getCompilerContracts(plugin) const confirmationCb = getConfirmationCb(plugin, dispatch, mainnetPrompt) - if (selectedContract.isOverSizeLimit()) { - return dispatch(displayNotification('Contract code size over limit', isOverSizePrompt(), 'Force Send', 'Cancel', () => { - deployContract(plugin, selectedContract, !isProxyDeployment && !isContractUpgrade ? args : '', contractMetadata, compilerContracts, { + args = !isProxyDeployment && !isContractUpgrade ? args : '' + const overSize = await selectedContract.isOverSizeLimit(args) + if (overSize.overSizeEip170 || overSize.overSizeEip3860) { + return dispatch(displayNotification('Contract code size over limit', isOverSizePrompt(overSize), 'Force Send', 'Cancel', () => { + deployContract(plugin, selectedContract, args, contractMetadata, compilerContracts, { continueCb: (error, continueTxExecution, cancelCb) => { continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb) }, @@ -199,7 +210,7 @@ export const createInstance = async ( return terminalLogger(plugin, log) })) } - deployContract(plugin, selectedContract, !isProxyDeployment && !isContractUpgrade ? args : '', contractMetadata, compilerContracts, { + deployContract(plugin, selectedContract, args, contractMetadata, compilerContracts, { continueCb: (error, continueTxExecution, cancelCb) => { continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb) }, 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 d13c9d0bfd..97ae968207 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -8,7 +8,7 @@ import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setM updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath } from './actions' import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions, updateInstanceBalance, syncContractsInternal, isValidContractAddress, isValidContractUpgrade } from './deploy' import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity' -import { ContractData, FuncABI } from "@remix-project/core-plugin" +import { ContractData, FuncABI, OverSizeLimit } from "@remix-project/core-plugin" import { DeployMode, MainnetPrompt } from '../types' import { runCurrentScenario, storeScenario } from './recorder' import { SolcInput, SolcOutput } from '@openzeppelin/upgrades-core' @@ -39,7 +39,7 @@ export const setPassphraseModal = (passphrase: string) => setPassphrasePrompt(di export const setMatchPassphraseModal = (passphrase: string) => setMatchPassphrasePrompt(dispatch, passphrase) export const signMessage = (account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => signMessageWithAddress(plugin, dispatch, account, message, modalContent, passphrase) export const fetchSelectedContract = (contractName: string, compiler: CompilerAbstractType) => getSelectedContract(contractName, compiler) -export const createNewInstance = async (selectedContract: ContractData, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, publishToStorage: (storage: 'ipfs' | 'swarm', contract: ContractData) => void, mainnetPrompt: MainnetPrompt, isOverSizePrompt: () => JSX.Element, args, deployMode: DeployMode[]) => createInstance(plugin, dispatch, selectedContract, gasEstimationPrompt, passphrasePrompt, publishToStorage, mainnetPrompt, isOverSizePrompt, args, deployMode) +export const createNewInstance = async (selectedContract: ContractData, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, publishToStorage: (storage: 'ipfs' | 'swarm', contract: ContractData) => void, mainnetPrompt: MainnetPrompt, isOverSizePrompt: (values: OverSizeLimit) => JSX.Element, args, deployMode: DeployMode[]) => createInstance(plugin, dispatch, selectedContract, gasEstimationPrompt, passphrasePrompt, publishToStorage, mainnetPrompt, isOverSizePrompt, args, deployMode) export const setSendValue = (value: string) => setSendTransactionValue(dispatch, value) export const setBaseFeePerGas = (baseFee: string) => updateBaseFeePerGas(dispatch, baseFee) export const setConfirmSettings = (confirmation: boolean) => updateConfirmSettings(dispatch, confirmation) 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 ef9d7fb720..cd5845970a 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { ContractDropdownProps, DeployMode } from '../types' -import { ContractData, FuncABI } from '@remix-project/core-plugin' +import { ContractData, FuncABI, OverSizeLimit} from '@remix-project/core-plugin' import * as ethJSUtil from '@ethereumjs/util' import { ContractGUI } from './contractGUI' import { CustomTooltip, deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper' @@ -246,10 +246,19 @@ export function ContractDropdownUI (props: ContractDropdownProps) { ) } - const isOverSizePrompt = () => { + const isOverSizePrompt = (values: OverSizeLimit) => { return (
- , a: eip-170 }} /> + { + values.overSizeEip170 &&
+ , a: eip-170 }} /> +
+ } + { + values.overSizeEip3860 &&
+ , a: eip-170 }} /> +
+ }
) } 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 a005664ed4..e686f0defa 100644 --- a/libs/remix-ui/run-tab/src/lib/types/index.ts +++ b/libs/remix-ui/run-tab/src/lib/types/index.ts @@ -1,6 +1,6 @@ import { Ref } from 'react' import { CompilerAbstract } from '@remix-project/remix-solidity' -import { ContractData, FuncABI } from '@remix-project/core-plugin' +import { ContractData, FuncABI, OverSizeLimit } from '@remix-project/core-plugin' import { RunTab } from './run-tab' import { SolcInput, SolcOutput } from '@openzeppelin/upgrades-core' import { LayoutCompatibilityReport } from '@openzeppelin/upgrades-core/dist/storage/report' @@ -250,7 +250,7 @@ export interface ContractDropdownProps { publishToStorage: (storage: 'ipfs' | 'swarm', contract: ContractData) => void, mainnetPrompt: MainnetPrompt, - isOverSizePrompt: () => JSX.Element, + isOverSizePrompt: (values: OverSizeLimit) => JSX.Element, args, deployMode: DeployMode[]) => void, ipfsCheckedState: boolean,