diff --git a/.github/workflows/pr-reminder.yml b/.github/workflows/pr-reminder.yml index 532a7555b1..567fd8e19b 100644 --- a/.github/workflows/pr-reminder.yml +++ b/.github/workflows/pr-reminder.yml @@ -2,7 +2,7 @@ name: PRs reviews reminder on: schedule: - - cron: "0 8-17/8 * * 1-5" + - cron: "0 8 * * 1-5" workflow_dispatch: jobs: @@ -14,4 +14,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} - freeze-date: '2024-02-26T18:00:00Z' + freeze-date: '2024-04-08T18:00:00Z' diff --git a/apps/circuit-compiler/src/app/actions/index.ts b/apps/circuit-compiler/src/app/actions/index.ts index 55944f7bc6..7c8e6077c3 100644 --- a/apps/circuit-compiler/src/app/actions/index.ts +++ b/apps/circuit-compiler/src/app/actions/index.ts @@ -6,7 +6,7 @@ export const compileCircuit = async (plugin: CircomPluginClient, appState: AppSt if (appState.status !== "compiling") { await plugin.compile(appState.filePath, { version: appState.version, prime: appState.primeValue }) } else { - console.log('Exisiting circuit compilation in progress') + console.log('Existing circuit compilation in progress') } } catch (e) { plugin.internalEvents.emit('circuit_compiling_errored', e) @@ -19,7 +19,7 @@ export const generateR1cs = async (plugin: CircomPluginClient, appState: AppStat if (appState.status !== "generating") { await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue }) } else { - console.log('Exisiting r1cs generation in progress') + console.log('Existing r1cs generation in progress') } } catch (e) { plugin.internalEvents.emit('circuit_generating_r1cs_errored', e) @@ -34,7 +34,7 @@ export const computeWitness = async (plugin: CircomPluginClient, status: string, await plugin.computeWitness(input) } else { - console.log('Exisiting witness computation in progress') + console.log('Existing witness computation in progress') } } catch (e) { plugin.internalEvents.emit('circuit_computing_witness_errored', e) diff --git a/apps/circuit-compiler/src/app/components/witness.tsx b/apps/circuit-compiler/src/app/components/witness.tsx index 4b085fda2f..08f82ca171 100644 --- a/apps/circuit-compiler/src/app/components/witness.tsx +++ b/apps/circuit-compiler/src/app/components/witness.tsx @@ -12,10 +12,20 @@ export function WitnessSection ({ plugin, signalInputs, status }: {plugin: Circo const handleSignalInput = (e: any) => { let value = e.target.value - try { - value = remixLib.execution.txFormat.parseFunctionParams(value) - } catch (e) { + if (value.startsWith('[') && value.endsWith(']')) { + try { + value = remixLib.execution.txFormat.parseFunctionParams(value) + } catch (e) { // do nothing + } + } else if (value.startsWith('[') && !value.endsWith(']')) { + // do nothing + } else { + try { + value = remixLib.execution.txFormat.parseFunctionParams(value) + } catch (e) { + // do nothing + } } setWitnessValues({ ...witnessValues, diff --git a/apps/learneth/README.md b/apps/learneth/README.md index a8e9909931..9bf4030879 100644 --- a/apps/learneth/README.md +++ b/apps/learneth/README.md @@ -92,7 +92,7 @@ addRepository(repoName, branch) startTutorial(repoName,branch,id) ``` -You don't need to add a seperate addRepository before calling startTutorial, this call will also add the repo. +You don't need to add a separate addRepository before calling startTutorial, this call will also add the repo. _Parameters_ @@ -117,7 +117,7 @@ tags: ``` (function () { try { - // You don't need to add a seperate addRepository before calling startTutorial, this is just an example + // You don't need to add a separate addRepository before calling startTutorial, this is just an example remix.call('LearnEth', 'addRepository', "ethereum/remix-workshops", "master") remix.call('LearnEth', 'startTutorial', "ethereum/remix-workshops", "master", "basics") remix.call('LearnEth', 'startTutorial', "ethereum/remix-workshops", "master", 2) diff --git a/apps/learneth/src/redux/models/workshop.ts b/apps/learneth/src/redux/models/workshop.ts index daf41b5ff9..8c975cc48c 100644 --- a/apps/learneth/src/redux/models/workshop.ts +++ b/apps/learneth/src/redux/models/workshop.ts @@ -56,7 +56,6 @@ const Model: ModelType = { const url = `${apiUrl}/clone/${encodeURIComponent(payload.name)}/${payload.branch}?${Math.random()}` console.log('loading ', url) const {data} = yield axios.get(url) - console.log(data) const repoId = `${payload.name}-${payload.branch}` for (let i = 0; i < data.ids.length; i++) { diff --git a/apps/remix-ide-e2e/package.json b/apps/remix-ide-e2e/package.json index 5723d9714e..4153017b27 100644 --- a/apps/remix-ide-e2e/package.json +++ b/apps/remix-ide-e2e/package.json @@ -6,8 +6,8 @@ "npm": "^6.14.15" }, "dependencies": { - "@openzeppelin/contracts": "^5.0.0", - "@openzeppelin/contracts-upgradeable": "^5.0.0", + "@openzeppelin/contracts": "^5.0.2", + "@openzeppelin/contracts-upgradeable": "^5.0.2", "@openzeppelin/upgrades-core": "^1.30.0", "@openzeppelin/wizard": "^0.4.0", "@remix-project/remixd": "../../dist/libs/remixd", diff --git a/apps/remix-ide-e2e/src/commands/openFile.ts b/apps/remix-ide-e2e/src/commands/openFile.ts index 7f7e7b2026..f214100eee 100644 --- a/apps/remix-ide-e2e/src/commands/openFile.ts +++ b/apps/remix-ide-e2e/src/commands/openFile.ts @@ -21,7 +21,7 @@ function openFile (browser: NightwatchBrowser, name: string, done: VoidFunction) // if side panel is shown, check this is the file panel browser.element('css selector', '[data-id="verticalIconsKindfilePanel"] img[data-id="selected"]', (result) => { if (result.status === 0) { - done() + done() } else browser.clickLaunchIcon('filePanel').perform(() => { done() }) diff --git a/apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx b/apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx index 88c9bc9646..2b1479dda1 100644 --- a/apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx +++ b/apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx @@ -46,7 +46,6 @@ function App() { const customProfiles = ['menuicons', 'tabs', 'solidityUnitTesting', 'hardhat-provider', 'notification'] client.testCommand = async (data: any) => { - console.log(data) methodLog(data) } diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts index 5fa826e502..60447b9d03 100644 --- a/apps/remix-ide-e2e/src/tests/debugger.test.ts +++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts @@ -95,7 +95,7 @@ module.exports = { .waitForElementVisible('#stepdetail') .waitForElementVisible({ locateStrategy: 'xpath', - selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"531")]', + selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"475")]', }) .getEditorValue((content) => { browser.assert.ok(content.indexOf(`constructor (string memory name_, string memory symbol_) { @@ -206,16 +206,40 @@ module.exports = { }, // depends on Should debug using generated sources 'Should call the debugger api: getTrace #group4': function (browser: NightwatchBrowser) { + let txhash browser - .addFile('test_jsGetTrace.js', { content: jsGetTrace }) + .clickLaunchIcon('udapp') + .perform((done) => { + browser.getLastTransactionHash((hash) => { + txhash = hash + done() + }) + }) + .perform((done) => { + browser.addFile('test_jsGetTrace.js', { content: jsGetTrace.replace('', txhash) }).perform(() => { + done() + }) + }) .executeScriptInTerminal('remix.exeCurrent()') .pause(3000) .waitForElementContainsText('*[data-id="terminalJournal"]', '{"gas":"0x5752","return":"0x0000000000000000000000000000000000000000000000000000000000000000","structLogs":', 60000) }, // depends on Should debug using generated sources 'Should call the debugger api: debug #group4': function (browser: NightwatchBrowser) { + let txhash browser - .addFile('test_jsDebug.js', { content: jsDebug }) + .clickLaunchIcon('udapp') + .perform((done) => { + browser.getLastTransactionHash((hash) => { + txhash = hash + done() + }) + }) + .perform((done) => { + browser.addFile('test_jsDebug.js', { content: jsDebug.replace('', txhash) }).perform(() => { + done() + }) + }) .executeScriptInTerminal('remix.exeCurrent()') .pause(3000) .clickLaunchIcon('debugger') @@ -495,7 +519,7 @@ const localVariable_step717_ABIEncoder = { // eslint-disable-line const jsGetTrace = `(async () => { try { - const result = await remix.call('debugger', 'getTrace', '0x00a9f5b1ac2c9cb93e5890ea86c81efbd36b619ef2378136ef74d8c6171ddda6') + const result = await remix.call('debugger', 'getTrace', '') console.log('result ', result) } catch (e) { console.log(e.message) @@ -504,7 +528,7 @@ const jsGetTrace = `(async () => { const jsDebug = `(async () => { try { - const result = await remix.call('debugger', 'debug', '0x00a9f5b1ac2c9cb93e5890ea86c81efbd36b619ef2378136ef74d8c6171ddda6') + const result = await remix.call('debugger', 'debug', '') console.log('result ', result) } catch (e) { console.log(e.message) diff --git a/apps/remix-ide-e2e/src/tests/eip1153.test.ts b/apps/remix-ide-e2e/src/tests/eip1153.test.ts new file mode 100644 index 0000000000..1f8598c49c --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/eip1153.test.ts @@ -0,0 +1,51 @@ +'use strict' + +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + '@disabled': true, + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + 'Should execute a contract that uses transient storage #group1': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('udapp') + .switchEnvironment('vm-cancun') // switch to a vm that know this eip. + .addFile('transient_storage.sol', { content: contractTransientStorage }) + .clickLaunchIcon('solidity') + .setSolidityCompilerVersion('soljson-v0.8.24+commit.e11b9ed9.js') + .click('*[data-id="scConfigExpander"]') + .setValue('#evmVersionSelector', 'cancun') // set target compilation to cancun + .clickLaunchIcon('solidity') + .verifyContracts(['TestTransientStorage']) + .clickLaunchIcon('udapp') + .createContract('') + .clickInstance(0) + .clickFunction('useTransientStorage - transact (not payable)') + .testFunction('last', + { + status: '0x1 Transaction mined and execution succeed', + 'decoded output': { + 0: 'uint256: out1 14', + 1: 'uint256: out2 15' + } + }) + .end() + } +} + +const contractTransientStorage = `// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.8.2 <0.9.0; + +contract TestTransientStorage { + function useTransientStorage() public returns (uint out1, uint out2) { + assembly { + tstore(0, 14) + tstore(1, 15) + out1 := tload(0) + out2 := tload(1) + } + } +}` diff --git a/apps/remix-ide-e2e/src/tests/etherscan_api.test.ts b/apps/remix-ide-e2e/src/tests/etherscan_api.test.ts index 6157d77ca2..48c208b58e 100644 --- a/apps/remix-ide-e2e/src/tests/etherscan_api.test.ts +++ b/apps/remix-ide-e2e/src/tests/etherscan_api.test.ts @@ -33,7 +33,7 @@ module.exports = { .execute(() => { (document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus() }, [], () => {}) - .setValue('[data-id="modalDialogCustomPromp"]', 'https://remix-goerli.ethdevops.io') + .setValue('[data-id="modalDialogCustomPromp"]', 'https://go.getblock.io/ee42d0a88f314707be11dd799b122cb9') // sepolia .modalFooterOKClick('basic-http-provider') .clickLaunchIcon('solidity') // compile .testContracts('Owner_1.sol', { content: verifiedContract }, ['Owner']) @@ -42,7 +42,7 @@ module.exports = { .frame(0) .click('[data-id="home"]') .setValue('select[name="contractName"]', 'Owner') - .setValue('*[name="contractAddress"]', ['0x9981c9d00103da481c3c65b22a79582a3e3ff50b', browser.Keys.TAB]) + .setValue('*[name="contractAddress"]', ['0xfF6A41815582cFD18855c5B90efD1d45784fd4f5', browser.Keys.TAB]) .click('[data-id="verify-contract"]') .waitForElementVisible('[data-id="verify-result"]') .waitForElementContainsText('[data-id="verify-result"]', 'Contract source code already verified', 15000) diff --git a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts index abf5f75f9a..4714be10f2 100644 --- a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts +++ b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts @@ -152,7 +152,7 @@ module.exports = { .scrollAndClick('*[data-id="settingsTabLocaleLabelen"]') .pause(2000) .assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'SETTINGS') - .assert.containsText('*[data-id="listenNetworkCheckInput"]', 'listen on all transactions') + .assert.containsText('*[data-id="listenNetworkCheckInput"]', 'Listen on all transactions') .assert.containsText('*[data-id="settingsTabGenerateContractMetadataLabel"]', 'Generate contract metadata') .assert.containsText('*[data-id="settingsAutoCompleteLabel"]', 'Enable code completion in editor') .assert.containsText('*[data-id="settingsShowGasLabel"]', 'Display gas estimates in editor') diff --git a/apps/remix-ide-e2e/src/tests/gist.test.ts b/apps/remix-ide-e2e/src/tests/gist.test.ts index ff0c60f23a..5b025e95f0 100644 --- a/apps/remix-ide-e2e/src/tests/gist.test.ts +++ b/apps/remix-ide-e2e/src/tests/gist.test.ts @@ -38,7 +38,7 @@ module.exports = { .executeScriptInTerminal(`remix.loadgist('${gistid}')`) // .perform((done) => { if (runtimeBrowser === 'chrome') { browser.openFile('gists') } done() }) .waitForElementVisible(`[data-id="treeViewLitreeViewItemREADME.txt"]`) - + .openFile(`README.txt`) // Remix publish to gist /* .click('*[data-id="fileExplorerNewFilepublishToGist"]') @@ -111,7 +111,7 @@ module.exports = { .click('[data-id="settingsTabRemoveGistToken"]') .clickLaunchIcon('filePanel') .click('*[data-id="workspacesMenuDropdown"]') - .click('*[data-id="workspacepublishToGist"]') + .click('*[data-id="workspacepublishToGist"]') .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(10000) @@ -145,24 +145,24 @@ module.exports = { .openFile(`README.txt`) .waitForElementVisible(`div[data-path='gist ${testData.validGistId}/README.txt']`) .assert.containsText(`div[data-path='gist ${testData.validGistId}/README.txt'] > span`, 'README.txt') - }, + }, - 'Load Gist from URL and verify truncated files are loaded #group3': function (browser: NightwatchBrowser) { - const gistId = '1b179bf1b92c8b0664b4cbe61774e15d' - browser - .url('http://127.0.0.1:8080/#gist=' + gistId) // loading the gist - .refreshPage() - .currentWorkspaceIs('gist ' + gistId) - .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 15000) - .waitForElementVisible(`#fileExplorerView li[data-path='contracts']`, 30000) - .openFile(`contracts/2_Owner.sol`) - .getEditorValue((content) => { - browser.assert.ok(content.indexOf('contract Owner {') !== -1) - }) - .click('*[data-id="workspacesMenuDropdown"]') - .click('*[data-id="workspacepublishToGist"]') - .modalFooterOKClick('fileSystem') - .waitForElementVisible('*[data-shared="tooltipPopup"]', 5000) - .assert.containsText('*[data-shared="tooltipPopup"]', 'Saving gist (' + gistId + ') ...') - } + 'Load Gist from URL and verify truncated files are loaded #group3': function (browser: NightwatchBrowser) { + const gistId = '1b179bf1b92c8b0664b4cbe61774e15d' + browser + .url('http://127.0.0.1:8080/#gist=' + gistId) // loading the gist + .refreshPage() + .currentWorkspaceIs('gist ' + gistId) + .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 15000) + .waitForElementVisible(`#fileExplorerView li[data-path='contracts']`, 30000) + .openFile(`contracts/2_Owner.sol`) + .getEditorValue((content) => { + browser.assert.ok(content.indexOf('contract Owner {') !== -1) + }) + .click('*[data-id="workspacesMenuDropdown"]') + .click('*[data-id="workspacepublishToGist"]') + .modalFooterOKClick('fileSystem') + .waitForElementVisible('*[data-shared="tooltipPopup"]', 5000) + .assert.containsText('*[data-shared="tooltipPopup"]', 'Saving gist (' + gistId + ') ...') + } } diff --git a/apps/remix-ide-e2e/src/tests/proxy_oz_v5_non_shanghai_runtime.test.ts b/apps/remix-ide-e2e/src/tests/proxy_oz_v5_non_shanghai_runtime.test.ts new file mode 100644 index 0000000000..fa2545c4e0 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/proxy_oz_v5_non_shanghai_runtime.test.ts @@ -0,0 +1,353 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +let firstProxyAddress: string +let lastProxyAddress: string +let shortenedFirstAddress: string +let shortenedLastAddress: string +module.exports = { + '@disabled': true, + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + + '@sources': function () { + return sources + }, + + + 'Should show deploy proxy option for UUPS upgradeable contract #group1': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('udapp') + .switchEnvironment('vm-paris') // this runtime doesn't have the PUSH0 opcode. + .clickLaunchIcon('solidity') + .click('.remixui_compilerConfigSection') + .setValue('#evmVersionSelector', 'paris') // set an evm version which doesn't have PUSH0 opcode. + .clickLaunchIcon('filePanel') + .addFile('myTokenV1.sol', sources[0]['myTokenV1.sol']) + .clickLaunchIcon('solidity') + .pause(2000) + // because the compilatiom imports are slow and sometimes stop loading (not sure why, it's bug) we need to recompile and check to see if the files are really in de FS + .click('[data-id="compilerContainerCompileBtn"]') + .clickLaunchIcon('filePanel') + .isVisible({ + selector: '*[data-id="treeViewDivtreeViewItem.deps/npm/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"]', + timeout: 120000, + suppressNotFoundErrors: true + }) + .clickLaunchIcon('solidity') + .click('[data-id="compilerContainerCompileBtn"]') + .clickLaunchIcon('filePanel') + .isVisible({ + selector: '*[data-id="treeViewDivtreeViewItem.deps/npm/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"]', + timeout: 120000, + suppressNotFoundErrors: true + }) + .clickLaunchIcon('solidity') + .click('[data-id="compilerContainerCompileBtn"]') + .clickLaunchIcon('filePanel') + .waitForElementVisible({ + selector: '*[data-id="treeViewDivtreeViewItem.deps/npm/@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"]', + timeout: 120000, + }) + .clickLaunchIcon('solidity') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyToken]') + .waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]') + .waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]') + }, + + 'Should show upgrade proxy option for child contract inheriting UUPS parent contract #group1': function (browser: NightwatchBrowser) { + browser + .addFile('myTokenV2.sol', sources[1]['myTokenV2.sol']) + .clickLaunchIcon('solidity') + .assert.visible('[data-id="compilerContainerCompileBtn"]') + .click('[data-id="compilerContainerCompileBtn"]') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyTokenV2]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyTokenV2]') + .waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]') + .waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]') + }, + + 'Should deploy proxy without initialize parameters #group1': function (browser: NightwatchBrowser) { + browser + .openFile('myTokenV1.sol') + .clickLaunchIcon('solidity') + .assert.visible('[data-id="compilerContainerCompileBtn"]') + .click('[data-id="compilerContainerCompileBtn"]') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyToken]') + .verify.visible('[data-id="contractGUIDeployWithProxyLabel"]') + .waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]') + .click('[data-id="contractGUIDeployWithProxyLabel"]') + .setValue('[data-id="initializeInputs-initialOwner"]', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4') + .createContract('') + .waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Proxy (ERC1967)') + .waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]') + .click('[data-id="udappNotify-modal-footer-ok-react"]') + .waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Deploy Proxy (ERC1967)') + .waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]') + .click('[data-id="confirmProxyDeployment-modal-footer-ok-react"]') + .waitForElementPresent('[data-id="universalDappUiTitleExpander0"]') + .waitForElementPresent('[data-id="universalDappUiTitleExpander1"]') + .waitForElementContainsText('*[data-id="terminalJournal"]', 'Deploying ERC1967 >= 5.0.0 as proxy...') + }, + + 'Should interact with deployed contract via ERC1967 (proxy) #group1': function (browser: NightwatchBrowser) { + browser + .getAddressAtPosition(1, (address) => { + firstProxyAddress = address + shortenedFirstAddress = address.slice(0, 5) + '...' + address.slice(address.length - 5, address.length) + }) + .clickInstance(1) + .perform((done) => { + browser.testConstantFunction(firstProxyAddress, 'name - call', null, '0:\nstring: MyToken').perform(() => { + done() + }) + }) + .perform((done) => { + browser.testConstantFunction(firstProxyAddress, 'symbol - call', null, '0:\nstring: MTK').perform(() => { + done() + }) + }) + }, + + 'Should deploy proxy with initialize parameters #group1': function (browser: NightwatchBrowser) { + browser + .waitForElementPresent('[data-id="deployAndRunClearInstances"]') + .click('[data-id="deployAndRunClearInstances"]') + .addFile('initializeProxy.sol', sources[2]['initializeProxy.sol']) + .clickLaunchIcon('solidity') + .assert.visible('[data-id="compilerContainerCompileBtn"]') + .click('[data-id="compilerContainerCompileBtn"]') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyInitializedToken]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyInitializedToken]') + .waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]') + .click('[data-id="contractGUIDeployWithProxyLabel"]') + .useXpath() + .waitForElementPresent('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[1]/input') + .waitForElementPresent('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[2]/input') + .setValue('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[1]/input', 'Remix') + .setValue('//*[@id="runTabView"]/div/div[2]/div[3]/div[1]/div/div[1]/div[4]/div/div[2]/input', "R") + .useCss() + .setValue('[data-id="initializeInputs-initialOwner"]', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4') + .createContract('') + .waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Proxy (ERC1967)') + .waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]') + .click('[data-id="udappNotify-modal-footer-ok-react"]') + .waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Deploy Proxy (ERC1967)') + .waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]') + .click('[data-id="confirmProxyDeployment-modal-footer-ok-react"]') + .waitForElementPresent('[data-id="universalDappUiTitleExpander0"]') + .waitForElementPresent('[data-id="universalDappUiTitleExpander1"]') + .waitForElementContainsText('*[data-id="terminalJournal"]', 'Deploying ERC1967 >= 5.0.0 as proxy...') + }, + + 'Should interact with initialized contract to verify parameters #group1': function (browser: NightwatchBrowser) { + browser + .getAddressAtPosition(1, (address) => { + lastProxyAddress = address + shortenedLastAddress = address.slice(0, 5) + '...' + address.slice(address.length - 5, address.length) + }) + .clickInstance(1) + .perform((done) => { + browser.testConstantFunction(lastProxyAddress, 'name - call', null, '0:\nstring: Remix').perform(() => { + done() + }) + }) + .perform((done) => { + browser.testConstantFunction(lastProxyAddress, 'symbol - call', null, '0:\nstring: R').perform(() => { + done() + }) + }) + }, + + 'Should upgrade contract by selecting a previously deployed proxy address from dropdown (MyTokenV1 to MyTokenV2) #group1': function (browser: NightwatchBrowser) { + browser + .click('*[data-id="terminalClearConsole"]') + .waitForElementPresent('[data-id="deployAndRunClearInstances"]') + .click('[data-id="deployAndRunClearInstances"]') + .openFile('myTokenV2.sol') + .clickLaunchIcon('solidity') + .assert.visible('[data-id="compilerContainerCompileBtn"]') + .click('[data-id="compilerContainerCompileBtn"]') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyTokenV2]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyTokenV2]') + .waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]') + .click('[data-id="contractGUIUpgradeImplementationLabel"]') + .waitForElementPresent('[data-id="toggleProxyAddressDropdown"]') + .click('[data-id="toggleProxyAddressDropdown"]') + .waitForElementVisible('[data-id="proxy-dropdown-items"]') + .assert.textContains('[data-id="proxy-dropdown-items"]', shortenedFirstAddress) + .assert.textContains('[data-id="proxy-dropdown-items"]', shortenedLastAddress) + + .click('[data-id="proxyAddress1"]') + .createContract('') + .waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Update Proxy') + .waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]') + .click('[data-id="udappNotify-modal-footer-ok-react"]') + .waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Update Proxy (ERC1967)') + .waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]') + .click( + { + selector: '[data-id="confirmProxyDeployment-modal-footer-ok-react"]', + }) + .waitForElementPresent('[data-id="universalDappUiTitleExpander0"]') + .waitForElementPresent('[data-id="universalDappUiTitleExpander1"]') + .waitForElementContainsText('*[data-id="terminalJournal"]', 'Using ERC1967 >= 5.0.0 for the proxy upgrade...') + }, + + 'Should interact with upgraded function in contract MyTokenV2 #group1': function (browser: NightwatchBrowser) { + browser + .clickInstance(1) + .perform((done) => { + browser.testConstantFunction(lastProxyAddress, 'version - call', null, '0:\nstring: MyTokenV2!').perform(() => { + done() + }) + }) + }, + + 'Should upgrade contract by providing proxy address in input field (MyTokenV1 to MyTokenV2) #group1': function (browser: NightwatchBrowser) { + browser + .click('*[data-id="terminalClearConsole"]') + .waitForElementPresent('[data-id="deployAndRunClearInstances"]') + .click('[data-id="deployAndRunClearInstances"]') + .openFile('myTokenV2.sol') + .clickLaunchIcon('solidity') + .assert.visible('[data-id="compilerContainerCompileBtn"]') + .click('[data-id="compilerContainerCompileBtn"]') + .waitForElementPresent('select[id="compiledContracts"] option[value=MyTokenV2]', 60000) + .clickLaunchIcon('udapp') + .click('select.udapp_contractNames') + .click('select.udapp_contractNames option[value=MyTokenV2]') + .waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]') + .waitForElementPresent('[data-id="toggleProxyAddressDropdown"]') + .clearValue('[data-id="ERC1967AddressInput"]') + .setValue('[data-id="ERC1967AddressInput"]', firstProxyAddress) + .createContract('') + .waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Update Proxy') + .waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]') + .click('[data-id="udappNotify-modal-footer-ok-react"]') + .waitForElementContainsText('[data-id="confirmProxyDeploymentModalDialogModalTitle-react"]', 'Confirm Update Proxy (ERC1967)') + .waitForElementVisible('[data-id="confirmProxyDeployment-modal-footer-ok-react"]') + .click('[data-id="confirmProxyDeployment-modal-footer-ok-react"]') + .waitForElementPresent('[data-id="universalDappUiTitleExpander0"]') + .waitForElementPresent('[data-id="universalDappUiTitleExpander1"]') + .waitForElementContainsText('*[data-id="terminalJournal"]', 'Using ERC1967 >= 5.0.0 for the proxy upgrade...') + }, + + 'Should interact with upgraded contract through provided proxy address #group1': function (browser: NightwatchBrowser) { + browser + .clearConsole() + .clickInstance(1) + .perform((done) => { + browser.testConstantFunction(firstProxyAddress, 'version - call', null, '0:\nstring: MyTokenV2!').perform(() => { + done() + }) + }) + }, + 'Should debug the call': function(browser: NightwatchBrowser) { + browser + .debugTransaction(0) + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"7")]', + timeout: 60000 + }) + .goToVMTraceStep(129) + .waitForElementContainsText('*[data-id="functionPanel"]', 'version()', 60000) + .end() + } +} + + +const sources = [ + { + 'myTokenV1.sol': { + content: ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; + + import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; + import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + + contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(address initialOwner) initializer public { + __ERC721_init("MyToken", "MTK"); + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); + } + + function _authorizeUpgrade(address newImplementation) + internal + onlyOwner + override + {} + } + ` + } + }, { + 'myTokenV2.sol': { + content: ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; + import "./myTokenV1.sol"; + + contract MyTokenV2 is MyToken { + function version () public view returns (string memory) { + return "MyTokenV2!"; + } + } + ` + } + }, { + 'initializeProxy.sol': { + content: ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; + + import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; + import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + + contract MyInitializedToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(string memory tokenName, string memory tokenSymbol, address initialOwner) initializer public { + __ERC721_init(tokenName, tokenSymbol); + __Ownable_init(initialOwner); + __UUPSUpgradeable_init(); + } + + function _authorizeUpgrade(address newImplementation) + internal + onlyOwner + override + {} + } + ` + } + } +] \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts index cebc84bc93..24388def61 100644 --- a/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts +++ b/apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts @@ -33,7 +33,7 @@ module.exports = { 'Should sign message using account key #group2': function (browser: NightwatchBrowser) { browser.waitForElementVisible('*[data-id="settingsRemixRunSignMsg"]') - .switchEnvironment('vm-merge') + .switchEnvironment('vm-paris') .pause(2000) .click('*[data-id="settingsRemixRunSignMsg"]') .pause(2000) @@ -82,21 +82,21 @@ module.exports = { instanceAddress = address console.log('instanceAddress', instanceAddress) browser - .waitForElementVisible(`#instance${instanceAddress} [data-id="instanceContractBal"]`) + .waitForElementVisible(`#instance${instanceAddress} [data-id="instanceContractBal"]`) //*[@id="instance0xbBF289D846208c16EDc8474705C748aff07732dB" and contains(.,"Balance") and contains(.,'0.000000000000000111')] - .waitForElementVisible({ - locateStrategy: 'xpath', - selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000111')]`, - timeout: 60000 - }) + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000111')]`, + timeout: 60000 + }) //.waitForElementContainsText(`#instance${instanceAddress} [data-id="instanceContractBal"]`, 'Balance: 0.000000000000000111 ETH', 60000) - .clickFunction('sendSomeEther - transact (not payable)', { types: 'uint256 num', values: '2' }) - .pause(1000) - .waitForElementVisible({ - locateStrategy: 'xpath', - selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000109')]`, - timeout: 60000 - }) + .clickFunction('sendSomeEther - transact (not payable)', { types: 'uint256 num', values: '2' }) + .pause(1000) + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: `//*[@id="instance${instanceAddress}" and contains(.,"Balance") and contains(.,'0.000000000000000109')]`, + timeout: 60000 + }) }) }, @@ -238,6 +238,95 @@ module.exports = { .executeScriptInTerminal('web3.eth.getAccounts()') .journalLastChildIncludes('[ "0x76a3ABb5a12dcd603B52Ed22195dED17ee82708f" ]') .end() + }, + + 'Should ensure that save environment state is checked by default #group4 #group5': function (browser: NightwatchBrowser) { + browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') + .clickLaunchIcon('settings') + .waitForElementPresent('[data-id="settingsEnableSaveEnvStateLabel"]') + .scrollInto('[data-id="settingsEnableSaveEnvStateLabel"]') + .verify.elementPresent('[data-id="settingsEnableSaveEnvState"]:checked') + }, + + 'Should deploy default storage contract; store value and ensure that state is saved. #group4 #group5': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('filePanel') + .click('*[data-id="treeViewLitreeViewItemcontracts"]') + .openFile('contracts/1_Storage.sol') + .pause(5000) + .clickLaunchIcon('udapp') + .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') + .click('*[data-id="Deploy - transact (not payable)"]') + .waitForElementPresent('#instance0xd9145CCE52D386f254917e481eB44e9943F39138') + .clickInstance(0) + .clickFunction('store - transact (not payable)', { types: 'uint256 num', values: '10' }) + .clickFunction('retrieve - call') + .waitForElementContainsText('[data-id="treeViewLi0"]', 'uint256: 10') + .clickLaunchIcon('filePanel') + .openFile('.states/vm-cancun/state.json') + .getEditorValue((content) => { + browser + .assert.ok(content.includes('"latestBlockNumber": "0x2"'), 'State is saved') + }) + }, + + 'Should load state after page refresh #group4': function (browser: NightwatchBrowser) { + browser.refreshPage() + .waitForElementVisible('*[data-id="remixIdeSidePanel"]') + .click('*[data-id="treeViewLitreeViewItemcontracts"]') + .openFile('contracts/1_Storage.sol') + .addAtAddressInstance('0xd9145CCE52D386f254917e481eB44e9943F39138', true, true, false) + .clickInstance(0) + .clickFunction('retrieve - call') + .waitForElementContainsText('[data-id="treeViewLi0"]', 'uint256: 10') + }, + + 'Should save state after running web3 script #group4': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .waitForElementPresent('[data-id="settingsTabGenerateContractMetadataLabel"]') + .click('[data-id="settingsTabGenerateContractMetadataLabel"]') + .verify.elementPresent('[data-id="settingsTabGenerateContractMetadata"]:checked') + .clickLaunchIcon('solidity') + .click('.remixui_compilerConfigSection') + .setValue('#evmVersionSelector', 'london') + .click('*[data-id="compilerContainerCompileBtn"]') + .pause(5000) + .clickLaunchIcon('udapp') + .switchEnvironment('vm-london') + .clickLaunchIcon('filePanel') + .click('*[data-id="treeViewLitreeViewItemscripts"]') + .openFile('scripts/deploy_with_web3.ts') + .click('[data-id="play-editor"]') + .waitForElementPresent('[data-id="treeViewDivDraggableItem.states/vm-london/state.json"]') + .click('[data-id="treeViewDivDraggableItem.states/vm-london/state.json"]') + .pause(100000) + .getEditorValue((content) => { + browser + .assert.ok(content.includes('"latestBlockNumber": "0x1"'), 'State is saved') + }) + }, + + 'Should ensure that .states is not updated when save env option is unchecked #group5': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .waitForElementPresent('[data-id="settingsEnableSaveEnvStateLabel"]') + .click('[data-id="settingsEnableSaveEnvStateLabel"]') + .verify.elementNotPresent('[data-id="settingsEnableSaveEnvState"]:checked') + .clickLaunchIcon('filePanel') + .openFile('contracts/1_Storage.sol') + .pause(5000) + .clickLaunchIcon('udapp') + .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') + .click('*[data-id="Deploy - transact (not payable)"]') + .pause(5000) + .clickLaunchIcon('filePanel') + .openFile('.states/vm-cancun/state.json') + .getEditorValue((content) => { + browser + .assert.ok(content.includes('"latestBlockNumber": "0x2"'), 'State is unchanged') + }) + .end() } } diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts index db80725552..3c195c99cd 100644 --- a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts @@ -339,8 +339,12 @@ module.exports = { 'Basic Solidity Unit tests with local compiler #group6': function (browser: NightwatchBrowser) { browser + .clickLaunchIcon('udapp') + .switchEnvironment('vm-cancun') .clickLaunchIcon('solidity') .setSolidityCompilerVersion('builtin') + .click('.remixui_compilerConfigSection') + .setValue('#evmVersionSelector', 'cancun') // Temporary fix .clickLaunchIcon('filePanel') .click('*[data-id="treeViewLitreeViewItemcontracts"]') .openFile('contracts/3_Ballot.sol') diff --git a/apps/remix-ide-e2e/src/tests/specialFunctions.test.ts b/apps/remix-ide-e2e/src/tests/specialFunctions.test.ts index 65ffb46186..0a5942bd07 100644 --- a/apps/remix-ide-e2e/src/tests/specialFunctions.test.ts +++ b/apps/remix-ide-e2e/src/tests/specialFunctions.test.ts @@ -129,7 +129,7 @@ module.exports = { }) }) }, - 'Use special functions receive/fallback - only fallback is diclared and is payable, sending data and wei #group3': function (browser: NightwatchBrowser) { + 'Use special functions receive/fallback - only fallback is declared and is payable, sending data and wei #group3': function (browser: NightwatchBrowser) { // don't need to redeploy it, same contract browser.perform((done) => { browser.getAddressAtPosition(0, (address) => { diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 0127b830ef..eb5074e3ca 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -319,7 +319,7 @@ module.exports = { .execute(() => { (document.querySelector('*[data-id="vm-custom-forkModalDialogContainer-react"] input[data-id="CustomForkEvmType"]') as any).focus() }, [], () => { }) - .click('*[data-id="CustomForkEvmType"] [value="merge"]') + .click('*[data-id="CustomForkEvmType"] [value="cancun"]') .pause(5000) .modalFooterOKClick('vm-custom-fork') .waitForElementPresent({ diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index 605c731cd3..04b0cb9642 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -107,6 +107,46 @@ module.exports = { }) }, + 'Should load Blockscout verified contracts from URL "address" and "blockscout" params (single source)': function (browser: NightwatchBrowser) { + browser + .url('http://127.0.0.1:8080/#address=0xdAC17F958D2ee523a2206206994597C13D831ec7&blockscout=eth.blockscout.com') + .refreshPage() + .pause(7000) + .currentWorkspaceIs('code-sample') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0xdAC17F958D2ee523a2206206994597C13D831ec7"]') + .getEditorValue((content) => { + browser.assert.ok(content && content.indexOf( + 'contract TetherToken is Pausable, StandardToken, BlackList {') !== -1) + + }) + }, + + 'Should load Blockscout verified contracts from URL "address" and "blockscout" params (multiple sources)': function (browser: NightwatchBrowser) { + browser + .url('http://127.0.0.1:8080/#address=0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9&blockscout=eth.blockscout.com') + .refreshPage() + .pause(7000) + .currentWorkspaceIs('code-sample') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/Address.sol"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/BaseAdminUpgradeabilityProxy.sol"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/BaseUpgradeabilityProxy.sol"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/InitializableAdminUpgradeabilityProxy.sol"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/InitializableUpgradeabilityProxy.sol"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/Proxy.sol"]') + .assert.elementPresent('*[data-id="treeViewLitreeViewItemeth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/UpgradeabilityProxy.sol"]') + .openFile('eth.blockscout.com/0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9/contracts/open-zeppelin/InitializableAdminUpgradeabilityProxy.sol') + .getEditorValue((content) => { + browser.assert.ok(content && content.indexOf( + 'contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy {') !== -1) + }) + + }, + 'Should load the code from URL & code params #group1': function (browser: NightwatchBrowser) { browser .url('http://127.0.0.1:8080/#optimize=true&runs=300&url=https://github.com/ethereum/remix-project/blob/master/apps/remix-ide/contracts/app/solidity/mode.sol&code=cHJhZ21hIHNvbGlkaXR5ID49MC42LjAgPDAuNy4wOwoKaW1wb3J0ICJodHRwczovL2dpdGh1Yi5jb20vT3BlblplcHBlbGluL29wZW56ZXBwZWxpbi1jb250cmFjdHMvYmxvYi9tYXN0ZXIvY29udHJhY3RzL2FjY2Vzcy9Pd25hYmxlLnNvbCI7Cgpjb250cmFjdCBHZXRQYWlkIGlzIE93bmFibGUgewogIGZ1bmN0aW9uIHdpdGhkcmF3KCkgZXh0ZXJuYWwgb25seU93bmVyIHsKICB9Cn0') diff --git a/apps/remix-ide-e2e/src/tests/vyper_api.test.ts b/apps/remix-ide-e2e/src/tests/vyper_api.test.ts index 3ac5b65610..ba77570263 100644 --- a/apps/remix-ide-e2e/src/tests/vyper_api.test.ts +++ b/apps/remix-ide-e2e/src/tests/vyper_api.test.ts @@ -67,7 +67,6 @@ module.exports = { browser // @ts-ignore .frame(0) - .click('[data-id="remote-compiler"]') .click('[data-id="compile"]') .waitForElementVisible({ selector:'[data-id="compilation-details"]', @@ -89,7 +88,6 @@ module.exports = { chromeBrowser.setPermission('clipboard-write', 'granted') browser .frame(0) - .click('[data-id="remote-compiler"]') .click('[data-id="compile"]') .waitForElementVisible({ selector:'[data-id="compilation-details"]', diff --git a/apps/remix-ide-e2e/yarn.lock b/apps/remix-ide-e2e/yarn.lock index e5e1999010..fe1b407d64 100644 --- a/apps/remix-ide-e2e/yarn.lock +++ b/apps/remix-ide-e2e/yarn.lock @@ -14,15 +14,15 @@ pathval "1.1.1" type-detect "4.0.8" -"@openzeppelin/contracts-upgradeable@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.0.0.tgz#859c00c55f04b6dda85b3c88bce507d65019888f" - integrity sha512-D54RHzkOKHQ8xUssPgQe2d/U92mwaiBDY7qCCVGq6VqwQjsT3KekEQ3bonev+BLP30oZ0R1U6YC8/oLpizgC5Q== +"@openzeppelin/contracts-upgradeable@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.0.2.tgz#3e5321a2ecdd0b206064356798c21225b6ec7105" + integrity sha512-0MmkHSHiW2NRFiT9/r5Lu4eJq5UJ4/tzlOgYXNAIj/ONkQTVnz22pLxDvp4C4uZ9he7ZFvGn3Driptn1/iU7tQ== -"@openzeppelin/contracts@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.0.tgz#ee0e4b4564f101a5c4ee398cd4d73c0bd92b289c" - integrity sha512-bv2sdS6LKqVVMLI5+zqnNrNU/CA+6z6CmwFXm/MzmOPBRSO5reEJN7z0Gbzvs0/bv/MZZXNklubpwy3v2+azsw== +"@openzeppelin/contracts@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.0.2.tgz#b1d03075e49290d06570b2fd42154d76c2a5d210" + integrity sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA== "@openzeppelin/upgrades-core@^1.30.0": version "1.30.1" @@ -1437,9 +1437,9 @@ flat@^5.0.2: integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== follow-redirects@^1.0.0, follow-redirects@^1.15.0: - version "1.15.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" - integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== for-each@^0.3.3: version "0.3.3" diff --git a/apps/remix-ide/ci/downloadsoljson.sh b/apps/remix-ide/ci/downloadsoljson.sh index ddbe6b5198..4d3d93932f 100644 --- a/apps/remix-ide/ci/downloadsoljson.sh +++ b/apps/remix-ide/ci/downloadsoljson.sh @@ -1,34 +1,45 @@ #!/usr/bin/env bash -echo "Downloading latest soljson.js from https://binaries.soliditylang.org/wasm/list.json" +echo "Downloading specified soljson.js version based on defaultVersion in package.json" + set -e -# check if curl is installed -if ! command -v curl &> /dev/null -then + +# Check if curl and jq are installed +if ! command -v curl &> /dev/null; then echo "curl could not be found" - exit + exit 1 fi +# Read the defaultVersion from package.json +defaultVersion=$(grep '"defaultVersion"' package.json | awk -F '"' '{print $4}') +echo "Specified version from package.json: $defaultVersion" -# download https://binaries.soliditylang.org/wasm/list.json as json +# Download the list.json file containing available versions curl -s https://binaries.soliditylang.org/wasm/list.json > list.json -# get the latest version without jq -latest=$(grep 'latestRelease' list.json | cut -d '"' -f 4) -echo "latest version: $latest" -# get url -url=$(grep "\"$latest\":" list.json | cut -d '"' -f 4) -echo "url: $url" -path="https://binaries.soliditylang.org/bin/$url" -echo "path: $path" -# download the file to ./apps/remix-ide/src/assets/js/soljson.js -curl -s $path > ./apps/remix-ide/src/assets/js/soljson.js -# if directory ./apps/remix-ide/src/assets/js/soljson does not exist, create it + +# Use jq to extract the path for the specified version from the builds array +check=$(grep "\"$defaultVersion\"" list.json) +if [ -z "$check" ]; then + echo "The specified version $defaultVersion could not be found in the list" + exit 1 +fi + +echo "Path for the specified version: $defaultVersion" +fullPath="https://binaries.soliditylang.org/bin/$defaultVersion" +echo "Download fullPath: $fullPath" + +# Ensure the target directory exists if [ ! -d "./apps/remix-ide/src/assets/js/soljson" ]; then - mkdir ./apps/remix-ide/src/assets/js/soljson + mkdir -p ./apps/remix-ide/src/assets/js/soljson fi -cp ./apps/remix-ide/src/assets/js/soljson.js ./apps/remix-ide/src/assets/js/soljson/$url + +# Download the file to ./apps/remix-ide/src/assets/js/soljson.js +echo "Downloading soljson.js from "$fullPath" to ./apps/remix-ide/src/assets/js/soljson.js" +curl -s "$fullPath" > ./apps/remix-ide/src/assets/js/soljson.js + +# Copy the downloaded soljson.js to the specific version directory +cp ./apps/remix-ide/src/assets/js/soljson.js "./apps/remix-ide/src/assets/js/soljson/$path" cp list.json ./apps/remix-ide/src/assets/list.json -# remove list.json +# Clean up by removing the list.json rm list.json - diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 017cceb9b3..5865637fd9 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -28,7 +28,7 @@ import {StoragePlugin} from './app/plugins/storage' import {Layout} from './app/panels/layout' import {NotificationPlugin} from './app/plugins/notification' import {Blockchain} from './blockchain/blockchain' -import {MergeVMProvider, LondonVMProvider, BerlinVMProvider, ShanghaiVMProvider} from './app/providers/vm-provider' +import {MergeVMProvider, LondonVMProvider, BerlinVMProvider, ShanghaiVMProvider, CancunVMProvider} from './app/providers/vm-provider' import {MainnetForkVMProvider} from './app/providers/mainnet-vm-fork-provider' import {SepoliaForkVMProvider} from './app/providers/sepolia-vm-fork-provider' import {GoerliForkVMProvider} from './app/providers/goerli-vm-fork-provider' @@ -264,6 +264,7 @@ class AppComponent { const vmProviderSepoliaFork = new SepoliaForkVMProvider(blockchain) const vmProviderGoerliFork = new GoerliForkVMProvider(blockchain) const vmProviderShanghai = new ShanghaiVMProvider(blockchain) + const vmProviderCancun = new CancunVMProvider(blockchain) const vmProviderMerge = new MergeVMProvider(blockchain) const vmProviderBerlin = new BerlinVMProvider(blockchain) const vmProviderLondon = new LondonVMProvider(blockchain) @@ -338,6 +339,7 @@ class AppComponent { dGitProvider, storagePlugin, vmProviderShanghai, + vmProviderCancun, vmProviderMerge, vmProviderBerlin, vmProviderLondon, diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index ef1ecaca66..045faa3c7b 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -52,7 +52,8 @@ class Editor extends Plugin { cairo: 'cairo', ts: 'typescript', move: 'move', - circom: 'circom' + circom: 'circom', + nr: 'rust' } this.activated = false diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 23d47cd094..b4b1a1ab9c 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -24,7 +24,7 @@ const profile = { methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'writeFileNoRewrite', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', - 'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodule' + 'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodule', 'copyFolderToJson' ], kind: 'file-system' } @@ -1041,6 +1041,14 @@ class FileManager extends Plugin { throw new Error(e) } } + + async copyFolderToJson(folder: string) { + const provider = this.currentFileProvider() + if (provider && provider.copyFolderToJson) { + return await provider.copyFolderToJson(folder) + } + throw new Error('copyFolderToJson not available') + } } module.exports = FileManager diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 984efe7534..fa6521023a 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -224,9 +224,23 @@ export class TabProxy extends Plugin { this.removeTab(oldName) } + /** + * + * @param {string} name + * @param {string} title + * @param {Function} switchTo + * @param {Function} close + * @param {string} icon + * @param {string} description + * @returns + */ addTab (name, title, switchTo, close, icon, description = '') { if (this._handlers[name]) return this.renderComponent() + if ((name.endsWith('.vy') && icon === undefined) || title.includes('Vyper')) { + icon = 'assets/img/vyperLogo2.webp' + } + var slash = name.split('/') const tabPath = slash.reverse() const tempTitle = [] @@ -292,7 +306,7 @@ export class TabProxy extends Plugin { if (!previous && tab.name === name) { if(index - 1 >= 0 && this.loadedTabs[index - 1]) previous = this.loadedTabs[index - 1] - else if (index + 1 && this.loadedTabs[index + 1]) + else if (index + 1 && this.loadedTabs[index + 1]) previous = this.loadedTabs[index + 1] } return tab.name !== name diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.tsx similarity index 78% rename from apps/remix-ide/src/app/panels/terminal.js rename to apps/remix-ide/src/app/panels/terminal.tsx index 5ad90adf00..3b93cd5c22 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.tsx @@ -1,12 +1,12 @@ /* global Node, requestAnimationFrame */ // eslint-disable-line import React from 'react' // eslint-disable-line -import { RemixUiTerminal } from '@remix-ui/terminal' // eslint-disable-line +import { RemixUiTerminal, RemixUITerminalWrapper } from '@remix-ui/terminal' // eslint-disable-line import { Plugin } from '@remixproject/engine' import * as packageJson from '../../../../../package.json' import {Registry} from '@remix-project/remix-lib' import { PluginViewWrapper } from '@remix-ui/helper' import vm from 'vm' -const EventManager = require('../../lib/events') +import EventManager from '../../lib/events' import { CompilerImports } from '@remix-project/core-plugin' // eslint-disable-line import { RemixUiXterminals } from '@remix-ui/xterm' @@ -26,6 +26,34 @@ const profile = { } class Terminal extends Plugin { + fileImport: CompilerImports + event: any + globalRegistry: Registry + element: HTMLDivElement + eventsDecoder: any + txListener: any + _deps: { fileManager: any; editor: any; compilersArtefacts: any; offsetToLineColumnConverter: any } + commandHelp: { 'remix.loadgist(id)': string; 'remix.loadurl(url)': string; 'remix.execute(filepath)': string; 'remix.exeCurrent()': string; 'remix.help()': string } + blockchain: any + vm: typeof vm + _api: any + _opts: any + config: any + version: string + data: { + lineLength: any // ???? + session: any[]; activeFilters: { commands: any; input: string }; filterFns: any + } + _view: { el: any; bar: any; input: any; term: any; journal: any; cli: any } + _components: any + _commands: any + commands: any + _JOURNAL: any[] + _jobs: any[] + _INDEX: any + _shell: any + dispatch: any + terminalApi: any constructor(opts, api) { super(profile) this.fileImport = new CompilerImports() @@ -75,7 +103,7 @@ class Terminal extends Plugin { this._INDEX.commandsMain = {} if (opts.shell) this._shell = opts.shell // ??? register(this) - this.event.register('debuggingRequested', async (hash) => { + this.event.register('debuggingRequested', async (hash: any) => { // TODO should probably be in the run module if (!await this._opts.appManager.isActive('debugger')) await this._opts.appManager.activatePlugin('debugger') this.call('menuicons', 'select', 'debugger') @@ -114,13 +142,12 @@ class Terminal extends Plugin { } updateComponent(state) { - return (Registry.getInstance().get('platform').api.isDesktop()) ? - - : + />) } renderComponent() { diff --git a/apps/remix-ide/src/app/providers/abstract-provider.tsx b/apps/remix-ide/src/app/providers/abstract-provider.tsx index ea4cc5e281..e169e048d6 100644 --- a/apps/remix-ide/src/app/providers/abstract-provider.tsx +++ b/apps/remix-ide/src/app/providers/abstract-provider.tsx @@ -119,7 +119,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider { } this.call('notification', 'alert', modalContent) } - await this.call('udapp', 'setEnvironmentMode', {context: 'vm-merge'}) + await this.call('udapp', 'setEnvironmentMode', {context: 'vm-paris'}) return } diff --git a/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx b/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx index e7f288448f..656cfa7a56 100644 --- a/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx +++ b/apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx @@ -8,16 +8,16 @@ export class MainnetForkVMProvider extends BasicVMProvider { super( { name: 'vm-mainnet-fork', - displayName: 'Mainet fork -Remix VM (London)', + displayName: 'Mainnet fork - Remix VM (Cancun)', kind: 'provider', - description: 'Remix VM (London)', + description: 'Remix VM (Cancun)', methods: ['sendAsync', 'init'], version: packageJson.version }, blockchain ) this.blockchain = blockchain - this.fork = 'shanghai' + this.fork = 'cancun' this.nodeUrl = 'https://go.getblock.io/56f8bc5187aa4ac696348f67545acf38' this.blockNumber = 'latest' } diff --git a/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx b/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx index b88b4a4439..26b82404b4 100644 --- a/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx +++ b/apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx @@ -8,7 +8,7 @@ export class SepoliaForkVMProvider extends BasicVMProvider { super( { name: 'vm-sepolia-fork', - displayName: 'Sepolia fork - Remix VM (London)', + displayName: 'Sepolia fork - Remix VM (Cancun)', kind: 'provider', description: 'Remix VM (London)', methods: ['sendAsync', 'init'], @@ -17,7 +17,7 @@ export class SepoliaForkVMProvider extends BasicVMProvider { blockchain ) this.blockchain = blockchain - this.fork = 'shanghai' + this.fork = 'cancun' this.nodeUrl = 'https://go.getblock.io/ee42d0a88f314707be11dd799b122cb9' this.blockNumber = 'latest' } diff --git a/apps/remix-ide/src/app/providers/vm-provider.tsx b/apps/remix-ide/src/app/providers/vm-provider.tsx index aa1583f596..94c25ffdfc 100644 --- a/apps/remix-ide/src/app/providers/vm-provider.tsx +++ b/apps/remix-ide/src/app/providers/vm-provider.tsx @@ -46,17 +46,17 @@ export class MergeVMProvider extends BasicVMProvider { constructor(blockchain) { super( { - name: 'vm-merge', - displayName: 'Remix VM (Merge)', + name: 'vm-paris', + displayName: 'Remix VM (Paris)', kind: 'provider', - description: 'Remix VM (Merge)', + description: 'Remix VM (Paris)', methods: ['sendAsync', 'init'], version: packageJson.version }, blockchain ) this.blockchain = blockchain - this.fork = 'merge' + this.fork = 'paris' } } @@ -113,3 +113,21 @@ export class ShanghaiVMProvider extends BasicVMProvider { this.fork = 'shanghai' } } + +export class CancunVMProvider extends BasicVMProvider { + constructor(blockchain) { + super( + { + name: 'vm-cancun', + displayName: 'Remix VM (Cancun)', + kind: 'provider', + description: 'Remix VM (Cancun)', + methods: ['sendAsync', 'init'], + version: packageJson.version + }, + blockchain + ) + this.blockchain = blockchain + this.fork = 'cancun' + } +} diff --git a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json index 05fef4beab..fd217f921f 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json @@ -34,6 +34,8 @@ "filePanel.tssoltestghaction": "Mocha Chai Test Workflow", "filePanel.workspace.addscriptetherscan": "Adds scripts which can be used to interact with the Etherscan API", "filePanel.addscriptetherscan": "Add Etherscan scripts", + "filePanel.workspace.addscriptsindri": "Adds scripts for interacting with Sindri, a zk proof generation remote service", + "filePanel.addscriptsindri": "Add Sindri ZK scripts", "filePanel.workspace.addscriptdeployer": "Adds scripts which can be used to deploy contracts", "filePanel.addscriptdeployer": "Add contract deployer scripts", "filePanel.workspace.slitherghaction": "Adds a preset yml file to run slither analysis on github actions CI", @@ -94,7 +96,7 @@ "filePanel.copyFolderFailed": "Copy Folder Failed", "filePanel.copyFolderFailedMsg": "Unexpected error while copying folder: {src}", "filePanel.runScriptFailed": "Run script failed", - "filePanel.createPublicGist": "Create a public gist", + "filePanel.createPublicGist": "Publish to a public gist", "filePanel.createPublicGistMsg1": "Are you sure you want to push changes to remote gist file on github.com?", "filePanel.createPublicGistMsg2": "Are you sure you want to anonymously publish all your files in the {path} folder as a public gist on github.com?", "filePanel.createPublicGistMsg3": "Are you sure you want to anonymously publish {path} file as a public gist on github.com?", diff --git a/apps/remix-ide/src/app/tabs/locales/en/settings.json b/apps/remix-ide/src/app/tabs/locales/en/settings.json index b290156eec..2ea6de0d4b 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/settings.json +++ b/apps/remix-ide/src/app/tabs/locales/en/settings.json @@ -17,6 +17,9 @@ "settings.etherscanTokenTitle": "EtherScan Access Token", "settings.etherscanAccessTokenText": "Manage the api key used to interact with Etherscan.", "settings.etherscanAccessTokenText2": "Go to Etherscan api key page (link below) to create a new api key and save it in Remix.", + "settings.sindriAccessTokenTitle": "Sindri Credentials", + "settings.sindriAccessTokenText": "The access token is used to compile ZKP circuits and generate proofs with Sindri.", + "settings.sindriAccessTokenText2":"Go to the Sindri account creation page (link below) to create a new token and save it in Remix.", "settings.save": "Save", "settings.remove": "Remove", "settings.themes": "Themes", @@ -40,5 +43,6 @@ "settings.copilot": "Solidity copilot - Alpha", "settings.copilot.activate": "Load & Activate copilot", "settings.copilot.max_new_tokens": "Maximum number of words to generate", - "settings.copilot.temperature": "Temperature" + "settings.copilot.temperature": "Temperature", + "settings.enableSaveEnvState": "Save environment state" } diff --git a/apps/remix-ide/src/app/tabs/locales/en/terminal.json b/apps/remix-ide/src/app/tabs/locales/en/terminal.json index 8a20e2e97a..431f54fbba 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/terminal.json +++ b/apps/remix-ide/src/app/tabs/locales/en/terminal.json @@ -1,8 +1,8 @@ { - "terminal.listen": "listen on all transactions", + "terminal.listen": "Listen on all transactions", "terminal.listenVM": "Listen on all transactions is disabled for VM environment", "terminal.listenTitle": "If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you", - "terminal.search": "Search with transaction hash or address", + "terminal.search": "Filter with transaction hash or address", "terminal.used": "used", "terminal.debug": "Debug", "terminal.welcomeText1": "Welcome to", 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 ccf96f0e66..1db24ca378 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/en/udapp.json @@ -65,10 +65,18 @@ "udapp.tooltipText3": "Click to open a bridge for converting L1 mainnet ETH to the selected network currency.", "udapp._comment_instanceContainerUI.tsx": "libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx", - "udapp.deployedContracts": "Deployed Contracts", + "udapp.deployedContracts": "Deployed/Unpinned Contracts", "udapp.deployAndRunClearInstances": "Clear instances list and reset recorder", - "udapp.deployAndRunNoInstanceText": "Currently you have no contract instances to interact with.", - "udapp.tooltipText6": "Autogenerated generic user interfaces for interaction with deployed contracts", + "udapp.deployAndRunNoInstanceText": "Currently you have no unpinned contracts to interact with.", + "udapp.tooltipText6": "Autogenerated generic user interfaces for interaction with deployed/unpinned contracts", + + "udapp.savedContracts": "Pinned Contracts", + "udapp.tooltipTextPinnedContracts": "List of pinned contracts for selected workspace & network", + "udapp.NoSavedInstanceText": "No pinned contracts found for selected workspace & network", + "udapp.tooltipTextDelete": "Delete pinned contract", + "udapp.tooltipTextUnpin": "Unpin contract", + "udapp.savedOn": "Pinned at", + "udapp.filePath": "File path", "udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx", "udapp.transactionsRecorded": "Transactions recorded", @@ -98,7 +106,8 @@ "udapp.tooltipText13": "Deployed {date}", "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", - "udapp.tooltipText7": "Remove from the list", + "udapp.tooltipTextRemove": "Remove from the list", + "udapp.tooltipTextPin": "Pin contract", "udapp.tooltipText8": "Click for docs about using 'receive'/'fallback'", "udapp.tooltipText9": "The Calldata to send to fallback function of the contract.", "udapp.tooltipText10": "Send data to contract.", diff --git a/apps/remix-ide/src/app/tabs/locales/es/filePanel.json b/apps/remix-ide/src/app/tabs/locales/es/filePanel.json index 7e4c76a90c..ca6da409aa 100644 --- a/apps/remix-ide/src/app/tabs/locales/es/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/es/filePanel.json @@ -86,7 +86,7 @@ "filePanel.copyFolderFailed": "Copia de Carpeta Fallida", "filePanel.copyFolderFailedMsg": "Error inesperado al copiar la carpeta: {src}", "filePanel.runScriptFailed": "Error al ejecutar el script", - "filePanel.createPublicGist": "Crear una lista pública", + "filePanel.createPublicGist": "Publicar una lista pública", "filePanel.createPublicGistMsg1": "¿Está seguro que desea empujar cambios al archivo gist remoto en github.com?", "filePanel.createPublicGistMsg2": "¿Estás seguro que quieres publicar todos tus archivos de forma anónima en la carpeta {path} como un gist público en github.com?", "filePanel.createPublicGistMsg3": "¿Estás seguro de que quieres publicar de forma anónima el archivo {path} como una gist público en github.com?", diff --git a/apps/remix-ide/src/app/tabs/locales/es/settings.json b/apps/remix-ide/src/app/tabs/locales/es/settings.json index 899e2787cc..5d7ddda6ab 100644 --- a/apps/remix-ide/src/app/tabs/locales/es/settings.json +++ b/apps/remix-ide/src/app/tabs/locales/es/settings.json @@ -36,5 +36,6 @@ "settings.port": "PUERTO", "settings.projectID": "ID DEL PROYECTO", "settings.projectSecret": "SECRETO DE PROYECTO", - "settings.analyticsInRemix": "Analíticas en IDE Remix" + "settings.analyticsInRemix": "Analíticas en IDE Remix", + "settings.enableSaveEnvState": "Save environment state" } diff --git a/apps/remix-ide/src/app/tabs/locales/es/udapp.json b/apps/remix-ide/src/app/tabs/locales/es/udapp.json index 9f9d2d8be8..5ed17d12f9 100644 --- a/apps/remix-ide/src/app/tabs/locales/es/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/es/udapp.json @@ -88,7 +88,7 @@ "udapp.tooltipText12": "Entrada requerida", "udapp.tooltipText13": "Publicado en {date}", "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", - "udapp.tooltipText7": "Eliminar de la lista", + "udapp.tooltipTextRemove": "Eliminar de la lista", "udapp.tooltipText8": "Haga clic para ver los documentos sobre el uso de 'receive'/'fallback'", "udapp.tooltipText9": "El datos de llamada a enviar a la función fallback del contrato.", "udapp.tooltipText10": "Enviar datos al contrato.", diff --git a/apps/remix-ide/src/app/tabs/locales/fr/filePanel.json b/apps/remix-ide/src/app/tabs/locales/fr/filePanel.json index 1a72cc82cd..b38d0682b9 100644 --- a/apps/remix-ide/src/app/tabs/locales/fr/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/fr/filePanel.json @@ -86,7 +86,7 @@ "filePanel.copyFolderFailed": "Échec de la copie du dossier", "filePanel.copyFolderFailedMsg": "Erreur inattendue lors de la copie du fichier : {src}", "filePanel.runScriptFailed": "Échec de l'exécution du script", - "filePanel.createPublicGist": "Créer un gist public", + "filePanel.createPublicGist": "Publier un gist public", "filePanel.createPublicGistMsg1": "Êtes-vous sûr de vouloir envoyer les changements dans le fichier gist distant sur github.com?", "filePanel.createPublicGistMsg2": "Êtes-vous sûr de vouloir publier anonymement tous vos fichiers dans le dossier {path} en tant que gist public sur github.com?", "filePanel.createPublicGistMsg3": "Êtes-vous sûr de vouloir publier anonymement tous vos fichiers dans le dossier {path} en tant que gist public sur github.com?", diff --git a/apps/remix-ide/src/app/tabs/locales/fr/settings.json b/apps/remix-ide/src/app/tabs/locales/fr/settings.json index 5d1859dfe9..3b61ab68c2 100644 --- a/apps/remix-ide/src/app/tabs/locales/fr/settings.json +++ b/apps/remix-ide/src/app/tabs/locales/fr/settings.json @@ -36,5 +36,6 @@ "settings.port": "PORT", "settings.projectID": "ID du projet", "settings.projectSecret": "SECRET DU PROJET", - "settings.analyticsInRemix": "Analytics dans l'IDE de Remix" + "settings.analyticsInRemix": "Analytics dans l'IDE de Remix", + "settings.enableSaveEnvState": "Save environment state" } diff --git a/apps/remix-ide/src/app/tabs/locales/fr/udapp.json b/apps/remix-ide/src/app/tabs/locales/fr/udapp.json index a6c60d8e28..80b22f2cc3 100644 --- a/apps/remix-ide/src/app/tabs/locales/fr/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/fr/udapp.json @@ -88,7 +88,7 @@ "udapp.tooltipText12": "Entrée nécessaire", "udapp.tooltipText13": "Déployé {date}", "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", - "udapp.tooltipText7": "Supprimer de la liste", + "udapp.tooltipTextRemove": "Supprimer de la liste", "udapp.tooltipText8": "Cliquez sur la documentation à propos de l'utilisation de 'receive'/'fallback'", "udapp.tooltipText9": "Les Calldata à envoyer à la fonction fallback du contrat.", "udapp.tooltipText10": "Envoyer des données au contrat.", diff --git a/apps/remix-ide/src/app/tabs/locales/it/filePanel.json b/apps/remix-ide/src/app/tabs/locales/it/filePanel.json index 994dedf5c6..fa8728fa68 100644 --- a/apps/remix-ide/src/app/tabs/locales/it/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/it/filePanel.json @@ -86,7 +86,7 @@ "filePanel.copyFolderFailed": "Copia Cartella Non Riuscita", "filePanel.copyFolderFailedMsg": "Errore inatteso durante la copia della cartella: {src}", "filePanel.runScriptFailed": "Esecuzione dello script non riuscita", - "filePanel.createPublicGist": "Crea Gist Pubblico", + "filePanel.createPublicGist": "Pubblicare Gist Pubblico", "filePanel.createPublicGistMsg1": "Sei sicuro di voler inviare le modifiche al file gist remoto su github.com?", "filePanel.createPublicGistMsg2": "Sei sicuro di voler pubblicare in modo anonimo tutti i tuoi file nella cartella {path} come gist pubblico su github.com?", "filePanel.createPublicGistMsg3": "Sei sicuro di voler pubblicare in modo anonimo tutti i tuoi file nella cartella {path} come gist pubblico su github.com?", diff --git a/apps/remix-ide/src/app/tabs/locales/it/settings.json b/apps/remix-ide/src/app/tabs/locales/it/settings.json index 416f338b64..251089f6cb 100644 --- a/apps/remix-ide/src/app/tabs/locales/it/settings.json +++ b/apps/remix-ide/src/app/tabs/locales/it/settings.json @@ -36,5 +36,6 @@ "settings.port": "PORTA", "settings.projectID": "ID PROGETTO", "settings.projectSecret": "SEGRETO DEL PROGETTO", - "settings.analyticsInRemix": "Analytics nella Remix IDE" + "settings.analyticsInRemix": "Analytics nella Remix IDE", + "settings.enableSaveEnvState": "Save environment state" } diff --git a/apps/remix-ide/src/app/tabs/locales/it/udapp.json b/apps/remix-ide/src/app/tabs/locales/it/udapp.json index 08c340ce01..bb00090309 100644 --- a/apps/remix-ide/src/app/tabs/locales/it/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/it/udapp.json @@ -88,7 +88,7 @@ "udapp.tooltipText12": "Input richiesto", "udapp.tooltipText13": "Deploiato", "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", - "udapp.tooltipText7": "Rimuovi dalla lista", + "udapp.tooltipTextRemove": "Rimuovi dalla lista", "udapp.tooltipText8": "Fare clic per i documenti sull'uso di 'receive'/'fallback'", "udapp.tooltipText9": "Il Calldata per inviare alla funzione di fallback del contratto.", "udapp.tooltipText10": "Invia dati al contratto.", diff --git a/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json b/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json index 4d1ea78fa7..6f74fdae19 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/zh/filePanel.json @@ -86,7 +86,7 @@ "filePanel.copyFolderFailed": "复制文件夹失败", "filePanel.copyFolderFailedMsg": "复制文件夹时出现意外错误:{src}", "filePanel.runScriptFailed": "执行脚本失败", - "filePanel.createPublicGist": "创建一个公开的 gist", + "filePanel.createPublicGist": "发布到公共 gist", "filePanel.createPublicGistMsg1": "您确定要将更改推送到 github.com 上的远程 gist 文件吗?", "filePanel.createPublicGistMsg2": "您确定要在 github.com 上以匿名方式将 {path} 文件夹中的所有文件发布为公开的 gist?", "filePanel.createPublicGistMsg3": "您确定要将 {path} 文件匿名发布为 github.com 上的公开 gist?", diff --git a/apps/remix-ide/src/app/tabs/locales/zh/settings.json b/apps/remix-ide/src/app/tabs/locales/zh/settings.json index 5504874efd..8a27f4a83d 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/settings.json +++ b/apps/remix-ide/src/app/tabs/locales/zh/settings.json @@ -36,5 +36,6 @@ "settings.port": "端口", "settings.projectID": "项目 ID", "settings.projectSecret": "项目密钥", - "settings.analyticsInRemix": "Remix IDE 中的分析功能" + "settings.analyticsInRemix": "Remix IDE 中的分析功能", + "settings.enableSaveEnvState": "Save environment state" } 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 f7e3d811a8..3e7b4e3ba8 100644 --- a/apps/remix-ide/src/app/tabs/locales/zh/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/zh/udapp.json @@ -88,7 +88,7 @@ "udapp.tooltipText12": "必填", "udapp.tooltipText13": "已部署 {date}", "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", - "udapp.tooltipText7": "从列表中删除", + "udapp.tooltipTextRemove": "从列表中删除", "udapp.tooltipText8": "点击查看有关使用 'receive'/'fallback' 的文档", "udapp.tooltipText9": "发送到合约 fallback 函数的 Calldata。", "udapp.tooltipText10": "将数据发送到合约。", diff --git a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js index e27edcd3d9..23b9abf4be 100644 --- a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js +++ b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js @@ -1,6 +1,6 @@ var async = require('async') var remixLib = require('@remix-project/remix-lib') -import { bufferToHex } from '@ethereumjs/util' +import { bytesToHex } from '@ethereumjs/util' import { hash } from '@remix-project/remix-lib' import { Plugin } from '@remixproject/engine' import * as packageJson from '../../../../.././../../package.json' @@ -43,7 +43,7 @@ class Recorder extends Plugin { } if (!to) { var abi = payLoad.contractABI - var keccak = bufferToHex(hash.keccakFromString(JSON.stringify(abi))) + var keccak = bytesToHex(hash.keccakFromString(JSON.stringify(abi))) record.abi = keccak record.contractName = payLoad.contractName record.bytecode = payLoad.contractBytecode @@ -208,7 +208,7 @@ class Recorder extends Plugin { // resolve the bytecode and ABI using the contract name, this ensure getting the last compiled one. const data = await this.call('compilerArtefacts', 'getArtefactsByContractName', tx.record.contractName) tx.record.bytecode = data.artefact.evm.bytecode.object - const updatedABIKeccak = bufferToHex(hash.keccakFromString(JSON.stringify(data.artefact.abi))) + const updatedABIKeccak = bytesToHex(hash.keccakFromString(JSON.stringify(data.artefact.abi))) abis[updatedABIKeccak] = data.artefact.abi tx.record.abi = updatedABIKeccak } diff --git a/apps/remix-ide/src/app/tabs/web3-provider.js b/apps/remix-ide/src/app/tabs/web3-provider.js index 16c3a3dd17..41bd9d3c27 100644 --- a/apps/remix-ide/src/app/tabs/web3-provider.js +++ b/apps/remix-ide/src/app/tabs/web3-provider.js @@ -58,11 +58,27 @@ export class Web3ProviderModule extends Plugin { const contractAddressStr = addressToString(receipt.contractAddress) const contractData = await this.call('compilerArtefacts', 'getContractDataFromAddress', contractAddressStr) if (contractData) { - this.call('udapp', 'addInstance', contractAddressStr, contractData.contract.abi, contractData.name) const data = await this.call('compilerArtefacts', 'getCompilerAbstract', contractData.file) + const contractObject = { + name: contractData.name, + abi: contractData.contract.abi, + compiler: data, + contract: { + file : contractData.file, + object: contractData.contract + } + } + this.call('udapp', 'addInstance', contractAddressStr, contractData.contract.abi, contractData.name, contractObject) await this.call('compilerArtefacts', 'addResolvedContract', contractAddressStr, data) } }, 50) + const isVM = this.blockchain.executionContext.isVM() + + if (isVM && this.blockchain.config.get('settings/save-evm-state')) { + await this.blockchain.executionContext.getStateDetails().then((state) => { + this.call('fileManager', 'writeFile', `.states/${this.blockchain.executionContext.getProvider()}/state.json`, state) + }) + } } } resolve(message) diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index f387f1da90..7d65cae8de 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -28,7 +28,9 @@ const profile = { 'getSettings', 'setEnvironmentMode', 'clearAllInstances', + 'clearAllSavedInstances', 'addInstance', + 'addSavedInstance', 'resolveContractAndAddInstance' ] } @@ -79,8 +81,16 @@ export class RunTab extends ViewPlugin { this.emit('clearAllInstancesReducer') } - addInstance(address, abi, name) { - this.emit('addInstanceReducer', address, abi, name) + clearAllSavedInstances() { + this.emit('clearAllSavedInstancesReducer') + } + + addInstance(address, abi, name, contractData) { + this.emit('addInstanceReducer', address, abi, name, contractData) + } + + addSavedInstance(address, abi, name, savedOn, filePath) { + this.emit('addSavedInstanceReducer', address, abi, name, savedOn, filePath) } createVMAccount(newAccount) { @@ -167,13 +177,14 @@ export class RunTab extends ViewPlugin { // VM const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.' + await addProvider('vm-cancun', 'Remix VM (Cancun)', false, true, 'cancun', 'settingsVMCancunMode', titleVM) await addProvider('vm-shanghai', 'Remix VM (Shanghai)', false, true, 'shanghai', 'settingsVMShanghaiMode', titleVM) - await addProvider('vm-merge', 'Remix VM (Merge)', false, true, 'merge', 'settingsVMMergeMode', titleVM) + await addProvider('vm-paris', 'Remix VM (Paris)', false, true, 'paris', 'settingsVMParisMode', titleVM) await addProvider('vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', titleVM) await addProvider('vm-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM) - await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'merge', 'settingsVMMainnetMode', titleVM) - await addProvider('vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'merge', 'settingsVMSepoliaMode', titleVM) - await addProvider('vm-goerli-fork', 'Remix VM - Goerli fork', false, true, 'merge', 'settingsVMGoerliMode', titleVM) + await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'cancun', 'settingsVMMainnetMode', titleVM) + await addProvider('vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'cancun', 'settingsVMSepoliaMode', titleVM) + await addProvider('vm-goerli-fork', 'Remix VM - Goerli fork', false, true, 'paris', 'settingsVMGoerliMode', titleVM) await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM) // wallet connect diff --git a/apps/remix-ide/src/assets/img/vyperLogo2.webp b/apps/remix-ide/src/assets/img/vyperLogo2.webp new file mode 100644 index 0000000000..fc85993627 Binary files /dev/null and b/apps/remix-ide/src/assets/img/vyperLogo2.webp differ diff --git a/apps/remix-ide/src/assets/list.json b/apps/remix-ide/src/assets/list.json index b22d882d4b..04a4929e01 100644 --- a/apps/remix-ide/src/assets/list.json +++ b/apps/remix-ide/src/assets/list.json @@ -1091,9 +1091,22 @@ "bzzr://c604bdd6384bf73594cd0e5cfbe979048191549ebc88e70996346f3b744c0680", "dweb:/ipfs/QmW2SQbEhiz3n2qV5iL8WBgzapv6cXjkLStvTMpCZhvr2x" ] + }, + { + "path": "soljson-v0.8.25+commit.b61c2a91.js", + "version": "0.8.25", + "build": "commit.b61c2a91", + "longVersion": "0.8.25+commit.b61c2a91", + "keccak256": "0x4639103a26b2f669bd3ecc22b1a1665819f2a2956f917ab91380bd9565dbcd01", + "sha256": "0xf8c9554471ff2db3843167dffb7a503293b5dc728c8305b044ef9fd37d626ca7", + "urls": [ + "bzzr://d201e60bd46193b11382988a854132b9e7fb0e1574cc766cb7f9efe8e44a680c", + "dweb:/ipfs/QmdduJxmPXungjJk2FBDw1bdDQ6ucHxYGLXRMBJqMFW7h9" + ] } ], "releases": { + "0.8.25": "soljson-v0.8.25+commit.b61c2a91.js", "0.8.24": "soljson-v0.8.24+commit.e11b9ed9.js", "0.8.23": "soljson-v0.8.23+commit.f704f362.js", "0.8.22": "soljson-v0.8.22+commit.4fc1097e.js", @@ -1186,5 +1199,5 @@ "0.4.0": "soljson-v0.4.0+commit.acd334c9.js", "0.3.6": "soljson-v0.3.6+commit.3fc68da5.js" }, - "latestRelease": "0.8.24" + "latestRelease": "0.8.25" } \ No newline at end of file diff --git a/apps/remix-ide/src/blockchain/blockchain.tsx b/apps/remix-ide/src/blockchain/blockchain.tsx index fe9b6598f1..fa57a1d58b 100644 --- a/apps/remix-ide/src/blockchain/blockchain.tsx +++ b/apps/remix-ide/src/blockchain/blockchain.tsx @@ -1,7 +1,7 @@ import React from 'react' // eslint-disable-line import {fromWei, toBigInt, toWei} from 'web3-utils' import {Plugin} from '@remixproject/engine' -import {toBuffer, addHexPrefix} from '@ethereumjs/util' +import {toBytes, addHexPrefix} from '@ethereumjs/util' import {EventEmitter} from 'events' import {format} from 'util' import {ExecutionContext} from './execution-context' @@ -135,7 +135,8 @@ export class Blockchain extends Plugin { setupEvents() { this.executionContext.event.register('contextChanged', async (context) => { - await this.resetEnvironment() + // reset environment to last known state of the context + await this.loadContext(context) this._triggerEvent('contextChanged', [context]) this.detectNetwork((error, network) => { this.networkStatus = {network, error} @@ -286,7 +287,7 @@ export class Blockchain extends Plugin { await this.saveDeployedContractStorageLayout(implementationContractObject, address, networkInfo) this.events.emit('newProxyDeployment', address, new Date().toISOString(), implementationContractObject.contractName) _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful']) - this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name) + this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name, implementationContractObject) } this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) @@ -336,7 +337,7 @@ export class Blockchain extends Plugin { } await this.saveDeployedContractStorageLayout(newImplementationContractObject, proxyAddress, networkInfo) _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful']) - this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name) + this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name, newImplementationContractObject) } this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) } @@ -643,8 +644,23 @@ export class Blockchain extends Plugin { }) } - async resetEnvironment() { - await this.getCurrentProvider().resetEnvironment() + async loadContext(context: string) { + const saveEvmState = this.config.get('settings/save-evm-state') + + if (saveEvmState) { + const contextExists = await this.call('fileManager', 'exists', `.states/${context}/state.json`) + + if (contextExists) { + const stateDb = await this.call('fileManager', 'readFile', `.states/${context}/state.json`) + + await this.getCurrentProvider().resetEnvironment(stateDb) + } else { + await this.getCurrentProvider().resetEnvironment() + } + } else { + await this.getCurrentProvider().resetEnvironment() + } + // TODO: most params here can be refactored away in txRunner const web3Runner = new TxRunnerWeb3( { @@ -677,7 +693,7 @@ export class Blockchain extends Plugin { view on etherscan ) - } + } }) }) this.txRunner = new TxRunner(web3Runner, {}) @@ -889,8 +905,16 @@ export class Blockchain extends Plugin { let execResult let returnValue = null if (isVM) { - const hhlogs = await this.web3().remix.getHHLogsForTx(txResult.transactionHash) + if (!tx.useCall && this.config.get('settings/save-evm-state')) { + try { + const state = await this.executionContext.getStateDetails() + this.call('fileManager', 'writeFile', `.states/${this.executionContext.getProvider()}/state.json`, state) + } catch (e) { + console.error(e) + } + } + const hhlogs = await this.web3().remix.getHHLogsForTx(txResult.transactionHash) if (hhlogs && hhlogs.length) { const finalLogs = (
@@ -920,8 +944,8 @@ export class Blockchain extends Plugin { if (execResult) { // if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value. returnValue = execResult - ? toBuffer(execResult.returnValue) - : toBuffer(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000') + ? toBytes(execResult.returnValue) + : toBytes(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000') const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas') const vmError = txExecution.checkError({ errorMessage: execResult.exceptionError ? execResult.exceptionError.error : '', errorData: execResult.returnValue }, compiledContracts) if (vmError.error) { @@ -930,7 +954,7 @@ export class Blockchain extends Plugin { } } if (!isVM && tx && tx.useCall) { - returnValue = toBuffer(addHexPrefix(txResult.result)) + returnValue = toBytes(addHexPrefix(txResult.result)) } let address = null diff --git a/apps/remix-ide/src/blockchain/execution-context.js b/apps/remix-ide/src/blockchain/execution-context.js index 7c33bb4f27..b54b8d371b 100644 --- a/apps/remix-ide/src/blockchain/execution-context.js +++ b/apps/remix-ide/src/blockchain/execution-context.js @@ -3,6 +3,7 @@ import Web3 from 'web3' import { execution } from '@remix-project/remix-lib' import EventManager from '../lib/events' +import { bytesToHex } from '@ethereumjs/util' const _paq = window._paq = window._paq || [] let web3 @@ -22,11 +23,11 @@ web3.eth.setConfig(config) export class ExecutionContext { constructor () { this.event = new EventManager() - this.executionContext = 'vm-shanghai' + this.executionContext = 'vm-cancun' this.lastBlock = null this.blockGasLimitDefault = 4300000 this.blockGasLimit = this.blockGasLimitDefault - this.currentFork = 'shanghai' + this.currentFork = 'cancun' this.mainNetGenesisHash = '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' this.customNetWorks = {} this.blocks = {} @@ -36,7 +37,7 @@ export class ExecutionContext { } init (config) { - this.executionContext = 'vm-shanghai' + this.executionContext = 'vm-cancun' this.event.trigger('contextChanged', [this.executionContext]) } @@ -71,40 +72,49 @@ export class ExecutionContext { } detectNetwork (callback) { - if (this.isVM()) { - callback(null, { id: '-', name: 'VM' }) - } else { - if (!web3.currentProvider) { - return callback('No provider set') - } - const cb = (err, id) => { - let name = null - if (err) name = 'Unknown' - // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md - else if (id === 1) name = 'Main' - else if (id === 3) name = 'Ropsten' - else if (id === 4) name = 'Rinkeby' - else if (id === 5) name = 'Goerli' - else if (id === 42) name = 'Kovan' - else if (id === 11155111) name = 'Sepolia' - else name = 'Custom' - - if (id === 1) { - web3.eth.getBlock(0).then((block) => { - if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom' - callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) - }).catch((error) => callback(error)) - } else { - callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) + return new Promise((resolve, reject) => { + if (this.isVM()) { + callback && callback(null, { id: '-', name: 'VM' }) + return resolve({ id: '-', name: 'VM' }) + } else { + if (!web3.currentProvider) { + callback && callback('No provider set') + return reject('No provider set') + } + const cb = (err, id) => { + let name = null + if (err) name = 'Unknown' + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md + else if (id === 1) name = 'Main' + else if (id === 3) name = 'Ropsten' + else if (id === 4) name = 'Rinkeby' + else if (id === 5) name = 'Goerli' + else if (id === 42) name = 'Kovan' + else if (id === 11155111) name = 'Sepolia' + else name = 'Custom' + + if (id === 1) { + web3.eth.getBlock(0).then((block) => { + if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom' + callback && callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) + return resolve({ id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) + }).catch((error) => { + callback && callback(error) + return reject(error) + }) + } else { + callback && callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) + return resolve({ id, name, lastBlock: this.lastBlock, currentFork: this.currentFork }) + } } + web3.eth.net.getId().then(id=>cb(null,parseInt(id))).catch(err=>cb(err)) } - web3.eth.net.getId().then(id=>cb(null,parseInt(id))).catch(err=>cb(err)) - } + }) } removeProvider (name) { if (name && this.customNetWorks[name]) { - if (this.executionContext === name) this.setContext('vm-merge', null, null, null) + if (this.executionContext === name) this.setContext('vm-cancun', null, null, null) delete this.customNetWorks[name] this.event.trigger('removeProvider', [name]) } @@ -164,7 +174,7 @@ export class ExecutionContext { try { this.currentFork = execution.forkAt(await web3.eth.net.getId(), block.number) } catch (e) { - this.currentFork = 'merge' + this.currentFork = 'cancun' console.log(`unable to detect fork, defaulting to ${this.currentFork}..`) console.error(e) } @@ -195,4 +205,32 @@ export class ExecutionContext { return transactionDetailsLinks[network] + hash } } + + async getStateDetails() { + const stateDb = await this.web3().remix.getStateDb() + const blocksData = await this.web3().remix.getBlocksData() + const state = { + db: Object.fromEntries(stateDb.db._database), + blocks: blocksData.blocks, + latestBlockNumber: blocksData.latestBlockNumber + } + const stringifyed = JSON.stringify(state, (key, value) => { + if (key === 'db') { + return value + } else if (key === 'blocks') { + return value.map(block => bytesToHex(block)) + } else if (key === '') { + return value + } + if (typeof value === 'string') { + return value.startsWith('0x') ? value : '0x' + value + } else if (typeof value === 'number') { + return '0x' + value.toString(16) + } else { + return bytesToHex(value) + } + }, '\t') + + return stringifyed + } } diff --git a/apps/remix-ide/src/blockchain/providers/injected.ts b/apps/remix-ide/src/blockchain/providers/injected.ts index 32b4938f70..905e77559a 100644 --- a/apps/remix-ide/src/blockchain/providers/injected.ts +++ b/apps/remix-ide/src/blockchain/providers/injected.ts @@ -1,5 +1,5 @@ import Web3 from 'web3' -import { hashPersonalMessage, isHexString } from '@ethereumjs/util' +import { hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util' import { ExecutionContext } from '../execution-context' export class InjectedProvider { @@ -42,7 +42,7 @@ export class InjectedProvider { try { message = isHexString(message) ? message : Web3.utils.utf8ToHex(message) this.executionContext.web3().eth.personal.sign(message, account).then((error, signedData) => { - cb(error, '0x' + messageHash.toString('hex'), signedData) + cb(error, bytesToHex(messageHash), signedData) }).catch((error => cb(error))) } catch (e) { cb(e.message) diff --git a/apps/remix-ide/src/blockchain/providers/node.ts b/apps/remix-ide/src/blockchain/providers/node.ts index c77421e045..fec159f11f 100644 --- a/apps/remix-ide/src/blockchain/providers/node.ts +++ b/apps/remix-ide/src/blockchain/providers/node.ts @@ -1,5 +1,5 @@ import Web3 from 'web3' -import { hashPersonalMessage, isHexString } from '@ethereumjs/util' +import { hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util' import { Personal } from 'web3-eth-personal' import { ExecutionContext } from '../execution-context' import Config from '../../config' @@ -49,8 +49,8 @@ export class NodeProvider { const personal = new Personal(this.executionContext.web3().currentProvider) message = isHexString(message) ? message : Web3.utils.utf8ToHex(message) personal.sign(message, account, passphrase) - .then(signedData => cb(undefined, '0x' + messageHash.toString('hex'), signedData)) - .catch(error => cb(error, '0x' + messageHash.toString('hex'), undefined)) + .then(signedData => cb(undefined, bytesToHex(messageHash), signedData)) + .catch(error => cb(error, bytesToHex(messageHash), undefined)) } catch (e) { cb(e.message) } diff --git a/apps/remix-ide/src/blockchain/providers/vm.ts b/apps/remix-ide/src/blockchain/providers/vm.ts index 67357b347e..e44b67946a 100644 --- a/apps/remix-ide/src/blockchain/providers/vm.ts +++ b/apps/remix-ide/src/blockchain/providers/vm.ts @@ -1,6 +1,6 @@ import Web3, { FMT_BYTES, FMT_NUMBER, LegacySendAsyncProvider } from 'web3' import { fromWei, toBigInt } from 'web3-utils' -import { privateToAddress, hashPersonalMessage, isHexString } from '@ethereumjs/util' +import { privateToAddress, hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util' import { extend, JSONRPCRequestPayload, JSONRPCResponseCallback } from '@remix-project/remix-simulator' import { ExecutionContext } from '../execution-context' @@ -12,9 +12,7 @@ export class VMProvider { sendAsync: (query: JSONRPCRequestPayload, callback: JSONRPCResponseCallback) => void } newAccountCallback: {[stamp: number]: (error: Error, address: string) => void} - constructor (executionContext: ExecutionContext) { - this.executionContext = executionContext this.worker = null this.provider = null @@ -29,7 +27,7 @@ export class VMProvider { }) } - async resetEnvironment () { + async resetEnvironment (stringifiedState?: string) { if (this.worker) this.worker.terminate() this.worker = new Worker(new URL('./worker-vm', import.meta.url)) const provider = this.executionContext.getProviderObject() @@ -76,17 +74,42 @@ export class VMProvider { } } }) - this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']}) + if (stringifiedState) { + try { + const blockchainState = JSON.parse(stringifiedState) + const blockNumber = parseInt(blockchainState.latestBlockNumber, 16) + const stateDb = blockchainState.db + + this.worker.postMessage({ + cmd: 'init', + fork: this.executionContext.getCurrentFork(), + nodeUrl: provider?.options['nodeUrl'], + blockNumber, + stateDb, + blocks: blockchainState.blocks + }) + } catch (e) { + console.error(e) + } + } else { + this.worker.postMessage({ + cmd: 'init', + fork: this.executionContext.getCurrentFork(), + nodeUrl: provider?.options['nodeUrl'], + blockNumber: provider?.options['blockNumber'] + }) + } }) } + // TODO: is still here because of the plugin API // can be removed later when we update the API createVMAccount (newAccount) { const { privateKey, balance } = newAccount this.worker.postMessage({ cmd: 'addAccount', privateKey: privateKey, balance }) const privKey = Buffer.from(privateKey, 'hex') - return '0x' + privateToAddress(privKey).toString('hex') + return bytesToHex(privateToAddress(privKey)) } newAccount (_passwordPromptCb, cb) { @@ -109,7 +132,7 @@ export class VMProvider { const messageHash = hashPersonalMessage(Buffer.from(message)) message = isHexString(message) ? message : Web3.utils.utf8ToHex(message) this.web3.eth.sign(message, account) - .then(signedData => cb(null, '0x' + messageHash.toString('hex'), signedData)) + .then(signedData => cb(null, bytesToHex(messageHash), signedData)) .catch(error => cb(error)) } diff --git a/apps/remix-ide/src/blockchain/providers/worker-vm.ts b/apps/remix-ide/src/blockchain/providers/worker-vm.ts index 64a8d0255b..e91e30dfd4 100644 --- a/apps/remix-ide/src/blockchain/providers/worker-vm.ts +++ b/apps/remix-ide/src/blockchain/providers/worker-vm.ts @@ -6,7 +6,7 @@ self.onmessage = (e: MessageEvent) => { switch (data.cmd) { case 'init': { - provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber }) + provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber, stateDb: data.stateDb, blocks: data.blocks}) provider.init().then(() => { self.postMessage({ cmd: 'initiateResult', diff --git a/apps/remix-ide/src/lib/helper.js b/apps/remix-ide/src/lib/helper.js index d332c7e937..cb0a421fca 100644 --- a/apps/remix-ide/src/lib/helper.js +++ b/apps/remix-ide/src/lib/helper.js @@ -1,5 +1,5 @@ var async = require('async') -import { toChecksumAddress } from '@ethereumjs/util' +import { toChecksumAddress, bytesToHex } from '@ethereumjs/util' export default { shortenAddress: function (address, etherBalance) { @@ -9,7 +9,7 @@ export default { addressToString: function (address) { if (!address) return null if (typeof address !== 'string') { - address = address.toString('hex') + address = bytesToHex(address) } if (address.indexOf('0x') === -1) { address = '0x' + address diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index e1780ceea3..5a5b2348d6 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -12,6 +12,7 @@ let requiredModules = [ // services + layout views + system views 'config', 'compilerArtefacts', 'compilerMetadata', + 'compilerloader', 'contextualListener', 'editor', 'offsetToLineColumnConverter', @@ -61,7 +62,7 @@ let requiredModules = [ // services + layout views + system views 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', - 'vm-merge', + 'vm-paris', 'vm-london', 'vm-berlin', 'vm-shanghai', @@ -108,6 +109,7 @@ export function isNative(name) { 'solidity', 'solidity-logic', 'solidityStaticAnalysis', + 'solhint', 'solidityUnitTesting', 'layout', 'notification', diff --git a/apps/remix-ide/src/remixEngine.js b/apps/remix-ide/src/remixEngine.js index 6b96d44783..2eac1e085d 100644 --- a/apps/remix-ide/src/remixEngine.js +++ b/apps/remix-ide/src/remixEngine.js @@ -27,7 +27,7 @@ export class RemixEngine extends Engine { if (name === 'filePanel') return { queueTimeout: 60000 * 20 } if (name === 'fileManager') return { queueTimeout: 60000 * 20 } if (name === 'openaigpt') return { queueTimeout: 60000 * 2 } - if (name === 'cookbookdev') return { queueTimeout: 60000 * 2 } + if (name === 'cookbookdev') return { queueTimeout: 60000 * 3 } return { queueTimeout: 10000 } } diff --git a/apps/remix-ide/team-best-practices.md b/apps/remix-ide/team-best-practices.md index 3753cf1778..4918dc0e09 100644 --- a/apps/remix-ide/team-best-practices.md +++ b/apps/remix-ide/team-best-practices.md @@ -168,7 +168,7 @@ Before starting coding, we should ensure all devs / contributors are aware of: ### 3) Documentation: - The documentation is done / updated just after the feature / release in a team effort. -- Documentation work is filable as a github issue. +- Documentation work is fileable as a github issue. - It is encouraged to find and link associated doc produced by the community (blog posts, videos, tutorials, ...) --- diff --git a/apps/remix-ide/webpack.config.js b/apps/remix-ide/webpack.config.js index 48d2c4eda1..77417b1b7b 100644 --- a/apps/remix-ide/webpack.config.js +++ b/apps/remix-ide/webpack.config.js @@ -6,7 +6,7 @@ const version = require('../../package.json').version const fs = require('fs') const TerserPlugin = require('terser-webpack-plugin') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') -const axios = require('axios') +const path = require('path') const versionData = { version: version, @@ -16,9 +16,10 @@ const versionData = { const loadLocalSolJson = async () => { //execute apps/remix-ide/ci/downloadsoljson.sh + console.log('loading local soljson') const child = require('child_process').execSync('bash ' + __dirname + '/ci/downloadsoljson.sh', { encoding: 'utf8', cwd: process.cwd(), shell: true }) // show output - //console.log(child) + console.log(child) } fs.writeFileSync(__dirname + '/src/assets/version.json', JSON.stringify(versionData)) @@ -74,7 +75,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => { // add externals config.externals = { ...config.externals, - solc: 'solc' + solc: 'solc', } // uncomment this to enable react profiling @@ -85,6 +86,17 @@ module.exports = composePlugins(withNx(), withReact(), (config) => { } */ + // use the web build instead of the node.js build + // we do like that because using "config.resolve.alias" doesn't work + let pkgVerkle = fs.readFileSync(path.resolve(__dirname, '../../node_modules/rust-verkle-wasm/package.json'), 'utf8') + pkgVerkle = pkgVerkle.replace('"main": "./nodejs/rust_verkle_wasm.js",', '"main": "./web/rust_verkle_wasm.js",') + fs.writeFileSync(path.resolve(__dirname, '../../node_modules/rust-verkle-wasm/package.json'), pkgVerkle) + + config.resolve.alias = { + ...config.resolve.alias, + // 'rust-verkle-wasm$': path.resolve(__dirname, '../../node_modules/rust-verkle-wasm/web/run_verkle_wasm.js') + } + // add public path if(process.env.NX_DESKTOP_FROM_DIST){ @@ -112,7 +124,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => { new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], url: ['url', 'URL'], - process: 'process/browser', + process: 'process/browser' }) ) diff --git a/apps/remixdesktop/package.json b/apps/remixdesktop/package.json index 855777b5c4..a92af25e36 100644 --- a/apps/remixdesktop/package.json +++ b/apps/remixdesktop/package.json @@ -52,7 +52,7 @@ "axios": "^1.6.1", "byline": "^5.0.0", "chokidar": "^3.5.3", - "express": "^4.18.2", + "express": "^4.19.2", "isomorphic-git": "^1.24.2", "node-pty": "^0.10.1", "semver": "^7.5.4" diff --git a/apps/remixdesktop/yarn.lock b/apps/remixdesktop/yarn.lock index fc9e558ee8..dfe7f7a7e7 100644 --- a/apps/remixdesktop/yarn.lock +++ b/apps/remixdesktop/yarn.lock @@ -520,9 +520,9 @@ semver "^7.3.5" "@openzeppelin/contracts@^4.7.3": - version "4.9.3" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364" - integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg== + version "4.9.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" + integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA== "@openzeppelin/wizard@^0.1.1": version "0.1.1" @@ -1259,25 +1259,7 @@ bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -body-parser@^1.16.0: +body-parser@1.20.2, body-parser@^1.16.0: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== @@ -1709,10 +1691,10 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== core-util-is@1.0.2: version "1.0.2" @@ -2173,13 +2155,14 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14: + version "0.10.63" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.63.tgz#9c222a63b6a332ac80b1e373b426af723b895bd6" + integrity sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ== dependencies: es6-iterator "^2.0.3" es6-symbol "^3.1.3" + esniff "^2.0.1" next-tick "^1.1.0" es6-error@^4.1.1: @@ -2224,6 +2207,16 @@ escape-string-regexp@^4.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -2351,6 +2344,14 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + eventemitter3@4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" @@ -2374,17 +2375,17 @@ exponential-backoff@^3.1.1: resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -express@^4.14.0, express@^4.18.2: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== +express@^4.14.0, express@^4.19.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.1" + body-parser "1.20.2" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.5.0" + cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" @@ -2489,9 +2490,9 @@ finalhandler@1.2.0: unpipe "~1.0.0" follow-redirects@^1.15.0: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== for-each@^0.3.3: version "0.3.3" @@ -4197,16 +4198,6 @@ range-parser@~1.2.1: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" diff --git a/apps/solhint/src/profile.json b/apps/solhint/src/profile.json index 2964511f3b..bac35b6d18 100644 --- a/apps/solhint/src/profile.json +++ b/apps/solhint/src/profile.json @@ -13,7 +13,7 @@ "icon": "https://raw.githubusercontent.com/protofire/solhint/master/solhint-icon.png", "location": "hiddenPanel", "url": "", - "documentation": "https://remix-plugins.readthedocs.io/en/latest/", + "documentation": "https://remix-ide.readthedocs.io/en/latest/static_analysis.html", "repo": "https://github.com/ethereum/remix-project", "maintainedBy": "Remix", "authorContact": "remix@ethereum.org" diff --git a/apps/solhint/yarn.lock b/apps/solhint/yarn.lock index 033b0accb4..c55c1b9826 100644 --- a/apps/solhint/yarn.lock +++ b/apps/solhint/yarn.lock @@ -245,9 +245,9 @@ fast-json-stable-stringify@^2.0.0: integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== follow-redirects@^1.14.0: - version "1.15.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" - integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== fs.realpath@^1.0.0: version "1.0.0" diff --git a/apps/vyper/src/app/app.css b/apps/vyper/src/app/app.css index f299c4d37a..2d05f1fead 100644 --- a/apps/vyper/src/app/app.css +++ b/apps/vyper/src/app/app.css @@ -222,3 +222,47 @@ html, body, #root, main { margin: 15px; padding: 15px; } + +.cursor-status { + +} + +.cursor-status :hover { + cursor: pointer; +} + +.accordion-background { + background-color: var(--body-bg); +} + +.accordion-background:hover { + cursor: pointer; +} + +.vyper-compile-warning, +.vyper-compile-error { + white-space: pre-line; + word-wrap: break-word; + cursor: pointer; + position: relative; + margin: 0.5em 0 1em 0; + border-radius: 5px; + line-height: 20px; + padding: 8px 15px; +} + +.vyper-compile-warning pre, +.vyper-compile-error pre { + white-space: pre-line; + overflow-y: hidden; + background-color: transparent; + margin: 0; + font-size: 12px; + border: 0 none; + padding: 0; + border-radius: 0; +} + +.vyper-panel-width { + width: 94%; +} diff --git a/apps/vyper/src/app/app.tsx b/apps/vyper/src/app/app.tsx index f09dc06cda..f6f0ef2279 100644 --- a/apps/vyper/src/app/app.tsx +++ b/apps/vyper/src/app/app.tsx @@ -1,4 +1,4 @@ -import React, {useState, useEffect} from 'react' +import React, {useState, useEffect, useRef} from 'react' import {remixClient} from './utils' import {CompilationResult} from '@remixproject/plugin-api' @@ -11,11 +11,14 @@ import LocalUrlInput from './components/LocalUrl' import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup' import ToggleButton from 'react-bootstrap/ToggleButton' import Button from 'react-bootstrap/Button' +import Accordion from 'react-bootstrap/Accordion' +import Card from 'react-bootstrap/Card' import './app.css' import {CustomTooltip} from '@remix-ui/helper' import {Form} from 'react-bootstrap' import {CompileErrorCard} from './components/CompileErrorCard' +import CustomAccordionToggle from './components/CustomAccordionToggle' interface AppState { status: 'idle' | 'inProgress' @@ -37,11 +40,16 @@ const App = () => { localUrl: 'http://localhost:8000/', }) + const spinnerIcon = useRef(null) + useEffect(() => { async function start() { try { await remixClient.loaded() - remixClient.onFileChange((name) => setContract(name)) + remixClient.onFileChange((name) => { + setOutput({}) + setContract(name) + }) remixClient.onNoFileSelected(() => setContract('')) } catch (err) { console.log(err) @@ -68,9 +76,6 @@ const App = () => { useEffect(() => { remixClient.eventEmitter.on('setOutput', (payload) => { - if (payload.status === 'failed') { - console.error('Error in the compiler', payload) - } setOutput(payload) }) @@ -98,17 +103,17 @@ const App = () => { setOutput(remixClient.compilerOutput) } + const startingCompilation = () => { + if (!spinnerIcon.current) return + spinnerIcon.current.setAttribute('title', 'compiling...') + spinnerIcon.current.classList.remove('remixui_bouncingIcon') + spinnerIcon.current.classList.add('remixui_spinningIcon') + } + + const [toggleAccordion, setToggleAccordion] = useState(false) + return (
-
-
- Vyper logo -

yper Compiler

-
- - - -
@@ -117,25 +122,55 @@ const App = () => {
-
-
- setEnvironment('remote')} label="Remote Compiler" style={{cursor: state.environment === 'remote' ? 'default' : 'pointer'}} className="d-flex mr-4" /> - setEnvironment('local')} label="Local Compiler" style={{cursor: state.environment === 'local' ? 'default' : 'pointer'}} /> + + +
+
+ + Advanced Compiler Settings + +
+ +
+ +
+
+ setEnvironment('remote')} className={`custom-control-input ${state.environment === 'remote' ? 'd-flex mr-1' : 'd-flex mr-1 cursor-status'}`} /> + +
+
+ setEnvironment('local')} className={`custom-control-input ${state.environment === 'local' ? '' : `cursor-status`}`} /> + +
+
+ + +
+
- - Specify the compiler version & EVM version in the .vy file - +
+ + Specify the{' '} + + compiler version + {' '} + &{' '} + + EVM version + {' '} + in the .vy file. +
setOutput({...output, [name]: update})} resetCompilerState={resetCompilerResultState} />
-
+
{output && Object.keys(output).length > 0 && output.status !== 'failed' ? ( <> ) : output.status === 'failed' ? ( - + ) : null}
diff --git a/apps/vyper/src/app/components/CompileErrorCard.tsx b/apps/vyper/src/app/components/CompileErrorCard.tsx index 4c8d1128b8..dce0afe26b 100644 --- a/apps/vyper/src/app/components/CompileErrorCard.tsx +++ b/apps/vyper/src/app/components/CompileErrorCard.tsx @@ -1,12 +1,17 @@ +import {CopyToClipboard} from '@remix-ui/clipboard' import Reaact from 'react' +import { RemixClient } from '../utils' -export function CompileErrorCard(props: any) { +export function CompileErrorCard(props: { output: any, plugin: RemixClient }) { return ( -
- +
{props.output.message.trim()} +
+
+ await props.plugin.askGpt(props.output.message)}> + Ask GPT + + + + +
+
) } diff --git a/apps/vyper/src/app/components/CompilerButton.tsx b/apps/vyper/src/app/components/CompilerButton.tsx index f7d2331348..54597171e9 100644 --- a/apps/vyper/src/app/components/CompilerButton.tsx +++ b/apps/vyper/src/app/components/CompilerButton.tsx @@ -29,7 +29,7 @@ function CompilerButton({contract, setOutput, compilerUrl, resetCompilerState}: className="btn btn-primary w-100 d-block btn-block text-break remixui_disabled mb-1 mt-3" >
- + {/* */}
Compile {contract} diff --git a/apps/vyper/src/app/components/CustomAccordionToggle.tsx b/apps/vyper/src/app/components/CustomAccordionToggle.tsx new file mode 100644 index 0000000000..67b11d9e20 --- /dev/null +++ b/apps/vyper/src/app/components/CustomAccordionToggle.tsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react' +import { useAccordionToggle } from 'react-bootstrap/AccordionToggle' + +export type CustomAccordionToggleProps = { + children: React.ReactNode + eventKey: string + callback?: any +} + +export default function CustomAccordionToggle({ children, eventKey }: CustomAccordionToggleProps) { + const [toggleAccordion, setToggleAccordion] = useState(false) + + const decoratedOnClick = useAccordionToggle(eventKey, () => + setToggleAccordion(!toggleAccordion) + ) + + return ( +
+ {children} + +
+ ) +} diff --git a/apps/vyper/src/app/components/VyperResult.tsx b/apps/vyper/src/app/components/VyperResult.tsx index 3234ef0cdc..94b9fdba16 100644 --- a/apps/vyper/src/app/components/VyperResult.tsx +++ b/apps/vyper/src/app/components/VyperResult.tsx @@ -58,9 +58,8 @@ function VyperResult({ output, plugin }: VyperResultProps) { return ( <> -
-
- + - (Object.values(output)[0] as OutputType).bytecode.object} direction="bottom" icon="far fa-copy"> - +
diff --git a/apps/vyper/src/app/utils/compiler.tsx b/apps/vyper/src/app/utils/compiler.tsx index fcb32a5b7a..c3b9182e1d 100644 --- a/apps/vyper/src/app/utils/compiler.tsx +++ b/apps/vyper/src/app/utils/compiler.tsx @@ -31,9 +31,7 @@ export interface VyperCompilationError { export type VyperCompilationOutput = VyperCompilationResult | VyperCompilationError /** Check if the output is an error */ -export function isCompilationError(output: VyperCompilationOutput): output is VyperCompilationError { - return output.status === 'failed' -} +export const isCompilationError = (output: VyperCompilationOutput): output is VyperCompilationError => output.status === 'failed' export function normalizeContractPath(contractPath: string): string[] { const paths = contractPath.split('/') @@ -52,22 +50,113 @@ function parseErrorString(errorString) { // Split the string into lines let lines = errorString.trim().split('\n') // Extract the line number and message - let message = lines[1].trim() + let message = errorString.trim() let targetLine = lines[2].split(',') - let lineColumn = targetLine[targetLine.length - 1].split(' ')[2].split(':') + let tline = lines[2].trim().split(' ')[1].split(':') + const errorObject = { status: 'failed', message: message, - column: parseInt(lineColumn[1]), - line: parseInt(lineColumn[0]) + column: tline[1], + line: tline[0] } message = null targetLine = null - lineColumn = null lines = null + tline = null return errorObject } +const buildError = (output) => { + if (isCompilationError(output)) { + const line = output.line + if (line) { + const lineColumnPos = { + start: {line: line - 1, column: 10}, + end: {line: line - 1, column: 10} + } + // remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4') + } else { + const regex = output?.message?.match(/line ((\d+):(\d+))+/g) + const errors = output?.message?.split(/line ((\d+):(\d+))+/g) // extract error message + if (regex) { + let errorIndex = 0 + regex.map((errorLocation) => { + const location = errorLocation?.replace('line ', '').split(':') + let message = errors[errorIndex] + errorIndex = errorIndex + 4 + if (message && message?.split('\n\n').length > 0) { + try { + message = message?.split('\n\n')[message.split('\n\n').length - 1] + } catch (e) {} + } + if (location?.length > 0) { + const lineColumnPos = { + start: {line: parseInt(location[0]) - 1, column: 10}, + end: {line: parseInt(location[0]) - 1, column: 10} + } + // remixClient.highlight(lineColumnPos as any, _contract.name, message) + } + }) + } + } + throw new Error(output.message) + } +} + +const compileReturnType = (output, contract) => { + const t: any = toStandardOutput(contract, output) + const temp = _.merge(t['contracts'][contract]) + const normal = normalizeContractPath(contract)[2] + const abi = temp[normal]['abi'] + const evm = _.merge(temp[normal]['evm']) + const dpb = evm.deployedBytecode + const runtimeBytecode = evm.bytecode + const methodIdentifiers = evm.methodIdentifiers + const version = output?.compilers[0]?.version ?? '0.3.10' + const optimized = output?.compilers[0]?.settings?.optimize ?? true + const evmVersion = '' + + const result: { + contractName: any, + abi: any, + bytecode: any, + runtimeBytecode: any, + ir: '', + methodIdentifiers: any, + version?: '', + evmVersion?: '' + optimized?: boolean + } = { + contractName: normal, + abi, + bytecode: dpb, + runtimeBytecode, + ir: '', + methodIdentifiers, + version, + evmVersion, + optimized + } + return result +} + +const fixContractContent = (content: string) => { + if (content.length === 0) return + const pragmaFound = content.includes('#pragma version ^0.3.10') + const evmVerFound = content.includes('#pragma evm-version shanghai') + const pragma = '#pragma version ^0.3.10' + const evmVer = '#pragma evm-version shanghai' + + if (!evmVerFound) { + content = `${evmVer}\n${content}` + } + if (!pragmaFound) { + content = `${pragma}\n${content}` + } + return content +} + /** * Compile the a contract * @param url The url of the compiler @@ -82,11 +171,13 @@ export async function compile(url: string, contract: Contract): Promise { throw new Error('Use extension .vy for Vyper.') } + + let contractName = contract['name'] const compilePackage = { manifest: 'ethpm/3', sources: { - [contractName] : {content : contract.content} + [contractName] : {content : fixContractContent(contract.content)} } } let response = await axios.post(`${url}compile`, compilePackage ) @@ -181,15 +272,11 @@ export async function compileContract(contract: string, compilerUrl: string, set try { _contract = await remixClient.getContract() } catch (e: any) { - // if (setOutput === null || setOutput === undefined) { - const compileResult = { + const errorGettingContract = { status: 'failed', message: e.message } - remixClient.eventEmitter.emit('setOutput', compileResult) - // } else { - // setOutput('', {status: 'failed', message: e.message}) - // } + remixClient.eventEmitter.emit('setOutput', errorGettingContract) return } remixClient.changeStatus({ @@ -198,76 +285,19 @@ export async function compileContract(contract: string, compilerUrl: string, set title: 'Compiling' }) let output - try { - output = await compile(compilerUrl, _contract) - console.log('checking compile result', output) - remixClient.eventEmitter.emit('setOutput', output) - } catch (e: any) { + // try { + output = await compile(compilerUrl, _contract) + if (output.status === 'failed') { remixClient.changeStatus({ key: 'failed', type: 'error', - title: `${e.message} debugging` + title: 'Compilation failed...' }) - // setOutput !== null || setOutput !== undefined && setOutput('', {status: 'failed', message: e.message}) - remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: e.message}) + remixClient.eventEmitter.emit('setOutput', {status: 'failed', message: output.message, title: 'Error compiling...', line: output.line, column: output.column}) + output = null return } - const compileReturnType = () => { - const t: any = toStandardOutput(contract, output) - const temp = _.merge(t['contracts'][contract]) - const normal = normalizeContractPath(contract)[2] - const abi = temp[normal]['abi'] - const evm = _.merge(temp[normal]['evm']) - const dpb = evm.deployedBytecode - const runtimeBytecode = evm.bytecode - const methodIdentifiers = evm.methodIdentifiers - - const result = { - contractName: normal, - abi: abi, - bytecode: dpb, - runtimeBytecode: runtimeBytecode, - ir: '', - methodIdentifiers: methodIdentifiers - } - return result - } - // ERROR - if (isCompilationError(output)) { - const line = output.line - if (line) { - const lineColumnPos = { - start: {line: line - 1, column: 10}, - end: {line: line - 1, column: 10} - } - // remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4') - } else { - const regex = output?.message?.match(/line ((\d+):(\d+))+/g) - const errors = output?.message?.split(/line ((\d+):(\d+))+/g) // extract error message - if (regex) { - let errorIndex = 0 - regex.map((errorLocation) => { - const location = errorLocation?.replace('line ', '').split(':') - let message = errors[errorIndex] - errorIndex = errorIndex + 4 - if (message && message?.split('\n\n').length > 0) { - try { - message = message?.split('\n\n')[message.split('\n\n').length - 1] - } catch (e) {} - } - if (location?.length > 0) { - const lineColumnPos = { - start: {line: parseInt(location[0]) - 1, column: 10}, - end: {line: parseInt(location[0]) - 1, column: 10} - } - // remixClient.highlight(lineColumnPos as any, _contract.name, message) - } - }) - } - } - throw new Error(output.message) - } // SUCCESS // remixClient.discardHighlight() remixClient.changeStatus({ @@ -278,12 +308,12 @@ export async function compileContract(contract: string, compilerUrl: string, set const data = toStandardOutput(_contract.name, output) remixClient.compilationFinish(_contract.name, _contract.content, data) + const contractName = _contract['name'] + const compileResult = compileReturnType(output, contractName) if (setOutput === null || setOutput === undefined) { - const contractName = _contract['name'] - const compileResult = compileReturnType() remixClient.eventEmitter.emit('setOutput', { contractName, compileResult }) } else { - setOutput(_contract.name, compileReturnType()) + remixClient.eventEmitter.emit('setOutput', { contractName, compileResult }) } } catch (err: any) { remixClient.changeStatus({ diff --git a/apps/vyper/src/app/utils/remix-client.tsx b/apps/vyper/src/app/utils/remix-client.tsx index 7e4a1952a6..94d149167b 100644 --- a/apps/vyper/src/app/utils/remix-client.tsx +++ b/apps/vyper/src/app/utils/remix-client.tsx @@ -60,6 +60,23 @@ export class RemixClient extends PluginClient { } } + async askGpt(message: string) { + if (message.length === 0) { + this.client.call('terminal', 'log', { type: 'log', value: 'kindly send a proper message so I can respond please' }) + return + } + try { + const formattedMessage = ` + ${message} + can you explain why this error occurred and how to fix it? + ` + await this.client.call('openaigpt' as any, 'message', formattedMessage) + } catch (err) { + console.error('unable to askGpt') + console.error(err) + } + } + async cloneVyperRepo() { try { // @ts-ignore @@ -144,4 +161,3 @@ export class RemixClient extends PluginClient { } export const remixClient = new RemixClient() -// export const RemixClientContext = React.createContext(new RemixClient()) diff --git a/apps/vyper/src/index.html b/apps/vyper/src/index.html index 85eb9df0fd..d5d907e0f9 100644 --- a/apps/vyper/src/index.html +++ b/apps/vyper/src/index.html @@ -7,8 +7,12 @@ +
+ + diff --git a/apps/vyper/src/profile.json b/apps/vyper/src/profile.json index 8ed04d3cb3..669d5340c4 100644 --- a/apps/vyper/src/profile.json +++ b/apps/vyper/src/profile.json @@ -1,12 +1,12 @@ { "name": "vyper", - "displayName": "Vyper", + "displayName": "Vyper Compiler", "methods": ["getCompilationResult", "compile", "vyperCompileCustomAction"], "url": "https://ipfs-cluster.ethdevops.io/ipfs/QmbmPzUg7ghTKcF2eo64zm1k1LKdibYfqYmiqXkHKXks8r", - "documentation": "https://remix-ide.readthedocs.io/en/latest/plugin_list.html", + "documentation": "https://remix-ide.readthedocs.io/en/latest/vyper.html", "description": "Compile vyper contracts", "kind": "compiler", - "icon": "data:image/svg+xml;base64,PHN2ZyBpZD0iRmxhdF9Mb2dvIiBkYXRhLW5hbWU9IkZsYXQgTG9nbyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjA0OCAxNzczLjYyIj4gIDx0aXRsZT52eXBlci1sb2dvLWZsYXQ8L3RpdGxlPiAgPHBvbHlsaW5lIHBvaW50cz0iMTAyNCA4ODYuODEgNzY4IDEzMzAuMjIgMTAyNCAxNzczLjYyIDEyODAgMTMzMC4yMiAxMDI0IDg4Ni44MSIgc3R5bGU9ImZpbGw6IzMzMyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTI4MCA0NDMuNDEgMTAyNCA4ODYuODEgMTI4MCAxMzMwLjIyIDE1MzYgODg2LjgxIDEyODAgNDQzLjQxIiBzdHlsZT0iZmlsbDojNjY2Ii8+ICA8cG9seWxpbmUgcG9pbnRzPSI3NjggNDQzLjQxIDUxMiA4ODYuODEgNzY4IDEzMzAuMjIgMTAyNCA4ODYuODEgNzY4IDQ0My40MSIgc3R5bGU9ImZpbGw6IzY2NiIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTUzNiAwIDEyODAgNDQzLjQxIDE1MzYgODg2LjgxIDE3OTIgNDQzLjQxIDE1MzYgMCIgc3R5bGU9ImZpbGw6IzhjOGM4YyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTE1MiAyMjEuNyA4OTYgMjIxLjcgNzY4IDQ0My40MSAxMDI0IDg4Ni44MSAxMjgwIDQ0My40MSAxMTUyIDIyMS43IiBzdHlsZT0iZmlsbDojOGM4YzhjIi8+ICA8cG9seWxpbmUgcG9pbnRzPSI1MTIgMCAyNTYgNDQzLjQxIDUxMiA4ODYuODEgNzY4IDQ0My40MSA1MTIgMCIgc3R5bGU9ImZpbGw6IzhjOGM4YyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMjA0OCAwIDE1MzYgMCAxNzkyIDQ0My40IDIwNDggMCIgc3R5bGU9ImZpbGw6I2IyYjJiMiIvPiAgPHBvbHlsaW5lIHBvaW50cz0iNTEyIDAgMCAwIDI1NiA0NDMuNCA1MTIgMCIgc3R5bGU9ImZpbGw6I2IyYjJiMiIvPjwvc3ZnPg==", + "icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzM4IiBoZWlnaHQ9IjM3NiIgdmlld0JveD0iMCAwIDMzOCAzNzYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xOTUuMzA0IDMxMS44MzdMODIuNTE4IDE5OS4wODRDNzcuOTI0MSAxOTQuMzA0IDc1LjM5MjcgMTg3LjgzNyA3NS4zOTI3IDE4MC42MlYxOS42OTE0Qzc1LjM5MjcgMTAuMzE4OCA2Ni4wMTc0IDAuOTQ2MTI3IDU2LjY0MiAwLjk0NjEyN0gxOS4wNDY3QzkuNjcxMjggMC45NDYxMjcgMC4yOTU4OTggMTAuMzE4OCAwLjI5NTg5OCAxOS42OTE0VjIwNy4xNDVDMC4yOTU4OTggMjE3LjA4IDMuOTUyMyAyMjYuNjQgMTAuOTgzOCAyMzMuNjY5TDE0Mi4yMzkgMzY0Ljg4NkMxNDkuNTUyIDM3Mi4xOTcgMTU5LjExNSAzNzUuODUyIDE2OC43NzIgMzc1Ljg1MkgxODcuNTIyQzE5Ny45MjkgMzc1Ljg1MiAyMDYuMjczIDM2Ny40MTcgMjA2LjI3MyAzNTcuMTA3VjMzOC40NTZDMjA2LjI3MyAzMjguODAyIDIwMi42MTcgMzE5LjI0MiAxOTUuMzA0IDMxMS45MzFWMzExLjgzN1oiIGZpbGw9IiMxODBDMjUiLz4KPHBhdGggZD0iTTMzNy44MSAxOS41OTc3QzMzNy44MSAxMC4xMzEzIDMyOC41MjkgMC44NTI0MTcgMzE5LjA1OSAwLjg1MjQxN0gyODEuNDY0QzI3Mi4wODkgMC44NTI0MTcgMjYyLjcxMyAxMC4yMjUxIDI2Mi43MTMgMTkuNTk3N1YxODAuNDMzQzI2Mi43MTMgMTg3LjY1IDI1OS45MDEgMTk0LjExNyAyNTUuMjEzIDE5OC45OUwyMTcuNDMgMjM2Ljc2MkMyMTAuMTE4IDI0NC4wNzMgMjA2LjQ2MSAyNTMuNjMzIDIwNi40NjEgMjYzLjI4N1YyODIuMDMyQzIwNi40NjEgMjkyLjQzNiAyMTQuODk5IDMwMC43NzggMjI1LjIxMiAzMDAuNzc4SDI0My45NjNDMjUzLjUyNiAzMDAuNzc4IDI2My4xODIgMjk3LjEyMiAyNzAuNDk1IDI4OS44MTFMMzI2Ljc0NyAyMzMuNTc2QzMzMy43NzkgMjI2LjU0NiAzMzcuNzE2IDIxNi45ODYgMzM3LjcxNiAyMDcuMDUxVjE5LjU5NzdIMzM3LjgxWiIgZmlsbD0iIzE4MEMyNSIvPgo8L3N2Zz4K", "location": "sidePanel", "repo": "https://github.com/ethereum/remix-project/tree/master/apps/vyper", "maintainedBy": "Remix", diff --git a/libs/ghaction-helper/package.json b/libs/ghaction-helper/package.json index 2678c622e5..1ba545bc93 100644 --- a/libs/ghaction-helper/package.json +++ b/libs/ghaction-helper/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/ghaction-helper", - "version": "0.1.22", + "version": "0.1.25", "description": "Solidity Tests GitHub Action Helper", "main": "src/index.js", "scripts": { @@ -19,17 +19,17 @@ }, "homepage": "https://github.com/ethereum/remix-project#readme", "devDependencies": { - "@remix-project/remix-solidity": "^0.5.28", + "@remix-project/remix-solidity": "^0.5.31", "@types/chai": "^4.3.4", "typescript": "^4.9.3" }, "dependencies": { "@ethereum-waffle/chai": "^3.4.4", - "@remix-project/remix-simulator": "^0.2.42", + "@remix-project/remix-simulator": "^0.2.45", "chai": "^4.3.7", "ethers": "^5.7.2", "web3": "^4.1.1" }, "types": "./src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901" + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c" } \ No newline at end of file diff --git a/libs/remix-analyzer/package.json b/libs/remix-analyzer/package.json index 0e0c3930b0..89e4f078f3 100644 --- a/libs/remix-analyzer/package.json +++ b/libs/remix-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-analyzer", - "version": "0.5.51", + "version": "0.5.54", "description": "Tool to perform static analysis on Solidity smart contracts", "scripts": { "test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts" @@ -21,12 +21,12 @@ } ], "dependencies": { - "@ethereumjs/block": "^4.2.0", - "@ethereumjs/tx": "^4.1.1", - "@ethereumjs/util": "^8.0.5", - "@ethereumjs/vm": "^6.4.1", - "@remix-project/remix-astwalker": "^0.0.72", - "@remix-project/remix-lib": "^0.5.49", + "@ethereumjs/block": "5.2.0", + "@ethereumjs/tx": "5.3.0", + "@ethereumjs/util": "9.0.3", + "@ethereumjs/vm": "8.0.0", + "@remix-project/remix-astwalker": "^0.0.75", + "@remix-project/remix-lib": "^0.5.52", "async": "^2.6.2", "ethers": "^5.4.2", "ethjs-util": "^0.1.6", @@ -50,6 +50,6 @@ "typescript": "^3.7.5" }, "typings": "src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901", + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c", "main": "./src/index.js" } \ No newline at end of file diff --git a/libs/remix-astwalker/package.json b/libs/remix-astwalker/package.json index ff25025c3b..e95507f89b 100644 --- a/libs/remix-astwalker/package.json +++ b/libs/remix-astwalker/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-astwalker", - "version": "0.0.72", + "version": "0.0.75", "description": "Tool to walk through Solidity AST", "main": "src/index.js", "scripts": { @@ -33,11 +33,11 @@ ] }, "dependencies": { - "@ethereumjs/block": "^4.2.0", - "@ethereumjs/tx": "^4.1.1", - "@ethereumjs/util": "^8.0.5", - "@ethereumjs/vm": "^6.4.1", - "@remix-project/remix-lib": "^0.5.49", + "@ethereumjs/block": "5.2.0", + "@ethereumjs/tx": "5.3.0", + "@ethereumjs/util": "9.0.3", + "@ethereumjs/vm": "8.0.0", + "@remix-project/remix-lib": "^0.5.52", "@types/tape": "^4.2.33", "async": "^2.6.2", "ethers": "^5.4.2", @@ -53,6 +53,6 @@ "tap-spec": "^5.0.0" }, "typings": "src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901", + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-core-plugin/src/index.ts b/libs/remix-core-plugin/src/index.ts index db3788cd5f..6e3ef19562 100644 --- a/libs/remix-core-plugin/src/index.ts +++ b/libs/remix-core-plugin/src/index.ts @@ -8,3 +8,4 @@ export * from './types/contract' export { LinkLibraries, DeployLibraries } from './lib/link-libraries' export { OpenZeppelinProxy } from './lib/openzeppelin-proxy' export { fetchContractFromEtherscan } from './lib/helpers/fetch-etherscan' +export { fetchContractFromBlockscout } from './lib/helpers/fetch-blockscout' diff --git a/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts b/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts index 5303ec0f3f..da73f7af52 100644 --- a/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts +++ b/libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts @@ -4,7 +4,7 @@ import { util } from '@remix-project/remix-lib' import { toChecksumAddress } from '@ethereumjs/util' import { fetchContractFromEtherscan } from './helpers/fetch-etherscan' import { fetchContractFromSourcify } from './helpers/fetch-sourcify' -import { UUPSDeployedByteCode, UUPSCompilerVersion, UUPSOptimize, UUPSRuns, UUPSEvmVersion, UUPSLanguage, UUPSDeployedByteCodeV5, UUPSCompilerVersionV5 } from './constants/uups' +import { UUPSDeployedByteCode, UUPSCompilerVersion, UUPSOptimize, UUPSRuns, UUPSEvmVersion, UUPSLanguage, UUPSDeployedByteCodeV5, UUPSCompilerVersionV5, UUPSEvmVersionv5, UUPSOptimizev5 } from './constants/uups' const profile = { name: 'fetchAndCompile', @@ -88,8 +88,8 @@ export class FetchAndCompile extends Plugin { const settings = { version: UUPSCompilerVersionV5, language: UUPSLanguage, - evmVersion: UUPSEvmVersion, - optimize: UUPSOptimize, + evmVersion: UUPSEvmVersionv5, + optimize: UUPSOptimizev5, runs: UUPSRuns } const proxyUrl = 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.0/contracts/proxy/ERC1967/ERC1967Proxy.sol' diff --git a/libs/remix-core-plugin/src/lib/constants/uups.ts b/libs/remix-core-plugin/src/lib/constants/uups.ts index 936d6ad440..2b54aaa86a 100644 --- a/libs/remix-core-plugin/src/lib/constants/uups.ts +++ b/libs/remix-core-plugin/src/lib/constants/uups.ts @@ -1,8 +1,8 @@ export const UUPS = 'UUPSUpgradeable' export const UUPSBytecode = '608060405260405162000c6638038062000c6683398181016040528101906200002991906200041e565b6200003d828260006200004560201b60201c565b5050620007e2565b62000056836200008860201b60201c565b600082511180620000645750805b156200008357620000818383620000df60201b620000371760201c565b505b505050565b62000099816200011560201b60201c565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b60606200010d838360405180606001604052806027815260200162000c3f60279139620001eb60201b60201c565b905092915050565b6200012b816200027d60201b620000641760201c565b6200016d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001649062000587565b60405180910390fd5b80620001a77f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b620002a060201b620000871760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516200021791906200054a565b600060405180830381855af49150503d806000811462000254576040519150601f19603f3d011682016040523d82523d6000602084013e62000259565b606091505b50915091506200027286838387620002aa60201b60201c565b925050509392505050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b606083156200031b576000835114156200031257620002cf856200027d60201b60201c565b62000311576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200030890620005a9565b60405180910390fd5b5b8290506200032e565b6200032d83836200033660201b60201c565b5b949350505050565b6000825111156200034a5781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000380919062000563565b60405180910390fd5b6000620003a06200039a84620005f4565b620005cb565b905082815260208101848484011115620003bf57620003be62000730565b5b620003cc84828562000690565b509392505050565b600081519050620003e581620007c8565b92915050565b600082601f8301126200040357620004026200072b565b5b81516200041584826020860162000389565b91505092915050565b600080604083850312156200043857620004376200073a565b5b60006200044885828601620003d4565b925050602083015167ffffffffffffffff8111156200046c576200046b62000735565b5b6200047a85828601620003eb565b9150509250929050565b600062000491826200062a565b6200049d818562000640565b9350620004af81856020860162000690565b80840191505092915050565b6000620004c88262000635565b620004d481856200064b565b9350620004e681856020860162000690565b620004f1816200073f565b840191505092915050565b60006200050b602d836200064b565b9150620005188262000750565b604082019050919050565b600062000532601d836200064b565b91506200053f826200079f565b602082019050919050565b600062000558828462000484565b915081905092915050565b600060208201905081810360008301526200057f8184620004bb565b905092915050565b60006020820190508181036000830152620005a281620004fc565b9050919050565b60006020820190508181036000830152620005c48162000523565b9050919050565b6000620005d7620005ea565b9050620005e58282620006c6565b919050565b6000604051905090565b600067ffffffffffffffff821115620006125762000611620006fc565b5b6200061d826200073f565b9050602081019050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b6000620006698262000670565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60005b83811015620006b057808201518184015260208101905062000693565b83811115620006c0576000848401525b50505050565b620006d1826200073f565b810181811067ffffffffffffffff82111715620006f357620006f2620006fc565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b620007d3816200065c565b8114620007df57600080fd5b50565b61044d80620007f26000396000f3fe6080604052366100135761001161001d565b005b61001b61001d565b005b610025610091565b610035610030610093565b6100a2565b565b606061005c83836040518060600160405280602781526020016103f1602791396100c8565b905092915050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b565b600061009d61014e565b905090565b3660008037600080366000845af43d6000803e80600081146100c3573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516100f291906102f8565b600060405180830381855af49150503d806000811461012d576040519150601f19603f3d011682016040523d82523d6000602084013e610132565b606091505b5091509150610143868383876101a5565b925050509392505050565b600061017c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610087565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060831561020857600083511415610200576101c085610064565b6101ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f690610331565b60405180910390fd5b5b829050610213565b610212838361021b565b5b949350505050565b60008251111561022e5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610262919061030f565b60405180910390fd5b600061027682610351565b6102808185610367565b9350610290818560208601610383565b80840191505092915050565b60006102a78261035c565b6102b18185610372565b93506102c1818560208601610383565b6102ca816103b6565b840191505092915050565b60006102e2601d83610372565b91506102ed826103c7565b602082019050919050565b6000610304828461026b565b915081905092915050565b60006020820190508181036000830152610329818461029c565b905092915050565b6000602082019050818103600083015261034a816102d5565b9050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60005b838110156103a1578082015181840152602081019050610386565b838111156103b0576000848401525b50505050565b6000601f19601f8301169050919050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060008201525056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122055b27d4c39ab82b8890fc1565c3858a1b7f1e0f5780871061f908d7503dcf94e64736f6c63430008070033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564' -export const UUPSBytecodeV5 = '60806040526040516106ae3803806106ae833981810160405281019061002591906104f2565b610035828261003c60201b60201c565b50506105ce565b61004b826100c060201b60201c565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a25f815111156100ad576100a7828261018f60201b60201c565b506100bc565b6100bb61021560201b60201c565b5b5050565b5f8173ffffffffffffffffffffffffffffffffffffffff163b0361011b57806040517f4c9c8ce3000000000000000000000000000000000000000000000000000000008152600401610112919061055b565b60405180910390fd5b8061014d7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b61025160201b60201c565b5f015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60605f808473ffffffffffffffffffffffffffffffffffffffff16846040516101b891906105b8565b5f60405180830381855af49150503d805f81146101f0576040519150601f19603f3d011682016040523d82523d5f602084013e6101f5565b606091505b509150915061020b85838361025a60201b60201c565b9250505092915050565b5f34111561024f576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b5f819050919050565b60608261027557610270826102ed60201b60201c565b6102e5565b5f825114801561029b57505f8473ffffffffffffffffffffffffffffffffffffffff163b145b156102dd57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016102d4919061055b565b60405180910390fd5b8190506102e6565b5b9392505050565b5f815111156102ff5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61036b82610342565b9050919050565b61037b81610361565b8114610385575f80fd5b50565b5f8151905061039681610372565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6103ea826103a4565b810181811067ffffffffffffffff82111715610409576104086103b4565b5b80604052505050565b5f61041b610331565b905061042782826103e1565b919050565b5f67ffffffffffffffff821115610446576104456103b4565b5b61044f826103a4565b9050602081019050919050565b5f5b8381101561047957808201518184015260208101905061045e565b5f8484015250505050565b5f6104966104918461042c565b610412565b9050828152602081018484840111156104b2576104b16103a0565b5b6104bd84828561045c565b509392505050565b5f82601f8301126104d9576104d861039c565b5b81516104e9848260208601610484565b91505092915050565b5f80604083850312156105085761050761033a565b5b5f61051585828601610388565b925050602083015167ffffffffffffffff8111156105365761053561033e565b5b610542858286016104c5565b9150509250929050565b61055581610361565b82525050565b5f60208201905061056e5f83018461054c565b92915050565b5f81519050919050565b5f81905092915050565b5f61059282610574565b61059c818561057e565b93506105ac81856020860161045c565b80840191505092915050565b5f6105c38284610588565b915081905092915050565b60d4806105da5f395ff3fe6080604052600a600c565b005b60186014601a565b6026565b565b5f60216044565b905090565b365f80375f80365f845af43d5f803e805f81146040573d5ff35b3d5ffd5b5f606e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6095565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f81905091905056fea26469706673582212202e577dc05daa509bce6e6a86e29397d40b31118aea9fcd0aa88768ad25c610f964736f6c63430008150033' +export const UUPSBytecodeV5 = '60806040526040516106e53803806106e583398181016040528101906100259190610512565b610035828261003c60201b60201c565b50506105f6565b61004b826100c160201b60201c565b8173ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a26000815111156100ae576100a8828261019460201b60201c565b506100bd565b6100bc61021e60201b60201c565b5b5050565b60008173ffffffffffffffffffffffffffffffffffffffff163b0361011d57806040517f4c9c8ce3000000000000000000000000000000000000000000000000000000008152600401610114919061057d565b60405180910390fd5b806101507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61025b60201b60201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60606000808473ffffffffffffffffffffffffffffffffffffffff16846040516101be91906105df565b600060405180830381855af49150503d80600081146101f9576040519150601f19603f3d011682016040523d82523d6000602084013e6101fe565b606091505b509150915061021485838361026560201b60201c565b9250505092915050565b6000341115610259576040517fb398979f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6000819050919050565b6060826102805761027b826102fa60201b60201c565b6102f2565b600082511480156102a8575060008473ffffffffffffffffffffffffffffffffffffffff163b145b156102ea57836040517f9996b3150000000000000000000000000000000000000000000000000000000081526004016102e1919061057d565b60405180910390fd5b8190506102f3565b5b9392505050565b60008151111561030d5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061037e82610353565b9050919050565b61038e81610373565b811461039957600080fd5b50565b6000815190506103ab81610385565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610404826103bb565b810181811067ffffffffffffffff82111715610423576104226103cc565b5b80604052505050565b600061043661033f565b905061044282826103fb565b919050565b600067ffffffffffffffff821115610462576104616103cc565b5b61046b826103bb565b9050602081019050919050565b60005b8381101561049657808201518184015260208101905061047b565b60008484015250505050565b60006104b56104b084610447565b61042c565b9050828152602081018484840111156104d1576104d06103b6565b5b6104dc848285610478565b509392505050565b600082601f8301126104f9576104f86103b1565b5b81516105098482602086016104a2565b91505092915050565b6000806040838503121561052957610528610349565b5b60006105378582860161039c565b925050602083015167ffffffffffffffff8111156105585761055761034e565b5b610564858286016104e4565b9150509250929050565b61057781610373565b82525050565b6000602082019050610592600083018461056e565b92915050565b600081519050919050565b600081905092915050565b60006105b982610598565b6105c381856105a3565b93506105d3818560208601610478565b80840191505092915050565b60006105eb82846105ae565b915081905092915050565b60e1806106046000396000f3fe6080604052600a600c565b005b60186014601a565b6027565b565b60006022604c565b905090565b3660008037600080366000845af43d6000803e80600081146047573d6000f35b3d6000fd5b600060787f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b60a1565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600081905091905056fea264697066735822122041a27953478306ba8612ebf56791d0be3be840199c19e335afbff17eadfc84ef64736f6c63430008150033' export const UUPSDeployedByteCode = '6080604052366100135761001161001d565b005b61001b61001d565b005b610025610091565b610035610030610093565b6100a2565b565b606061005c83836040518060600160405280602781526020016103f1602791396100c8565b905092915050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b565b600061009d61014e565b905090565b3660008037600080366000845af43d6000803e80600081146100c3573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516100f291906102f8565b600060405180830381855af49150503d806000811461012d576040519150601f19603f3d011682016040523d82523d6000602084013e610132565b606091505b5091509150610143868383876101a5565b925050509392505050565b600061017c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610087565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6060831561020857600083511415610200576101c085610064565b6101ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f690610331565b60405180910390fd5b5b829050610213565b610212838361021b565b5b949350505050565b60008251111561022e5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610262919061030f565b60405180910390fd5b600061027682610351565b6102808185610367565b9350610290818560208601610383565b80840191505092915050565b60006102a78261035c565b6102b18185610372565b93506102c1818560208601610383565b6102ca816103b6565b840191505092915050565b60006102e2601d83610372565b91506102ed826103c7565b602082019050919050565b6000610304828461026b565b915081905092915050565b60006020820190508181036000830152610329818461029c565b905092915050565b6000602082019050818103600083015261034a816102d5565b9050919050565b600081519050919050565b600081519050919050565b600081905092915050565b600082825260208201905092915050565b60005b838110156103a1578082015181840152602081019050610386565b838111156103b0576000848401525b50505050565b6000601f19601f8301169050919050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060008201525056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122055b27d4c39ab82b8890fc1565c3858a1b7f1e0f5780871061f908d7503dcf94e64736f6c63430008070033' -export const UUPSDeployedByteCodeV5 = '6080604052600a600c565b005b60186014601a565b6026565b565b5f60216044565b905090565b365f80375f80365f845af43d5f803e805f81146040573d5ff35b3d5ffd5b5f606e7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5f1b6095565b5f015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b5f81905091905056fea26469706673582212202e577dc05daa509bce6e6a86e29397d40b31118aea9fcd0aa88768ad25c610f964736f6c63430008150033' +export const UUPSDeployedByteCodeV5 = '6080604052600a600c565b005b60186014601a565b6027565b565b60006022604c565b905090565b3660008037600080366000845af43d6000803e80600081146047573d6000f35b3d6000fd5b600060787f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b60a1565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600081905091905056fea264697066735822122041a27953478306ba8612ebf56791d0be3be840199c19e335afbff17eadfc84ef64736f6c63430008150033' // OZ v4.8.0 bytecode compilation config export const UUPSProxyContractCode = 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/proxy/ERC1967/ERC1967Proxy.sol' @@ -18,7 +18,7 @@ export const UUPSCompilerVersionV5 = 'soljson-v0.8.21+commit.d9974bed.js' export const UUPSLanguagev5 = 'Solidity' export const UUPSOptimizev5 = false export const UUPSRunsv5 = 0 -export const UUPSEvmVersionv5 = null +export const UUPSEvmVersionv5 = 'paris' export const UUPSABI = [ { "inputs": [ diff --git a/libs/remix-core-plugin/src/lib/gist-handler.ts b/libs/remix-core-plugin/src/lib/gist-handler.ts index c3a53a75f1..83c932d4c6 100644 --- a/libs/remix-core-plugin/src/lib/gist-handler.ts +++ b/libs/remix-core-plugin/src/lib/gist-handler.ts @@ -117,8 +117,11 @@ export class GistHandler extends Plugin { const gistIdWorkspace = 'gist ' + gistId const workspaces = await this.call('filePanel', 'getWorkspaces') const found = workspaces.find((workspace) => workspace.name === gistIdWorkspace) - if (found) { - await this.call('notification', 'alert', `workspace "${gistIdWorkspace}" already exist`) + if (found) { + await this.call('notification', 'alert', { + id: 'gistAlert', + message: `workspace "${gistIdWorkspace}" already exists`, + }) return } await this.call('filePanel', 'createWorkspace', 'gist ' + gistId, '', true) diff --git a/libs/remix-core-plugin/src/lib/helpers/fetch-blockscout.ts b/libs/remix-core-plugin/src/lib/helpers/fetch-blockscout.ts new file mode 100644 index 0000000000..a2984c64c3 --- /dev/null +++ b/libs/remix-core-plugin/src/lib/helpers/fetch-blockscout.ts @@ -0,0 +1,66 @@ +export const fetchContractFromBlockscout = async (plugin, endpoint, contractAddress, targetPath, shouldSetFile = true) => { + let data + const compilationTargets = {} + + try { + data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress) + data = await data.json() + // blockscout api doc https://blockscout.com/poa/core/api-docs + if (data.message === 'OK' && data.status === "1") { + if (data.result.length) { + if (!data.result[0].SourceCode || data.result[0].SourceCode === '') { + throw new Error(`contract not verified on Blockscout ${endpoint} network`) + } + } + } else throw new Error('unable to retrieve contract data ' + data.message) + } catch (e) { + throw new Error('unable to retrieve contract data: ' + e.message) + } + + if (!data || !data.result) { + return null + } + + if (data.result[0].FileName === '') { + const fileName = `${targetPath}/${data.result[0].ContractName}.sol` + if (shouldSetFile) await plugin.call('fileManager', 'setFile', fileName, data.result[0].SourceCode) + compilationTargets[fileName] = { content: data.result[0].SourceCode } + } else { + const sources = {} + sources[data.result[0].FileName] = data.result[0].SourceCode + if (data.result[0].AdditionalSources && Array.isArray(data.result[0].AdditionalSources)) { + for (const object of data.result[0].AdditionalSources) { + sources[object.Filename] = object.SourceCode + } + } + + for (let [file, source] of Object.entries(sources)) { // eslint-disable-line + file = file.replace('browser/', '') // should be fixed in the remix IDE end. + file = file.replace(/^\//g, '') // remove first slash. + if (await plugin.call('contentImport', 'isExternalUrl', file)) { + // nothing to do, the compiler callback will handle those + } else { + const path = `${targetPath}/${file}` + const content = source + if (shouldSetFile) await plugin.call('fileManager', 'setFile', path, content) + compilationTargets[path] = { content } + } + } + } + + let runs = 0 + try { + runs = parseInt(data.result[0].OptimizationRuns) + } catch (e) { } + const settings = { + version: data.result[0].CompilerVersion.replace(/^v/, ''), + language: 'Solidity', + evmVersion: data.result[0].EVMVersion.toLowerCase(), + optimize: data.result[0].OptimizationUsed === 'true', + runs + } + return { + settings, + compilationTargets + } +} diff --git a/libs/remix-debug/package.json b/libs/remix-debug/package.json index fd0d5544a7..10a95862b8 100644 --- a/libs/remix-debug/package.json +++ b/libs/remix-debug/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-debug", - "version": "0.5.42", + "version": "0.5.45", "description": "Tool to debug Ethereum transactions", "contributors": [ { @@ -21,15 +21,15 @@ "test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts" }, "dependencies": { - "@ethereumjs/block": "^4.2.0", - "@ethereumjs/common": "^3.1.1", - "@ethereumjs/tx": "^4.1.1", - "@ethereumjs/util": "^8.0.5", - "@ethereumjs/vm": "^6.4.1", - "@remix-project/remix-astwalker": "^0.0.72", - "@remix-project/remix-lib": "^0.5.49", - "@remix-project/remix-simulator": "^0.2.42", - "@remix-project/remix-solidity": "^0.5.28", + "@ethereumjs/block": "5.2.0", + "@ethereumjs/common": "4.3.0", + "@ethereumjs/tx": "5.3.0", + "@ethereumjs/util": "9.0.3", + "@ethereumjs/vm": "8.0.0", + "@remix-project/remix-astwalker": "^0.0.75", + "@remix-project/remix-lib": "^0.5.52", + "@remix-project/remix-simulator": "^0.2.45", + "@remix-project/remix-solidity": "^0.5.31", "ansi-gray": "^0.1.1", "async": "^2.6.2", "color-support": "^1.1.3", @@ -69,6 +69,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme", "typings": "src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901", + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-debug/src/code/codeUtils.ts b/libs/remix-debug/src/code/codeUtils.ts index f9784bb135..24907aa069 100644 --- a/libs/remix-debug/src/code/codeUtils.ts +++ b/libs/remix-debug/src/code/codeUtils.ts @@ -1,6 +1,8 @@ 'use strict' +import { bytesToHex } from '@ethereumjs/util' import { Common } from '@ethereumjs/common' -import { getOpcodesForHF, OpcodeList } from '@ethereumjs/evm/dist/opcodes/codes' +// TODO fix the import when getOpcodesForHF is exported +import { getOpcodesForHF } from '@ethereumjs/evm' import getOpcodes from './opcodes' export function nameOpCodes (raw, hardfork) { @@ -27,7 +29,8 @@ export function nameOpCodes (raw, hardfork) { i += jumpNum } - const data = (pushData as any).toString('hex') !== '' ? ' ' + (pushData as any).toString('hex') : '' + const hexCode = bytesToHex((pushData as any)) + const data = hexCode !== '' ? ' ' + hexCode : '' code.push(pad(pc, roundLog(raw.length, 10)) + ' ' + curOpCode + data) pushData = '' @@ -46,7 +49,7 @@ type Opcode = { * information about the opcode. */ export function parseCode (raw) { - const common = new Common({ chain: 'mainnet', hardfork: 'merge' }) + const common = new Common({ chain: 'mainnet', hardfork: 'cancun' }) const opcodes = getOpcodesForHF(common).opcodes const code = [] diff --git a/libs/remix-debug/src/code/disassembler.ts b/libs/remix-debug/src/code/disassembler.ts index b828fe7e5d..8087f44380 100644 --- a/libs/remix-debug/src/code/disassembler.ts +++ b/libs/remix-debug/src/code/disassembler.ts @@ -2,7 +2,7 @@ import { parseCode } from './codeUtils' import { util } from '@remix-project/remix-lib' -import { bufferToHex } from '@ethereumjs/util' +import { bytesToHex } from '@ethereumjs/util' function createExpressions (instructions) { const expressions = [] @@ -37,7 +37,7 @@ function createExpressions (instructions) { function toString (expr) { if (expr.name.slice(0, 4) === 'PUSH') { - return bufferToHex(expr.pushData) + return bytesToHex(expr.pushData) } else if (expr.name === 'JUMPDEST') { return expr.label + ':' } else if (expr.args) { diff --git a/libs/remix-debug/src/solidity-decoder/types/Mapping.ts b/libs/remix-debug/src/solidity-decoder/types/Mapping.ts index 2968ac1ba9..2810a9e6fb 100644 --- a/libs/remix-debug/src/solidity-decoder/types/Mapping.ts +++ b/libs/remix-debug/src/solidity-decoder/types/Mapping.ts @@ -2,7 +2,7 @@ import { hash } from '@remix-project/remix-lib' import { RefType } from './RefType' import { normalizeHex } from './util' -import { toBuffer, setLengthLeft, bufferToHex, addHexPrefix } from '@ethereumjs/util' +import { toBytes, setLengthLeft, bytesToHex, addHexPrefix } from '@ethereumjs/util' import BN from 'bn.js' export class Mapping extends RefType { @@ -44,7 +44,7 @@ export class Mapping extends RefType { } async decodeMappingsLocation (preimages, location, storageResolver) { - const mapSlot = normalizeHex(bufferToHex(location.slot)) + const mapSlot = normalizeHex('0x' + location.slot.toString(16)) if (!preimages[mapSlot]) { return {} } @@ -66,11 +66,11 @@ function getMappingLocation (key, position) { // > the value corresponding to a mapping key k is located at keccak256(k . p) where . is concatenation. // key should be a hex string, and position an int - const mappingK = toBuffer(addHexPrefix(key)) - let mappingP = toBuffer(addHexPrefix(position)) + const mappingK = toBytes(addHexPrefix(key)) + let mappingP = toBytes(addHexPrefix(position)) mappingP = setLengthLeft(mappingP, 32) const mappingKeyBuf = concatTypedArrays(mappingK, mappingP) - const mappingStorageLocation: Buffer = hash.keccak(mappingKeyBuf) + const mappingStorageLocation: Uint8Array = hash.keccak(mappingKeyBuf) const mappingStorageLocationinBn: BN = new BN(mappingStorageLocation, 16) return mappingStorageLocationinBn } diff --git a/libs/remix-debug/src/solidity-decoder/types/util.ts b/libs/remix-debug/src/solidity-decoder/types/util.ts index bd6a7f4bd1..a8d78b3acd 100644 --- a/libs/remix-debug/src/solidity-decoder/types/util.ts +++ b/libs/remix-debug/src/solidity-decoder/types/util.ts @@ -1,5 +1,5 @@ 'use strict' -import { bufferToHex, unpadHexString } from '@ethereumjs/util' +import { unpadHex } from '@ethereumjs/util' import BN from 'bn.js' export function decodeIntFromHex (value, byteLength, signed) { @@ -11,7 +11,7 @@ export function decodeIntFromHex (value, byteLength, signed) { } export function readFromStorage (slot, storageResolver): Promise { - const hexSlot = '0x' + normalizeHex(bufferToHex(slot)) + const hexSlot = '0x' + normalizeHex(slot.toString(16)) return new Promise((resolve, reject) => { storageResolver.storageSlot(hexSlot, (error, slot) => { if (error) { @@ -58,7 +58,7 @@ export function toBN (value) { if (value instanceof BN) { return value } else if (value.match && value.match(/^(0x)?([a-f0-9]*)$/)) { - value = unpadHexString(value) + value = unpadHex(value) value = value.replace('0x', '') value = new BN(value === '' ? '0' : value, 16) } else if (!isNaN(value)) { diff --git a/libs/remix-debug/src/trace/traceHelper.ts b/libs/remix-debug/src/trace/traceHelper.ts index 0f49bde110..1ab108b83d 100644 --- a/libs/remix-debug/src/trace/traceHelper.ts +++ b/libs/remix-debug/src/trace/traceHelper.ts @@ -44,7 +44,7 @@ export function isSSTOREInstruction (step) { } export function isSHA3Instruction (step) { - return step.op === 'SHA3' + return step.op === 'SHA3' || step.op === 'KECCAK256' } export function newContextStorage (step) { diff --git a/libs/remix-debug/src/trace/traceManager.ts b/libs/remix-debug/src/trace/traceManager.ts index e15f8e180b..97b3d70ce9 100644 --- a/libs/remix-debug/src/trace/traceManager.ts +++ b/libs/remix-debug/src/trace/traceManager.ts @@ -40,7 +40,7 @@ export class TraceManager { const networkId = await this.web3.eth.net.getId() this.fork = execution.forkAt(networkId, tx.blockNumber) } catch (e) { - this.fork = 'merge' + this.fork = 'cancun' console.log(`unable to detect fork, defaulting to ${this.fork}..`) console.error(e) } diff --git a/libs/remix-debug/test/decoder/stateTests/mapping.ts b/libs/remix-debug/test/decoder/stateTests/mapping.ts index 09056d3693..d13f4c36f5 100644 --- a/libs/remix-debug/test/decoder/stateTests/mapping.ts +++ b/libs/remix-debug/test/decoder/stateTests/mapping.ts @@ -10,7 +10,7 @@ import { InternalCallTree } from '../../../src/solidity-decoder/internalCallTree import * as vmCall from '../../vmCall' import { StorageResolver } from '../../../src/storage/storageResolver' import { StorageViewer } from '../../../src/storage/storageViewer' -import { Address, bufferToHex } from '@ethereumjs/util' +import { Address, bytesToHex } from '@ethereumjs/util' module.exports = async function testMappingStorage (st, cb) { const mappingStorage = require('../contracts/mappingStorage') diff --git a/libs/remix-lib/package.json b/libs/remix-lib/package.json index 72a2808445..dc379f5b73 100644 --- a/libs/remix-lib/package.json +++ b/libs/remix-lib/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-lib", - "version": "0.5.49", + "version": "0.5.52", "description": "Library to various Remix tools", "contributors": [ { @@ -17,7 +17,7 @@ "test": "./../../node_modules/.bin/ts-node --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts" }, "dependencies": { - "@ethereumjs/util": "^8.0.5", + "@ethereumjs/util": "9.0.3", "async": "^2.1.2", "create-hash": "^1.2.0", "ethers": "^5.7.2", @@ -55,6 +55,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme", "typings": "src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901", + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-lib/src/execution/forkAt.ts b/libs/remix-lib/src/execution/forkAt.ts index fe7b1b0804..163e47ab68 100644 --- a/libs/remix-lib/src/execution/forkAt.ts +++ b/libs/remix-lib/src/execution/forkAt.ts @@ -61,7 +61,15 @@ const forks = { }, { number: 15537394, - name: 'merge' + name: 'paris' + }, + { + number: 17034870, + name: 'shanghai' + }, + { + number: 19426587, + name: 'cancun' } ], 3: [ diff --git a/libs/remix-lib/src/execution/logsManager.ts b/libs/remix-lib/src/execution/logsManager.ts index 7d9bba2c6b..c81bea4b6c 100644 --- a/libs/remix-lib/src/execution/logsManager.ts +++ b/libs/remix-lib/src/execution/logsManager.ts @@ -1,6 +1,6 @@ import { eachOf } from 'async' import { randomBytes } from 'crypto' -import { toChecksumAddress } from '@ethereumjs/util' +import { toChecksumAddress, bytesToHex } from '@ethereumjs/util' export class LogsManager { notificationCallbacks @@ -19,8 +19,9 @@ export class LogsManager { checkBlock (blockNumber, block, web3) { eachOf(block.transactions, (tx: any, i, next) => { - const txHash = '0x' + tx.hash().toString('hex') + const txHash = bytesToHex(tx.hash()) web3.eth.getTransactionReceipt(txHash, (_error, receipt) => { + if (!receipt) return next() for (const log of receipt.logs) { this.oldLogs.push({ type: 'block', blockNumber, block, tx, log, txNumber: i, receipt }) const subscriptions = this.getSubscriptionsFor({ type: 'block', blockNumber, block, tx, log, receipt}) @@ -28,8 +29,8 @@ export class LogsManager { const result = { logIndex: '0x1', // 1 blockNumber: blockNumber, - blockHash: ('0x' + block.hash().toString('hex')), - transactionHash: ('0x' + tx.hash().toString('hex')), + blockHash: bytesToHex(block.hash()), + transactionHash: bytesToHex(tx.hash()), transactionIndex: '0x' + i.toString(16), // TODO: if it's a contract deploy, it should be that address instead address: log.address, @@ -139,7 +140,7 @@ export class LogsManager { if (filterType === 'block') { const blocks = this.oldLogs.filter(x => x.type === 'block').filter(x => tracking.block === undefined || x.blockNumber >= tracking.block) tracking.block = blocks[blocks.length - 1] - return blocks.map(block => ('0x' + block.hash().toString('hex'))) + return blocks.map(block => bytesToHex(block.hash())) } if (filterType === 'pendingTransactions') { return [] @@ -147,13 +148,13 @@ export class LogsManager { } getLogsByTxHash (hash) { - return this.oldLogs.filter((log) => '0x' + log.tx.hash().toString('hex') === hash) + return this.oldLogs.filter((log) => bytesToHex(log.tx.hash()) === hash) .map((log) => { return { logIndex: '0x1', // 1 blockNumber: log.blockNumber, - blockHash: ('0x' + log.block.hash().toString('hex')), - transactionHash: ('0x' + log.tx.hash().toString('hex')), + blockHash: bytesToHex(log.block.hash()), + transactionHash: bytesToHex(log.tx.hash()), transactionIndex: '0x' + log.txNumber.toString(16), // TODO: if it's a contract deploy, it should be that address instead address: log.log.address, @@ -170,8 +171,8 @@ export class LogsManager { results.push({ logIndex: '0x1', // 1 blockNumber: log.blockNumber, - blockHash: ('0x' + log.block.hash().toString('hex')), - transactionHash: ('0x' + log.tx.hash().toString('hex')), + blockHash: bytesToHex(log.block.hash()), + transactionHash: bytesToHex(log.tx.hash()), transactionIndex: '0x' + log.txNumber.toString(16), // TODO: if it's a contract deploy, it should be that address instead address: log.log.address, diff --git a/libs/remix-lib/src/execution/txFormat.ts b/libs/remix-lib/src/execution/txFormat.ts index 5763406c57..16cd34b5b2 100644 --- a/libs/remix-lib/src/execution/txFormat.ts +++ b/libs/remix-lib/src/execution/txFormat.ts @@ -409,7 +409,7 @@ export function decodeResponse (response, fnabi) { const name = fnabi.outputs[i].name json[i] = outputTypes[i] + ': ' + (name ? name + ' ' + decodedObj[i] : decodedObj[i]) } - + return json } catch (e) { return { error: 'Failed to decode output: ' + e } diff --git a/libs/remix-lib/src/execution/txListener.ts b/libs/remix-lib/src/execution/txListener.ts index 32f5427ebe..b0023b35fc 100644 --- a/libs/remix-lib/src/execution/txListener.ts +++ b/libs/remix-lib/src/execution/txListener.ts @@ -1,6 +1,6 @@ 'use strict' import { ethers } from 'ethers' -import { toBuffer, addHexPrefix } from '@ethereumjs/util' +import { toBytes, addHexPrefix } from '@ethereumjs/util' import { EventManager } from '../eventManager' import { compareByteCode, getinputParameters } from '../util' import { decodeResponse } from './txFormat' @@ -64,9 +64,9 @@ export class TxListener { let execResult if (this.executionContext.isVM()) { execResult = await this.executionContext.web3().remix.getExecutionResultFromSimulator(txResult.transactionHash) - returnValue = toBuffer(execResult.returnValue) + returnValue = toBytes(execResult.returnValue) } else { - returnValue = toBuffer(addHexPrefix(txResult.result)) + returnValue = toBytes(addHexPrefix(txResult.result)) } const call = { from: from, @@ -374,7 +374,7 @@ export class TxListener { } _decodeInputParams (data, abi) { - data = toBuffer(addHexPrefix(data)) + data = toBytes(addHexPrefix(data)) if (!data.length) data = new Uint8Array(32 * abi.inputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not enough data const inputTypes = [] diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts index 7604bee0b2..ffdb00e639 100644 --- a/libs/remix-lib/src/execution/txRunnerVM.ts +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -1,9 +1,10 @@ 'use strict' import { RunBlockResult, RunTxResult } from '@ethereumjs/vm' import { ConsensusType } from '@ethereumjs/common' -import { Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx' +import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Block } from '@ethereumjs/block' -import { bufferToHex, Address } from '@ethereumjs/util' +import { bytesToHex, Address, hexToBytes } from '@ethereumjs/util' +import { EVM } from '@ethereumjs/evm' import type { Account } from '@ethereumjs/util' import { EventManager } from '../eventManager' import { LogsManager } from './logsManager' @@ -13,7 +14,7 @@ export type VMexecutionResult = { result: RunTxResult, transactionHash: string block: Block, - tx: Transaction + tx: LegacyTransaction } export type VMExecutionCallBack = (error: string | Error, result?: VMexecutionResult) => void @@ -24,24 +25,23 @@ export class TxRunnerVM { pendingTxs vmaccounts queusTxs - blocks + blocks: Uint8Array[] logsManager commonContext blockParentHash nextNonceForCall: number + standaloneTx: boolean getVMObject: () => any - constructor (vmaccounts, api, getVMObject, blockNumber) { + constructor (vmaccounts, api, getVMObject, blocks: Uint8Array[] = []) { this.event = new EventManager() this.logsManager = new LogsManager() // has a default for now for backwards compatibility this.getVMObject = getVMObject - this.commonContext = this.getVMObject().common - this.blockNumber = blockNumber || 0 + this.commonContext = this.getVMObject().common this.pendingTxs = {} this.vmaccounts = vmaccounts this.queusTxs = [] - this.blocks = [] /* txHash is generated using the nonce, in order to have unique transaction hash, we need to keep using different nonce (in case of a call) @@ -51,7 +51,15 @@ export class TxRunnerVM { this.nextNonceForCall = 0 const vm = this.getVMObject().vm - this.blockParentHash = vm.blockchain.genesisBlock.hash() + if (Array.isArray(blocks) && (blocks || []).length > 0) { + const lastBlock = Block.fromRLPSerializedBlock(blocks[blocks.length - 1], { common: this.commonContext }) + + this.blockParentHash = lastBlock.hash() + this.blocks = blocks + } else { + this.blockParentHash = vm.blockchain.genesisBlock.hash() + this.blocks = [vm.blockchain.genesisBlock.serialize()] + } } execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback: VMExecutionCallBack) { @@ -67,7 +75,7 @@ export class TxRunnerVM { } } - runInVm (from: string, to: string, data: string, value: string, gasLimit: number, useCall: boolean, callback: VMExecutionCallBack) { + async runInVm (from: string, to: string, data: string, value: string, gasLimit: number, useCall: boolean, callback: VMExecutionCallBack) { let account if (!from && useCall && Object.keys(this.vmaccounts).length) { from = Object.keys(this.vmaccounts)[0] @@ -78,76 +86,90 @@ export class TxRunnerVM { return callback('Invalid account selected') } - this.getVMObject().stateManager.getAccount(Address.fromString(from)).then((res: Account) => { + try { + const res = await this.getVMObject().stateManager.getAccount(Address.fromString(from)) const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle. let tx if (!EIP1559) { - tx = Transaction.fromTxData({ + tx = LegacyTransaction.fromTxData({ nonce: useCall ? this.nextNonceForCall : res.nonce, gasPrice: '0x1', gasLimit: gasLimit, to: to, value: value, - data: Buffer.from(data.slice(2), 'hex') + data: hexToBytes(data) }, { common: this.commonContext }).sign(account.privateKey) } else { tx = FeeMarketEIP1559Transaction.fromTxData({ nonce: useCall ? this.nextNonceForCall : res.nonce, maxPriorityFeePerGas: '0x01', - maxFeePerGas: '0x1', + maxFeePerGas: '0x7', gasLimit: gasLimit, to: to, value: value, - data: Buffer.from(data.slice(2), 'hex') + data: hexToBytes(data) }).sign(account.privateKey) } if (useCall) this.nextNonceForCall++ const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] const difficulties = [69762765929000, 70762765929000, 71762765929000] - const difficulty = this.commonContext.consensusType() === ConsensusType.ProofOfStake ? 0 : difficulties[this.blockNumber % difficulties.length] - - const blocknumber = this.blockNumber + 1 + const difficulty = this.commonContext.consensusType() === ConsensusType.ProofOfStake ? 0 : difficulties[this.blocks.length % difficulties.length] const block = Block.fromBlockData({ header: { timestamp: new Date().getTime() / 1000 | 0, - number: blocknumber, - coinbase: coinbases[blocknumber % coinbases.length], + number: this.blocks.length, + coinbase: coinbases[this.blocks.length % coinbases.length], difficulty, gasLimit, baseFeePerGas: EIP1559 ? '0x1' : undefined, parentHash: this.blockParentHash }, transactions: [tx] - }, { common: this.commonContext, hardforkByBlockNumber: false, hardforkByTTD: undefined }) + }, { common: this.commonContext }) - if (!useCall) { - this.blockNumber = this.blockNumber + 1 + if (!this.standaloneTx) { this.blockParentHash = block.hash() - this.runBlockInVm(tx, block, (err, result) => { - if (!err) this.getVMObject().vm.blockchain.putBlock(block) + this.runBlockInVm(tx, block, async (err, result) => { + if (!err) { + if (!useCall) { + this.getVMObject().vm.blockchain.putBlock(block) + this.blocks.push(block.serialize()) + } + } callback(err, result) }) } else { - this.getVMObject().stateManager.checkpoint().then(() => { - this.runBlockInVm(tx, block, (err, result) => { - this.getVMObject().stateManager.revert().then(() => { - callback(err, result) - }) - }) + await this.getVMObject().vm.evm.journal.checkpoint() + this.runTxInVm(tx, block, async (err, result) => { + await this.getVMObject().vm.evm.journal.revert() + callback(err, result) }) } - }).catch((e) => { + } catch (e) { callback(e) + } + } + + runTxInVm (tx, block, callback) { + this.getVMObject().vm.runTx({ tx, skipNonce: true, skipBlockValidation: true, skipBalance: false }).then((result: RunTxResult) => { + callback(null, { + result, + transactionHash: bytesToHex(Buffer.from(tx.hash())), + block, + tx + }) + }).catch(function (err) { + callback(err) }) } runBlockInVm (tx, block, callback) { - this.getVMObject().vm.runBlock({ block: block, generate: true, skipBlockValidation: true, skipBalance: false, skipNonce: true }).then((results: RunBlockResult) => { + this.getVMObject().vm.runBlock({ block: block, generate: true, skipNonce: true, skipBlockValidation: true, skipBalance: false }).then((results: RunBlockResult) => { const result: RunTxResult = results.results[0] callback(null, { result, - transactionHash: bufferToHex(Buffer.from(tx.hash())), + transactionHash: bytesToHex(Buffer.from(tx.hash())), block, tx }) diff --git a/libs/remix-lib/src/execution/typeConversion.ts b/libs/remix-lib/src/execution/typeConversion.ts index c05e9873ec..b960cae173 100644 --- a/libs/remix-lib/src/execution/typeConversion.ts +++ b/libs/remix-lib/src/execution/typeConversion.ts @@ -1,6 +1,6 @@ 'use strict' import { BN } from 'bn.js' -import { bufferToHex } from '@ethereumjs/util' +import { bytesToHex } from '@ethereumjs/util' import { isBigInt } from 'web3-validator' export function toInt (h) { @@ -27,7 +27,7 @@ function convertToString (v) { } else if (v._isBigNumber) { return toInt(v._hex) } else if (v._isBuffer) { - return bufferToHex(v) + return bytesToHex(v) } else if (typeof v === 'object') { const retObject = {} for (const i in v) { diff --git a/libs/remix-lib/src/hash.ts b/libs/remix-lib/src/hash.ts index a12cc75d88..4d543f0e52 100644 --- a/libs/remix-lib/src/hash.ts +++ b/libs/remix-lib/src/hash.ts @@ -1,27 +1,26 @@ import { keccak224, keccak384, keccak256 as k256, keccak512 } from 'ethereum-cryptography/keccak' const createHash = require('create-hash') import { encode, Input } from 'rlp' -import { toBuffer, setLengthLeft, isHexString } from '@ethereumjs/util' +import { toBytes, setLengthLeft, isHexString } from '@ethereumjs/util' /** - * Creates Keccak hash of a Buffer input - * @param a The input data (Buffer) + * Creates Keccak hash of a Uint8Array input + * @param a The input data (Uint8Array) * @param bits (number = 256) The Keccak width */ -export const keccak = function(a: Buffer, bits: number = 256): Buffer { - assertIsBuffer(a) +export const keccak = function(a: Uint8Array, bits: number = 256): Uint8Array { switch (bits) { case 224: { - return toBuffer(keccak224(a)) + return toBytes(keccak224(Buffer.from(a))) } case 256: { - return toBuffer(k256(a)) + return toBytes(k256(Buffer.from(a))) } case 384: { - return toBuffer(keccak384(a)) + return toBytes(keccak384(Buffer.from(a))) } case 512: { - return toBuffer(keccak512(a)) + return toBytes(keccak512(Buffer.from(a))) } default: { throw new Error(`Invalid algorithm: keccak${bits}`) @@ -33,7 +32,7 @@ export const keccak = function(a: Buffer, bits: number = 256): Buffer { * Creates Keccak-256 hash of the input, alias for keccak(a, 256). * @param a The input data (Buffer) */ -export const keccak256 = function(a: Buffer): Buffer { +export const keccak256 = function(a: Buffer): Uint8Array { return keccak(a) } @@ -55,7 +54,7 @@ export const keccakFromString = function(a: string, bits: number = 256) { */ export const keccakFromHexString = function(a: string, bits: number = 256) { assertIsHexString(a) - return keccak(toBuffer(a), bits) + return keccak(Buffer.from(toBytes(a)), bits) } /** @@ -65,7 +64,7 @@ export const keccakFromHexString = function(a: string, bits: number = 256) { */ export const keccakFromArray = function(a: number[], bits: number = 256) { assertIsArray(a) - return keccak(toBuffer(a), bits) + return keccak(Buffer.from(toBytes(a)), bits) } /** @@ -73,7 +72,7 @@ export const keccakFromArray = function(a: number[], bits: number = 256) { * @param a The input data (Buffer|Array|String) */ const _sha256 = function(a: any): Buffer { - a = toBuffer(a) + a = toBytes(a) return createHash('sha256') .update(a) .digest() @@ -112,12 +111,12 @@ export const sha256FromArray = function(a: number[]): Buffer { * @param padded Whether it should be padded to 256 bits or not */ const _ripemd160 = function(a: any, padded: boolean): Buffer { - a = toBuffer(a) + a = toBytes(a) const hash = createHash('rmd160') .update(a) .digest() if (padded === true) { - return setLengthLeft(hash, 32) + return Buffer.from(setLengthLeft(hash, 32)) } else { return hash } @@ -158,7 +157,7 @@ export const ripemd160FromArray = function(a: number[], padded: boolean): Buffer * @param a The input data */ export const rlphash = function(a: Input): Buffer { - return keccak(Buffer.from(encode(a))) + return Buffer.from(keccak(Buffer.from(encode(a)))) } /** diff --git a/libs/remix-lib/src/helpers/txResultHelper.ts b/libs/remix-lib/src/helpers/txResultHelper.ts index a7a89163c6..44ce0e0536 100644 --- a/libs/remix-lib/src/helpers/txResultHelper.ts +++ b/libs/remix-lib/src/helpers/txResultHelper.ts @@ -1,14 +1,34 @@ 'use strict' -import { bufferToHex } from '@ethereumjs/util' +import { bytesToHex } from '@ethereumjs/util' import { isHexString } from 'ethjs-util' +import { BN } from 'bn.js' +import { isBigInt } from 'web3-validator' function convertToPrefixedHex (input) { if (input === undefined || input === null || isHexString(input)) { return input - } else if (Buffer.isBuffer(input)) { - return bufferToHex(input) } - return '0x' + input.toString(16) + if ((input.constructor && input.constructor.name === 'BigNumber') + || BN.isBN(input) + || isBigInt(input) + || typeof input === 'number') { + return '0x' + input.toString(16) + } + + try { + return bytesToHex(input) + } catch (e) { + console.log(e) + } + + try { + // BigNumber + return '0x' + input.toString(16) + } catch (e) { + console.log(e) + } + + return input } /* @@ -34,10 +54,10 @@ export function resultToRemixTx (txResult, execResult?) { return { transactionHash, - status, + status: convertToPrefixedHex(status), gasUsed: convertToPrefixedHex(gasUsed), error: errorMessage, - return: convertToPrefixedHex(returnValue), + return: returnValue ? convertToPrefixedHex(returnValue) : undefined, createdAddress: convertToPrefixedHex(contractAddress) } } diff --git a/libs/remix-lib/src/util.ts b/libs/remix-lib/src/util.ts index 3cefab792e..668d7c60c6 100644 --- a/libs/remix-lib/src/util.ts +++ b/libs/remix-lib/src/util.ts @@ -1,7 +1,9 @@ 'use strict' import { hash } from '@remix-project/remix-lib' -import { bufferToHex, setLengthLeft, toBuffer, addHexPrefix } from '@ethereumjs/util' +import { bytesToHex, setLengthLeft, toBytes, addHexPrefix } from '@ethereumjs/util' import stringSimilarity from 'string-similarity' +import { BN } from 'bn.js' +import { isBigInt } from 'web3-validator' /* contains misc util: @TODO should be split @@ -58,7 +60,7 @@ export function toHexPaddedString(v: bigint | string): string { ints: ints: IntArray */ export function formatMemory (mem) { - const hexMem = bufferToHex(mem).substr(2) + const hexMem = bytesToHex(mem).substr(2) const ret = [] for (let k = 0; k < hexMem.length; k += 32) { const row = hexMem.substr(k, 32) @@ -152,9 +154,15 @@ export function buildCallPath (index, rootCall) { */ // eslint-disable-next-line camelcase export function sha3_256 (value) { - value = toBuffer(addHexPrefix(value)) - const retInBuffer: Buffer = hash.keccak(setLengthLeft(value, 32)) - return bufferToHex(retInBuffer) + if ((value.constructor && value.constructor.name === 'BigNumber') || BN.isBN(value) || isBigInt(value)) { + value = value.toString(16) + } + if (typeof value === 'number') { + value = value.toString(16) + } + value = toBytes(addHexPrefix(value)) + const retInBuffer: Uint8Array = hash.keccak(Buffer.from(setLengthLeft(value, 32))) + return bytesToHex(retInBuffer) } /** diff --git a/libs/remix-lib/test/txResultHelper.ts b/libs/remix-lib/test/txResultHelper.ts index cb972b68b7..c2ab1780ca 100644 --- a/libs/remix-lib/test/txResultHelper.ts +++ b/libs/remix-lib/test/txResultHelper.ts @@ -2,16 +2,16 @@ import tape from 'tape' import { BN } from 'bn.js' -import { toBuffer } from '@ethereumjs/util' +import { toBytes } from '@ethereumjs/util' import { resultToRemixTx } from '../src/helpers/txResultHelper' const TRANSACTION_HASH = '0x538ad944d09c2df403f064c1e4556fae877fe3f1b600c567622e330c2bdbbe2e' const CONTRACT_ADDRESS_HEX = '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a' -const CONTRACT_ADDRESS_BUFFER = toBuffer( +const CONTRACT_ADDRESS_BUFFER = toBytes( [105, 42, 112, 210, 228, 36, 165, 109, 44, 108, 39, 170, 151, 209, 168, 99, 149, 135, 123, 58]) const RETURN_VALUE_HEX = '0x0000000000000000000000000000000000000000000000000000000000000001' -const RETURN_VALUE_BUFFER = toBuffer( +const RETURN_VALUE_BUFFER = toBytes( [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) const STATUS_OK = '0x1' diff --git a/libs/remix-simulator/package.json b/libs/remix-simulator/package.json index 4a2f5299bc..d79b6b1117 100644 --- a/libs/remix-simulator/package.json +++ b/libs/remix-simulator/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-simulator", - "version": "0.2.42", + "version": "0.2.45", "description": "Ethereum IDE and tools for the web", "contributors": [ { @@ -17,12 +17,12 @@ "test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/mocha test/*.ts" }, "dependencies": { - "@ethereumjs/block": "^4.2.0", - "@ethereumjs/common": "^3.1.1", - "@ethereumjs/tx": "^4.1.1", - "@ethereumjs/util": "^8.0.5", - "@ethereumjs/vm": "^6.4.1", - "@remix-project/remix-lib": "^0.5.49", + "@ethereumjs/block": "5.2.0", + "@ethereumjs/common": "4.3.0", + "@ethereumjs/tx": "5.3.0", + "@ethereumjs/util": "9.0.3", + "@ethereumjs/vm": "8.0.0", + "@remix-project/remix-lib": "^0.5.52", "ansi-gray": "^0.1.1", "async": "^3.1.0", "body-parser": "^1.18.2", @@ -70,6 +70,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme", "typings": "src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901", + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-simulator/src/VmProxy.ts b/libs/remix-simulator/src/VmProxy.ts index d0a5e634e2..04ecf1f1f8 100644 --- a/libs/remix-simulator/src/VmProxy.ts +++ b/libs/remix-simulator/src/VmProxy.ts @@ -3,13 +3,13 @@ const { toHexPaddedString, formatMemory } = util import { helpers } from '@remix-project/remix-lib' const { normalizeHexAddress } = helpers.ui import { ConsoleLogs, hash } from '@remix-project/remix-lib' -import { toChecksumAddress, bufferToHex, Address, toBuffer } from '@ethereumjs/util' +import { toChecksumAddress, bytesToHex, Address, toBytes, bigIntToHex} from '@ethereumjs/util' import utils, {toBigInt} from 'web3-utils' import {isBigInt} from 'web3-validator' import { ethers } from 'ethers' import { VMContext } from './vm-context' -import type { StateManager } from '@ethereumjs/statemanager' -import type { InterpreterStep } from '@ethereumjs/evm/dist/interpreter' +import type { EVMStateManagerInterface } from '@ethereumjs/common' +import type { EVMResult, InterpreterStep, Message } from '@ethereumjs/evm' import type { AfterTxEvent, VM } from '@ethereumjs/vm' import type { TypedTransaction } from '@ethereumjs/tx' @@ -43,9 +43,11 @@ export class VmProxy { utils txsMapBlock blocks - stateCopy: StateManager - flagDoNotRecordEVMSteps: boolean + stateCopy: EVMStateManagerInterface + flagrecordVMSteps: boolean lastMemoryUpdate: Array + callIncrement: bigint + txRunning: boolean constructor (vmContext: VMContext) { this.vmContext = vmContext @@ -89,20 +91,26 @@ export class VmProxy { this.txsMapBlock = {} this.blocks = {} this.lastMemoryUpdate = [] + this.flagrecordVMSteps = true + this.callIncrement = BigInt(1) + this.txRunning = false } setVM (vm) { if (this.vm === vm) return this.vm = vm - this.vm.evm.events.on('step', async (data: InterpreterStep) => { + this.vm.evm.events.on('step', async (data: InterpreterStep, resolve: (result?: any) => void) => { await this.pushTrace(data) + resolve() }) this.vm.events.on('afterTx', async (data: AfterTxEvent, resolve: (result?: any) => void) => { await this.txProcessed(data) + this.txRunning = false resolve() }) this.vm.events.on('beforeTx', async (data: TypedTransaction, resolve: (result?: any) => void) => { - await this.txWillProcess(data) + this.txRunning = true + await this.txWillProcess(data) resolve() }) } @@ -113,16 +121,16 @@ export class VmProxy { return ret } - flagNextAsDoNotRecordEvmSteps () { - this.flagDoNotRecordEVMSteps = true + recordVMSteps (record) { + this.flagrecordVMSteps = record } - + async txWillProcess (data: TypedTransaction) { - if (this.flagDoNotRecordEVMSteps) return + if (!this.flagrecordVMSteps) return this.lastMemoryUpdate = [] - this.stateCopy = await this.vm.stateManager.copy() + this.stateCopy = await this.vm.stateManager.shallowCopy() this.incr++ - this.processingHash = bufferToHex(data.hash()) + this.processingHash = bytesToHex(data.hash()) this.vmTraces[this.processingHash] = { gas: '0x0', return: '0x0', @@ -135,7 +143,7 @@ export class VmProxy { tx['to'] = toChecksumAddress(data.to.toString()) } this.processingAddress = tx['to'] - tx['input'] = bufferToHex(data.data) + tx['input'] = bytesToHex(data.data) tx['gas'] = data.gasLimit.toString(10) if (data.value) { tx['value'] = data.value.toString(10) @@ -158,10 +166,7 @@ export class VmProxy { } async txProcessed (data: AfterTxEvent) { - if (this.flagDoNotRecordEVMSteps) { - this.flagDoNotRecordEVMSteps = false - return - } + if (!this.flagrecordVMSteps) return const lastOp = this.vmTraces[this.processingHash].structLogs[this.processingIndex - 1] if (lastOp) { lastOp.error = lastOp.op !== 'RETURN' && lastOp.op !== 'STOP' && lastOp.op !== 'DESTRUCT' @@ -176,14 +181,14 @@ export class VmProxy { if (log[1].length > 0) { for (const k in log[1]) { // @ts-ignore - topics.push('0x' + log[1][k].toString('hex')) + topics.push(bytesToHex(log[1][k])) } } else { topics.push('0x') } logs.push({ - address: toChecksumAddress('0x' + log[0].toString('hex')), - data: '0x' + log[2].toString('hex'), + address: toChecksumAddress(bytesToHex(log[0])), + data: bytesToHex(log[2]), topics: topics, rawVMResponse: log }) @@ -216,18 +221,20 @@ export class VmProxy { this.vmTraces[this.processingHash].return = checksumedAddress this.txsReceipt[this.processingHash].contractAddress = checksumedAddress } else if (data.execResult.returnValue) { - this.vmTraces[this.processingHash].return = '0x' + data.execResult.returnValue.toString('hex') + this.vmTraces[this.processingHash].return = bytesToHex(data.execResult.returnValue) } else { this.vmTraces[this.processingHash].return = '0x' } this.processingIndex = null this.processingAddress = null + this.processingHash = null this.previousDepth = 0 this.stateCopy = null } async pushTrace (data: InterpreterStep) { - if (this.flagDoNotRecordEVMSteps) return + if (!this.flagrecordVMSteps) return + try { const depth = data.depth + 1 // geth starts the depth from 1 if (!this.processingHash) { @@ -308,7 +315,7 @@ export class VmProxy { } } } - if (previousOpcode && previousOpcode.op === 'SHA3') { + if (previousOpcode && (previousOpcode.op === 'SHA3' || previousOpcode.op === 'KECCAK256')) { const preimage = this.getSha3Input(previousOpcode.stack, formatMemory(this.lastMemoryUpdate)) const imageHash = toHexPaddedString(step.stack[step.stack.length - 1]).replace('0x', '') this.sha3Preimages[imageHash] = { @@ -325,7 +332,7 @@ export class VmProxy { getCode (address, cb) { address = toChecksumAddress(address) this.vm.stateManager.getContractCode(Address.fromString(address)).then((result) => { - cb(null, bufferToHex(result)) + cb(null, bytesToHex(result)) }).catch((error) => { cb(error) }) @@ -352,10 +359,10 @@ export class VmProxy { blockNumber = blockNumber === 'latest' ? this.vmContext.latestBlockNumber : blockNumber const block = this.vmContext.blocks[blockNumber] - const txHash = '0x' + block.transactions[block.transactions.length - 1].hash().toString('hex') + const txHash = bytesToHex(block.transactions[block.transactions.length - 1].hash()) if (this.storageCache['after_' + txHash] && this.storageCache['after_' + txHash][address]) { - const slot = '0x' + hash.keccak(toBuffer(ethers.utils.hexZeroPad(position, 32))).toString('hex') + const slot = bytesToHex(hash.keccak(toBytes(ethers.utils.hexZeroPad(position, 32)))) const storage = this.storageCache['after_' + txHash][address] return cb(null, storage[slot].value) } @@ -369,7 +376,7 @@ export class VmProxy { address = toChecksumAddress(address) const block = this.vmContext.blocks[blockNumber] - const txHash = '0x' + block.transactions[txIndex].hash().toString('hex') + const txHash = bytesToHex(block.transactions[txIndex].hash()) if (this.storageCache[txHash] && this.storageCache[txHash][address]) { const storage = this.storageCache[txHash][address] diff --git a/libs/remix-simulator/src/methods/accounts.ts b/libs/remix-simulator/src/methods/accounts.ts index 62a8411a0d..5ac0116555 100644 --- a/libs/remix-simulator/src/methods/accounts.ts +++ b/libs/remix-simulator/src/methods/accounts.ts @@ -1,4 +1,4 @@ -import { privateToAddress, toChecksumAddress, isValidPrivate, Address } from '@ethereumjs/util' +import { privateToAddress, toChecksumAddress, isValidPrivate, Address, toBytes, bytesToHex, Account } from '@ethereumjs/util' import { privateKeyToAccount } from 'web3-eth-accounts' import { toBigInt } from 'web3-utils' import * as crypto from 'crypto' @@ -36,26 +36,27 @@ export class Web3Accounts { await this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000') } - _addAccount (privateKey, balance) { - return new Promise((resolve, reject) => { - privateKey = Buffer.from(privateKey, 'hex') - const address: Buffer = privateToAddress(privateKey) - const addressStr = toChecksumAddress('0x' + address.toString('hex')) + async _addAccount (privateKey, balance) { + try { + privateKey = toBytes('0x' + privateKey) + const address: Uint8Array = privateToAddress(privateKey) + const addressStr = toChecksumAddress(bytesToHex(address)) this.accounts[addressStr] = { privateKey, nonce: 0 } - this.accountsKeys[addressStr] = '0x' + privateKey.toString('hex') + this.accountsKeys[addressStr] = bytesToHex(privateKey) const stateManager = this.vmContext.vm().stateManager - stateManager.getAccount(Address.fromString(addressStr)).then((account) => { + const account = await stateManager.getAccount(Address.fromString(addressStr)) + if (!account) { + const account = new Account(BigInt(0), toBigInt(balance || '0xf00000000000000001')) + await stateManager.putAccount(Address.fromString(addressStr), account) + } else { account.balance = toBigInt(balance || '0xf00000000000000001') - stateManager.putAccount(Address.fromString(addressStr), account).catch((error) => { - reject(error) - }).then(() => { - resolve({}) - }) - }).catch((error) => { - reject(error) - }) - }) + await stateManager.putAccount(Address.fromString(addressStr), account) + } + } catch (e) { + console.error(e) + } + } newAccount (cb) { @@ -64,7 +65,7 @@ export class Web3Accounts { privateKey = crypto.randomBytes(32) } while (!isValidPrivate(privateKey)) this._addAccount(privateKey, '0x56BC75E2D63100000') - return cb(null, '0x' + privateToAddress(privateKey).toString('hex')) + return cb(null, bytesToHex(privateToAddress(privateKey))) } methods (): Record { @@ -82,7 +83,6 @@ export class Web3Accounts { eth_getBalance (payload, cb) { const address = payload.params[0] - this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { cb(null, toBigInt(account.balance).toString(10)) }).catch((error) => { diff --git a/libs/remix-simulator/src/methods/blocks.ts b/libs/remix-simulator/src/methods/blocks.ts index 60b47bab25..0ca0dd768f 100644 --- a/libs/remix-simulator/src/methods/blocks.ts +++ b/libs/remix-simulator/src/methods/blocks.ts @@ -1,6 +1,6 @@ import { toHex } from 'web3-utils' import { VMContext } from '../vm-context' -import { bigIntToHex } from '@ethereumjs/util' +import { bigIntToHex, bytesToHex } from '@ethereumjs/util' export class Blocks { vmContext: VMContext @@ -43,12 +43,12 @@ export class Blocks { } const transactions = block.transactions.map((t) => { - const hash = '0x' + t.hash().toString('hex') + const hash = bytesToHex(t.hash()) const tx = this.vmContext.txByHash[hash] const receipt = this.vmContext.currentVm.web3vm.txsReceipt[hash] if (receipt) { return { - blockHash: '0x' + block.hash().toString('hex'), + blockHash: bytesToHex(block.hash()), blockNumber: bigIntToHex(block.header.number), from: receipt.from, gas: bigIntToHex(receipt.gas), @@ -89,20 +89,20 @@ export class Blocks { toHex (value) { if (!value) return '0x0' - const v = value.toString('hex') - return ((v === '0x' || v === '') ? '0x0' : ('0x' + v)) + const v = bytesToHex(value) + return ((v === '0x' || v === '') ? '0x0' : v) } eth_getBlockByHash (payload, cb) { const block = this.vmContext.blocks[payload.params[0]] const transactions = block.transactions.map((t) => { - const hash = '0x' + t.hash().toString('hex') + const hash = bytesToHex(t.hash()) const tx = this.vmContext.txByHash[hash] const receipt = this.vmContext.currentVm.web3vm.txsReceipt[hash] if (receipt) { return { - blockHash: '0x' + block.hash().toString('hex'), + blockHash: bytesToHex(block.hash()), blockNumber: bigIntToHex(block.header.number), from: receipt.from, gas: toHex(receipt.gas), diff --git a/libs/remix-simulator/src/methods/transactions.ts b/libs/remix-simulator/src/methods/transactions.ts index 6018f9fc99..9ac74d3d79 100644 --- a/libs/remix-simulator/src/methods/transactions.ts +++ b/libs/remix-simulator/src/methods/transactions.ts @@ -1,10 +1,10 @@ import { toHex, toNumber, toBigInt } from 'web3-utils' -import { toChecksumAddress, Address, bigIntToHex } from '@ethereumjs/util' +import { toChecksumAddress, Address, bigIntToHex, bytesToHex} from '@ethereumjs/util' import { processTx } from './txProcess' import { execution } from '@remix-project/remix-lib' import { ethers } from 'ethers' import { VMexecutionResult } from '@remix-project/remix-lib' -import { RunTxResult } from '@ethereumjs/vm' +import { VMContext } from '../vm-context' import { Log, EvmError } from '@ethereumjs/evm' const TxRunnerVM = execution.TxRunnerVM const TxRunner = execution.TxRunner @@ -19,7 +19,7 @@ export type VMExecResult = { } export class Transactions { - vmContext + vmContext: VMContext accounts tags txRunnerVMInstance @@ -32,7 +32,7 @@ export class Transactions { this.tags = {} } - init (accounts, blockNumber) { + init (accounts, blocksData: Buffer[]) { this.accounts = accounts const api = { logMessage: (msg) => { @@ -55,11 +55,11 @@ export class Transactions { } } - this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject(), blockNumber) + this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vmObject(), blocksData) this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, {}) this.txRunnerInstance.vmaccounts = accounts } - + methods () { return { eth_sendTransaction: this.eth_sendTransaction.bind(this), @@ -74,7 +74,9 @@ export class Transactions { eth_getExecutionResultFromSimulator: this.eth_getExecutionResultFromSimulator.bind(this), eth_getHHLogsForTx: this.eth_getHHLogsForTx.bind(this), eth_getHashFromTagBySimulator: this.eth_getHashFromTagBySimulator.bind(this), - eth_registerCallId: this.eth_registerCallId.bind(this) + eth_registerCallId: this.eth_registerCallId.bind(this), + eth_getStateDb: this.eth_getStateDb.bind(this), + eth_getBlocksData: this.eth_getBlocksData.bind(this) } } @@ -86,9 +88,9 @@ export class Transactions { processTx(this.txRunnerInstance, payload, false, (error, result: VMexecutionResult) => { if (!error && result) { this.vmContext.addBlock(result.block) - const hash = '0x' + result.tx.hash().toString('hex') + const hash = bytesToHex(result.tx.hash()) this.vmContext.trackTx(hash, result.block, result.tx) - const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` + const returnValue = `${bytesToHex(result.result.execResult.returnValue) || '0x0'}` const execResult: VMExecResult = { exceptionError: result.result.execResult.exceptionError, executionGasUsed: result.result.execResult.executionGasUsed, @@ -127,7 +129,7 @@ export class Transactions { const r: Record = { transactionHash: receipt.hash, transactionIndex: this.TX_INDEX, - blockHash: '0x' + txBlock.hash().toString('hex'), + blockHash: bytesToHex(txBlock.hash()), blockNumber: bigIntToHex(txBlock.header.number), gasUsed: receipt.gasUsed, cumulativeGasUsed: receipt.gasUsed, // only 1 tx per block @@ -155,13 +157,16 @@ export class Transactions { } payload.params[0].gas = 10000000 * 10 - this.vmContext.web3().flagNextAsDoNotRecordEvmSteps() + this.vmContext.web3().recordVMSteps(false) + this.txRunnerInstance.internalRunner.standaloneTx = true processTx(this.txRunnerInstance, payload, true, (error, value: VMexecutionResult) => { + this.txRunnerInstance.internalRunner.standaloneTx = false + this.vmContext.web3().recordVMSteps(true) if (error) return cb(error) const result: any = value.result if ((result as any).receipt?.status === '0x0' || (result as any).receipt?.status === 0) { try { - const msg = `0x${result.execResult.returnValue.toString('hex') || '0'}` + const msg = `${bytesToHex(result.execResult.returnValue) || '0x00'}` const abiCoder = new ethers.utils.AbiCoder() const reason = abiCoder.decode(['string'], '0x' + msg.slice(10))[0] return cb('revert ' + reason) @@ -198,6 +203,23 @@ export class Transactions { cb() } + eth_getStateDb (_, cb) { + const run = async () => { + if ((this.vmContext.currentVm.stateManager as any)._getCodeDB) { + return cb(null, await (this.vmContext.currentVm.stateManager as any)._getCodeDB()) + } + throw new Error('current state does not support "getStateDetails"') + } + run() + } + + eth_getBlocksData (_, cb) { + cb(null, { + blocks: this.txRunnerVMInstance.blocks, + latestBlockNumber: this.txRunnerVMInstance.blocks.length - 1 + }) + } + eth_call (payload, cb) { // from might be lowercased address (web3) if (payload.params && payload.params.length > 0 && payload.params[0].from) { @@ -212,9 +234,9 @@ export class Transactions { processTx(this.txRunnerInstance, payload, true, (error, result: VMexecutionResult) => { if (!error && result) { this.vmContext.addBlock(result.block, null, true) - const hash = '0x' + result.tx.hash().toString('hex') + const hash = bytesToHex(result.tx.hash()) this.vmContext.trackTx(hash, result.block, result.tx) - const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` + const returnValue = `${bytesToHex(result.result.execResult.returnValue) || '0x0'}` const execResult: VMExecResult = { exceptionError: result.result.execResult.exceptionError, executionGasUsed: result.result.execResult.executionGasUsed, @@ -265,7 +287,7 @@ export class Transactions { // TODO: params to add later const r: Record = { - blockHash: '0x' + txBlock.hash().toString('hex'), + blockHash: bytesToHex(txBlock.hash()), blockNumber: bigIntToHex(txBlock.header.number), from: receipt.from, gas: toHex(BigInt(receipt.gas)), @@ -303,7 +325,7 @@ export class Transactions { const txIndex = payload.params[1] const txBlock = this.vmContext.blocks[payload.params[0]] - const txHash = '0x' + txBlock.transactions[toNumber(txIndex) as number].hash().toString('hex') + const txHash = bytesToHex(txBlock.transactions[toNumber(txIndex) as number].hash()) this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { if (error) { @@ -314,7 +336,7 @@ export class Transactions { // TODO: params to add later const r: Record = { - blockHash: '0x' + txBlock.hash().toString('hex'), + blockHash: bytesToHex(txBlock.hash()), blockNumber: bigIntToHex(txBlock.header.number), from: receipt.from, gas: toHex(BigInt(receipt.gas)), @@ -348,7 +370,7 @@ export class Transactions { const txIndex = payload.params[1] const txBlock = this.vmContext.blocks[payload.params[0]] - const txHash = '0x' + txBlock.transactions[toNumber(txIndex) as number].hash().toString('hex') + const txHash = bytesToHex(txBlock.transactions[toNumber(txIndex) as number].hash()) this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { if (error) { @@ -359,7 +381,7 @@ export class Transactions { // TODO: params to add later const r: Record = { - blockHash: '0x' + txBlock.hash().toString('hex'), + blockHash: bytesToHex(txBlock.hash()), blockNumber: bigIntToHex(txBlock.header.number), from: receipt.from, gas: toHex(BigInt(receipt.gas)), diff --git a/libs/remix-simulator/src/provider.ts b/libs/remix-simulator/src/provider.ts index 59066c0ab6..95b7cf6632 100644 --- a/libs/remix-simulator/src/provider.ts +++ b/libs/remix-simulator/src/provider.ts @@ -11,6 +11,7 @@ import { Transactions } from './methods/transactions' import { Debug } from './methods/debug' import { VMContext } from './vm-context' import { Web3PluginBase } from 'web3' +import { Block } from '@ethereumjs/block' export interface JSONRPCRequestPayload { params: any[]; @@ -27,8 +28,20 @@ export interface JSONRPCResponsePayload { export type JSONRPCResponseCallback = (err: Error, result?: JSONRPCResponsePayload) => void +export type State = Record + +export type ProviderOptions = { + fork?: string, + nodeUrl?: string, + blockNumber?: number | 'latest', + stateDb?: State, + logDetails?: boolean + blocks?: string[], + coinbase?: string +} + export class Provider { - options: Record + options: ProviderOptions vmContext Accounts Transactions @@ -37,10 +50,10 @@ export class Provider { initialized: boolean pendingRequests: Array - constructor (options: Record = {}) { + constructor (options: ProviderOptions = {} as ProviderOptions) { this.options = options this.connected = true - this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as (number | 'latest')) + this.vmContext = new VMContext(options['fork'], options['nodeUrl'], options['blockNumber'], options['stateDb'], options['blocks']) this.Accounts = new Web3Accounts(this.vmContext) this.Transactions = new Transactions(this.vmContext) @@ -60,7 +73,7 @@ export class Provider { this.pendingRequests = [] await this.vmContext.init() await this.Accounts.resetAccounts() - this.Transactions.init(this.Accounts.accounts, this.vmContext.blockNumber) + this.Transactions.init(this.Accounts.accounts, this.vmContext.serializedBlocks) this.initialized = true if (this.pendingRequests.length > 0) { this.pendingRequests.map((req) => { @@ -168,4 +181,18 @@ class Web3TestPlugin extends Web3PluginBase { params: [id] }) } + + public getStateDb() { + return this.requestManager.send({ + method: 'eth_getStateDb', + params: [] + }) + } + + public getBlocksData() { + return this.requestManager.send({ + method: 'eth_getBlocksData', + params: [] + }) + } } diff --git a/libs/remix-simulator/src/vm-context.ts b/libs/remix-simulator/src/vm-context.ts index 0bf58c3ad8..0b85f4cf60 100644 --- a/libs/remix-simulator/src/vm-context.ts +++ b/libs/remix-simulator/src/vm-context.ts @@ -1,27 +1,25 @@ /* global ethereum */ 'use strict' -import { Cache } from '@ethereumjs/statemanager/dist/cache' import { hash } from '@remix-project/remix-lib' -import { bufferToHex, Account, toBuffer, bufferToBigInt} from '@ethereumjs/util' +import { bytesToHex, Account, bigIntToHex, MapDB, toBytes, bytesToBigInt } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' -import type { Address } from '@ethereumjs/util' +import { Address } from '@ethereumjs/util' import { decode } from 'rlp' import { ethers } from 'ethers' import { execution } from '@remix-project/remix-lib' const { LogsManager } = execution import { VmProxy } from './VmProxy' import { VM } from '@ethereumjs/vm' -import type { BigIntLike } from '@ethereumjs/util' import { Common, ConsensusType } from '@ethereumjs/common' import { Trie } from '@ethereumjs/trie' -import { DefaultStateManager, StateManager, EthersStateManager, EthersStateManagerOpts } from '@ethereumjs/statemanager' -import { StorageDump } from '@ethereumjs/statemanager/dist/interface' +import { DefaultStateManager } from '@ethereumjs/statemanager' +import { EVMStateManagerInterface, StorageDump } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' -import { EEI } from '@ethereumjs/vm' import { Blockchain } from '@ethereumjs/blockchain' import { Block } from '@ethereumjs/block' -import { Transaction } from '@ethereumjs/tx' -import { bigIntToHex } from '@ethereumjs/util' +import { TypedTransaction } from '@ethereumjs/tx' +import { State } from './provider' +import { hexToBytes } from 'web3-utils' /** * Options for constructing a {@link StateManager}. @@ -50,40 +48,52 @@ class StateManagerCommonStorageDump extends DefaultStateManager { this.keyHashes = {} } + getDb () { + // @ts-ignore + return this._trie.database().db + } + putContractStorage (address, key, value) { - this.keyHashes[hash.keccak(key).toString('hex')] = bufferToHex(key) + this.keyHashes[bytesToHex(hash.keccak(key))] = bytesToHex(key) return super.putContractStorage(address, key, value) } - copy(): StateManagerCommonStorageDump { + shallowCopy(): StateManagerCommonStorageDump { const copyState = new StateManagerCommonStorageDump({ - trie: this._trie.copy(false), + trie: this._trie.shallowCopy(false), }) copyState.keyHashes = this.keyHashes return copyState } async dumpStorage (address): Promise { + await this.flush() + const account = await this.getAccount(address) + if (!account) { + throw new Error(`dumpStorage f() can only be called for an existing account`) + } return new Promise((resolve, reject) => { - this._getStorageTrie(address) - .then((trie) => { - const storage = {} - const stream = trie.createReadStream() - - stream.on('data', (val) => { - const value: any = decode(val.value) - storage['0x' + val.key.toString('hex')] = { - key: this.keyHashes[val.key.toString('hex')], - value: '0x' + value.toString('hex') - } - }) - stream.on('end', () => { - resolve(storage) - }) + try { + const trie = this._getStorageTrie(address, account) + const storage = {} + const stream = trie.createReadStream() + + stream.on('data', (val) => { + const value: any = decode(val.value) + storage[bytesToHex(val.key)] = { + key: this.keyHashes[bytesToHex(val.key)], + value: bytesToHex(value) + } }) - .catch((e) => { + stream.on('end', () => { + resolve(storage) + }) + stream.on('error', (e) => { reject(e) }) + } catch (e) { + reject(e) + } }) } } @@ -100,7 +110,7 @@ export interface CustomEthersStateManagerOpts { class CustomEthersStateManager extends StateManagerCommonStorageDump { private provider: ethers.providers.StaticJsonRpcProvider | ethers.providers.JsonRpcProvider private blockTag: string - + constructor(opts: CustomEthersStateManagerOpts) { super(opts) if (typeof opts.provider === 'string') { @@ -112,32 +122,6 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { } this.blockTag = opts.blockTag - - /* - * For a custom StateManager implementation adopt these - * callbacks passed to the `Cache` instantiated to perform - * the `get`, `put` and `delete` operations with the - * desired backend. - */ - const getCb = async (address) => { - const rlp = await this._trie.get(address.buf) - if (rlp) { - const ac = Account.fromRlpSerializedAccount(rlp) - return ac - } else { - const ac = await this.getAccountFromProvider(address) - return ac - } - } - const putCb = async (keyBuf, accountRlp) => { - const trie = this._trie - await trie.put(keyBuf, accountRlp) - } - const deleteCb = async (keyBuf: Buffer) => { - const trie = this._trie - await trie.del(keyBuf) - } - this._cache = new Cache({ getCb, putCb, deleteCb }) } /** @@ -149,11 +133,11 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { this.blockTag = blockTag === 'earliest' ? blockTag : bigIntToHex(blockTag) } - copy(): CustomEthersStateManager { + shallowCopy(): CustomEthersStateManager { const newState = new CustomEthersStateManager({ provider: this.provider, blockTag: this.blockTag, - trie: this._trie.copy(false), + trie: this._trie.shallowCopy(false), }) return newState } @@ -164,11 +148,11 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { * @returns {Promise} - Resolves with the code corresponding to the provided address. * Returns an empty `Buffer` if the account has no associated code. */ - async getContractCode(address: Address): Promise { + async getContractCode(address: Address): Promise { const code = await super.getContractCode(address) if (code && code.length > 0) return code else { - const code = toBuffer(await this.provider.getCode(address.toString(), this.blockTag)) + const code = toBytes(await this.provider.getCode(address.toString(), this.blockTag)) await super.putContractCode(address, code) return code } @@ -183,13 +167,13 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { * corresponding to the provided address at the provided key. * If this does not exist an empty `Buffer` is returned. */ - async getContractStorage(address: Address, key: Buffer): Promise { + async getContractStorage(address: Address, key: Buffer): Promise { let storage = await super.getContractStorage(address, key) if (storage && storage.length > 0) return storage else { - storage = toBuffer(await this.provider.getStorageAt( + storage = toBytes(await this.provider.getStorageAt( address.toString(), - bufferToBigInt(key), + bytesToBigInt(key), this.blockTag) ) await super.putContractStorage(address, key, storage) @@ -202,19 +186,30 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { * @param address - Address of the `account` to check */ async accountExists(address: Address): Promise { - const localAccount = this._cache.get(address) - if (!localAccount.isEmpty()) return true + const account = await super.getAccount(address) + if (!account.isEmpty()) return true + // Get merkle proof for `address` from provider const proof = await this.provider.send('eth_getProof', [address.toString(), [], this.blockTag]) - const proofBuf = proof.accountProof.map((proofNode: string) => toBuffer(proofNode)) + const proofBuf = proof.accountProof.map((proofNode: string) => toBytes(proofNode)) const trie = new Trie({ useKeyHashing: true }) const verified = await trie.verifyProof( Buffer.from(keccak256(proofBuf[0])), - address.buf, + address.bytes, proofBuf ) + if (verified) { + const codeHash = proof.codeHash === '0x0000000000000000000000000000000000000000000000000000000000000000' ? '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' : proof.codeHash + const account = Account.fromAccountData({ + balance: BigInt(proof.balance), + nonce: BigInt(proof.nonce), + codeHash: hexToBytes(codeHash) + // storageRoot: toBuffer([]), // we have to remove this in order to force the creation of the Trie in the local state. + }) + super.putAccount(address, account) + } // if not verified (i.e. verifyProof returns null), account does not exist return verified === null ? false : true } @@ -240,14 +235,14 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { account = Account.fromAccountData({ balance: BigInt(0), nonce: BigInt(0), - codeHash: toBuffer('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') + codeHash: hexToBytes('0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') }) } else { const codeHash = accountData.codeHash === '0x0000000000000000000000000000000000000000000000000000000000000000' ? '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' : accountData.codeHash account = Account.fromAccountData({ balance: BigInt(accountData.balance), nonce: BigInt(accountData.nonce), - codeHash: toBuffer(codeHash) + codeHash: hexToBytes(codeHash) // storageRoot: toBuffer([]), // we have to remove this in order to force the creation of the Trie in the local state. }) } @@ -255,29 +250,21 @@ class CustomEthersStateManager extends StateManagerCommonStorageDump { } } + export type CurrentVm = { vm: VM, web3vm: VmProxy, - stateManager: StateManager, + stateManager: EVMStateManagerInterface, common: Common } export class VMCommon extends Common { /** - * Override "setHardforkByBlockNumber" to disable updating the original fork state - * - * @param blockNumber - * @param td - * @param timestamp - * @returns The name of the HF set - */ - setHardforkByBlockNumber( - blockNumber: BigIntLike, - td?: BigIntLike, - timestamp?: BigIntLike - ): string { - return this.hardfork() + * Always return the fork set at initialization + */ + setHardforkBy() { + return this._hardfork; } } @@ -291,19 +278,23 @@ export class VMContext { blocks: Record latestBlockNumber: string blockByTxHash: Record - txByHash: Record + txByHash: Record currentVm: CurrentVm web3vm: VmProxy logsManager: any // LogsManager - exeResults: Record + exeResults: Record nodeUrl: string blockNumber: number | 'latest' + stateDb: State + rawBlocks: string[] + serializedBlocks: Uint8Array[] - constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest') { + constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest', stateDb?: State, blocksData?: string[]) { this.blockGasLimitDefault = 4300000 this.blockGasLimit = this.blockGasLimitDefault - this.currentFork = fork || 'merge' + this.currentFork = fork || 'cancun' this.nodeUrl = nodeUrl + this.stateDb = stateDb this.blockNumber = blockNumber this.blocks = {} this.latestBlockNumber = "0x0" @@ -311,6 +302,8 @@ export class VMContext { this.txByHash = {} this.exeResults = {} this.logsManager = new LogsManager() + this.rawBlocks = blocksData + this.serializedBlocks = [] } async init () { @@ -318,7 +311,7 @@ export class VMContext { } async createVm (hardfork) { - let stateManager: StateManager + let stateManager: EVMStateManagerInterface if (this.nodeUrl) { let block = this.blockNumber if (this.blockNumber === 'latest') { @@ -332,18 +325,27 @@ export class VMContext { } else { stateManager = new CustomEthersStateManager({ provider: this.nodeUrl, - blockTag: '0x' + this.blockNumber.toString(16) + blockTag: '0x' + block.toString(16) }) } + } else { + const db = this.stateDb ? new Map(Object.entries(this.stateDb).map(([k, v]) => [k, hexToBytes(v)])) : new Map() + const mapDb = new MapDB(db) + const trie = await Trie.create({ useKeyHashing: true, db: mapDb, useRootPersistence: true }) - } else - stateManager = new StateManagerCommonStorageDump() + stateManager = new StateManagerCommonStorageDump({ trie }) + } const consensusType = hardfork === 'berlin' || hardfork === 'london' ? ConsensusType.ProofOfWork : ConsensusType.ProofOfStake const difficulty = consensusType === ConsensusType.ProofOfStake ? 0 : 69762765929000 const common = new VMCommon({ chain: 'mainnet', hardfork }) - const genesisBlock: Block = Block.fromBlockData({ + const blocks = (this.rawBlocks || []).map(block => { + const serializedBlock = hexToBytes(block) + this.serializedBlocks.push(serializedBlock) + return Block.fromRLPSerializedBlock(serializedBlock, { common }) + }) + const genesisBlock: Block = blocks.length > 0 && (blocks[0] || {}).isGenesis ? blocks[0] : Block.fromBlockData({ header: { timestamp: (new Date().getTime() / 1000 | 0), number: 0, @@ -351,27 +353,29 @@ export class VMContext { difficulty, gasLimit: 8000000 } - }, { common, hardforkByBlockNumber: false, hardforkByTTD: undefined }) + }, { common }) const blockchain = await Blockchain.create({ common, validateBlocks: false, validateConsensus: false, genesisBlock }) - const eei = new EEI(stateManager, common, blockchain) - const evm = new EVM({ common, eei, allowUnlimitedContractSize: true }) - + const evm = await EVM.create({ common, allowUnlimitedContractSize: true, stateManager, blockchain }) + const vm = await VM.create({ common, activatePrecompiles: true, - hardforkByBlockNumber: false, stateManager, blockchain, evm }) - // VmProxy and VMContext are very intricated. // VmProxy is used to track the EVM execution (to listen on opcode execution, in order for instance to generate the VM trace) const web3vm = new VmProxy(this) web3vm.setVM(vm) this.addBlock(genesisBlock, true) - return { vm, web3vm, stateManager, common } + if (blocks.length > 0) blocks.splice(0, 1) + for (const block of blocks) { + await blockchain.putBlock(block) + this.addBlock(block, false, false, web3vm) + } + return { vm, web3vm, stateManager, common, blocks } } getCurrentFork () { @@ -390,17 +394,18 @@ export class VMContext { return this.currentVm } - addBlock (block: Block, genesis?: boolean, isCall?: boolean) { + addBlock (block: Block, genesis?: boolean, isCall?: boolean, web3vm?: VmProxy) { let blockNumber = bigIntToHex(block.header.number) if (blockNumber === '0x') { blockNumber = '0x0' } - this.blocks['0x' + block.hash().toString('hex')] = block + this.blocks[bytesToHex(block.hash())] = block this.blocks[blockNumber] = block this.latestBlockNumber = blockNumber - if (!isCall && !genesis) this.logsManager.checkBlock(blockNumber, block, this.web3()) + if (!isCall && !genesis && web3vm) this.logsManager.checkBlock(blockNumber, block, web3vm) + if (!isCall && !genesis && !web3vm) this.logsManager.checkBlock(blockNumber, block, this.web3()) } trackTx (txHash, block, tx) { diff --git a/libs/remix-simulator/test/events.ts b/libs/remix-simulator/test/events.ts index 98133679a4..de8bb9b02a 100644 --- a/libs/remix-simulator/test/events.ts +++ b/libs/remix-simulator/test/events.ts @@ -19,20 +19,20 @@ describe('Events', () => { from: accounts[0], gas: 1000000, data: '0x608060405234801561001057600080fd5b506101ea806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80632801617e14610030575b600080fd5b61004a6004803603810190610045919061015d565b61004c565b005b8073ffffffffffffffffffffffffffffffffffffffff1663a6f9dae1306040518263ffffffff1660e01b81526004016100859190610199565b600060405180830381600087803b15801561009f57600080fd5b505af11580156100b3573d6000803e3d6000fd5b50505050607b7fdcd9c7fa0342f01013bd0bf2bec103a81936162dcebd1f0c38b1d4164c17e0fc60405160405180910390a250565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610118826100ed565b9050919050565b600061012a8261010d565b9050919050565b61013a8161011f565b811461014557600080fd5b50565b60008135905061015781610131565b92915050565b600060208284031215610173576101726100e8565b5b600061018184828501610148565b91505092915050565b6101938161010d565b82525050565b60006020820190506101ae600083018461018a565b9291505056fea2646970667358221220cf5368dd204d44a75752e8ba7512b73d2f54b09f6ca6147e376dd3cf2942b96464736f6c63430008120033' - }) + }, null, { checkRevertBeforeSending: false, ignoreGasPricing: true }) // deploy the contract "owner", this will trigger an event. const receiptOwner = await web3.eth.sendTransaction({ from: accounts[0], gas: 1000000, data: '0x608060405234801561001057600080fd5b5061005a6040518060400160405280601b81526020017f4f776e657220636f6e7472616374206465706c6f7965642062793a00000000008152503361011a60201b61015b1760201c565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a361034d565b6101b882826040516024016101309291906102ee565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506101bc60201b60201c565b5050565b6101dd816101d86101e060201b6101f71761020160201b60201c565b60201c565b50565b60006a636f6e736f6c652e6c6f679050600080835160208501845afa505050565b61021360201b61023d17819050919050565b61021b61031e565b565b600081519050919050565b600082825260208201905092915050565b60005b8381101561025757808201518184015260208101905061023c565b60008484015250505050565b6000601f19601f8301169050919050565b600061027f8261021d565b6102898185610228565b9350610299818560208601610239565b6102a281610263565b840191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d8826102ad565b9050919050565b6102e8816102cd565b82525050565b600060408201905081810360008301526103088185610274565b905061031760208301846102df565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052605160045260246000fd5b6104268061035c6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b6040516100509190610288565b60405180910390f35b610073600480360381019061006e91906102d4565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6101f38282604051602401610171929190610391565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050610218565b5050565b60006a636f6e736f6c652e6c6f679050600080835160208501845afa505050565b61022f816102276101f7610232565b63ffffffff16565b50565b61023d819050919050565b6102456103c1565b565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061027282610247565b9050919050565b61028281610267565b82525050565b600060208201905061029d6000830184610279565b92915050565b600080fd5b6102b181610267565b81146102bc57600080fd5b50565b6000813590506102ce816102a8565b92915050565b6000602082840312156102ea576102e96102a3565b5b60006102f8848285016102bf565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561033b578082015181840152602081019050610320565b60008484015250505050565b6000601f19601f8301169050919050565b600061036382610301565b61036d818561030c565b935061037d81856020860161031d565b61038681610347565b840191505092915050565b600060408201905081810360008301526103ab8185610358565b90506103ba6020830184610279565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052605160045260246000fdfea2646970667358221220cb7beb012e0831cc632ed85a11a8652f72efc03360c81beb1fcd842a7782c3cb64736f6c63430008120033' - }) + }, null, { checkRevertBeforeSending: false, ignoreGasPricing: true }) // call function set(Owner p) from "test", this will trigger 2 events, one from each contract. await web3.eth.sendTransaction({ from: accounts[0], to: receiptTest.contractAddress, gas: 1000000, data: '0x2801617e' + web3.utils.padLeft(receiptOwner.contractAddress, 64).replace('0x', '') - }) + }, null, { checkRevertBeforeSending: false, ignoreGasPricing: true }) const testLogs = await web3.eth.getPastLogs({ address: receiptTest.contractAddress, @@ -47,9 +47,10 @@ describe('Events', () => { toBlock: 'latest', topics: ['0x342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a735', '0xdcd9c7fa0342f01013bd0bf2bec103a81936162dcebd1f0c38b1d4164c17e0fc'] }) + // this should include the event triggered by the "set" transaction call. - assert.equal(testLogs.length, 1) - assert.equal(ownerLogs.length, 1) + assert.equal(testLogs.length, 1, '1) testLogs length should be equal to 1') + assert.equal(ownerLogs.length, 1, '2) ownerLogs length should be equal to 1') ownerLogs = await web3.eth.getPastLogs({ address: receiptOwner.contractAddress, @@ -58,7 +59,7 @@ describe('Events', () => { topics: ['0x342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a735', '0xdcd9c7fa0342f01013bd0bf2bec103a81936162dcebd1f0c38b1d4164c17e0fc'] }) // this should include the event triggered from the ctor. - assert.equal(ownerLogs.length, 2) + assert.equal(ownerLogs.length, 2, '3) ownerLogs length should be equal to 2') ownerLogs = await web3.eth.getPastLogs({ address: receiptOwner.contractAddress, @@ -67,7 +68,7 @@ describe('Events', () => { topics: ['0x342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a735', '0xdcd9c7fa0342f01013bd0bf2bec103a81936162dcebd1f0c38b1d4164c17e0fc'] }) // this should only include the event triggered from the ctor. - assert.equal(ownerLogs.length, 1) + assert.equal(ownerLogs.length, 1, '4) ownerLogs length should be equal to 1') }) }) }) diff --git a/libs/remix-solidity/package.json b/libs/remix-solidity/package.json index e2192d252a..828e20a2bf 100644 --- a/libs/remix-solidity/package.json +++ b/libs/remix-solidity/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-solidity", - "version": "0.5.28", + "version": "0.5.31", "description": "Tool to load and run Solidity compiler", "main": "src/index.js", "types": "src/index.d.ts", @@ -15,11 +15,11 @@ } ], "dependencies": { - "@ethereumjs/block": "^4.2.0", - "@ethereumjs/tx": "^4.1.1", - "@ethereumjs/util": "^8.0.5", - "@ethereumjs/vm": "^6.4.1", - "@remix-project/remix-lib": "^0.5.49", + "@ethereumjs/block": "5.2.0", + "@ethereumjs/tx": "5.3.0", + "@ethereumjs/util": "9.0.3", + "@ethereumjs/vm": "8.0.0", + "@remix-project/remix-lib": "^0.5.52", "async": "^2.6.2", "eslint-scope": "^5.0.0", "ethers": "^5.4.2", @@ -57,5 +57,5 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme", "typings": "src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901" + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c" } \ No newline at end of file diff --git a/libs/remix-solidity/src/compiler/types.ts b/libs/remix-solidity/src/compiler/types.ts index 89058af0cc..8754b8c63d 100644 --- a/libs/remix-solidity/src/compiler/types.ts +++ b/libs/remix-solidity/src/compiler/types.ts @@ -72,7 +72,7 @@ export interface CompilerInput { // Addresses of the libraries. If not all libraries are given here, // it can result in unlinked objects whose output data is different. libraries?: { - // The top level key is the the name of the source file where the library is used. + // The top level key is the name of the source file where the library is used. // If remappings are used, this source file should match the global path // after remappings were applied. // If this key is an empty string, that refers to a global level. diff --git a/libs/remix-tests/package.json b/libs/remix-tests/package.json index 98d1e1c9e8..c62a13a2fc 100644 --- a/libs/remix-tests/package.json +++ b/libs/remix-tests/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-tests", - "version": "0.2.42", + "version": "0.2.45", "description": "Tool to test Solidity smart contracts", "main": "src/index.js", "types": "./src/index.d.ts", @@ -36,14 +36,14 @@ "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme", "dependencies": { "@erebos/bzz-node": "^0.13.0", - "@ethereumjs/block": "^4.2.0", - "@ethereumjs/common": "^3.1.1", - "@ethereumjs/tx": "^4.1.1", - "@ethereumjs/util": "^8.0.5", - "@ethereumjs/vm": "^6.4.1", - "@remix-project/remix-lib": "^0.5.49", - "@remix-project/remix-simulator": "^0.2.42", - "@remix-project/remix-solidity": "^0.5.28", + "@ethereumjs/block": "5.2.0", + "@ethereumjs/common": "4.3.0", + "@ethereumjs/tx": "5.3.0", + "@ethereumjs/util": "9.0.3", + "@ethereumjs/vm": "8.0.0", + "@remix-project/remix-lib": "^0.5.52", + "@remix-project/remix-simulator": "^0.2.45", + "@remix-project/remix-solidity": "^0.5.31", "@remix-project/remix-url-resolver": "^0.0.42", "ansi-gray": "^0.1.1", "async": "^2.6.0", @@ -77,6 +77,17 @@ "ts-node": "^8.0.2", "typescript": "^3.3.1" }, + "resolutions": { + "@ethereumjs/blockchain": "7.2.0", + "@ethereumjs/block": "5.2.0", + "@ethereumjs/common": "4.3.0", + "@ethereumjs/evm": "3.0.0", + "@ethereumjs/statemanager": "2.3.0", + "@ethereumjs/tx": "5.3.0", + "@ethereumjs/util": "9.0.3", + "@ethereumjs/vm": "8.0.0", + "@ethereumjs/trie": "6.2.0" + }, "typings": "src/index.d.ts", - "gitHead": "817089ab1f206f5e195756a4051afb812ec26901" + "gitHead": "aebbd10b67952c3aa0cbf7e82944cd91fcd6f48c" } \ No newline at end of file diff --git a/libs/remix-tests/tests/testRunner.cli.spec.ts b/libs/remix-tests/tests/testRunner.cli.spec.ts index f9686ca073..363e69cb8d 100644 --- a/libs/remix-tests/tests/testRunner.cli.spec.ts +++ b/libs/remix-tests/tests/testRunner.cli.spec.ts @@ -26,11 +26,12 @@ describe('testRunner: remix-tests CLI', function(){ it('remix-tests version', () => { const res = spawnSync(executablePath, ['-V']) // eslint-disable-next-line @typescript-eslint/no-var-requires - expect(res.stdout.toString().trim()).to.equal(require('../package.json').version) + expect(res.stdout.toString().trim()).to.equal(require('../package.json').version, `actual value: ${res.stdout.toString()}`) }) it('remix-tests help', () => { const res = spawnSync(executablePath, ['-h']) + console.log(res.stdout.toString()) const expectedHelp = `Usage: remix-tests [options] [command] Arguments: @@ -62,6 +63,7 @@ Commands: const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_test.sol')]) //console.log(res.stdout.toString()) // match initial lines + console.log(res.stdout.toString()) expect(res.stdout.toString().trim()).to.match(/:: Running tests using remix-tests ::/) expect(res.stdout.toString().trim()).to.match(/creation of library remix_tests.sol:Assert pending.../) // match test result diff --git a/libs/remix-tests/tests/testRunner.spec.ts b/libs/remix-tests/tests/testRunner.spec.ts index c0f8951ae8..799bdfb3f9 100644 --- a/libs/remix-tests/tests/testRunner.spec.ts +++ b/libs/remix-tests/tests/testRunner.spec.ts @@ -161,18 +161,18 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertEqualTest', filename: __dirname + '/examples_0/assert_equal_test.sol' }, - { type: 'testPass', debugTxHash: '0x233b8d91f0fa068b1a4deae1141178bc3eb79c3d2a6786160595a358363a157c', value: 'Equal uint pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0xa5e39c78663c2e5071c08467047ba5b2650d16081b50369700d46d7f90c4d94b', value: 'Equal uint fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalUintFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '273:57:0', expected: '2', returned: '1' }, - { type: 'testPass', debugTxHash: '0x57af51c2c19db390a4ccf72fa3d32347fb3d998e70820909c7876bd8ccebf8a3', value: 'Equal int pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x710f3a54a561c009fcf0277273b8fe337b2c493e9e83e0ae02786d487339ca7b', value: 'Equal int fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalIntFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '493:45:0', expected: '2', returned: '-1' }, - { type: 'testPass', debugTxHash: '0x10c1ed8651110ad5de6adcad8e1284aa5c1fd3a998a1e863bbecc0ec855fcd7b', value: 'Equal bool pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x004871a82968f43e02278eab9dd3d7eb0bbe88b64d459efa50065e5996fe5fad', value: 'Equal bool fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBoolFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '708:52:0', expected: false, returned: true }, - { type: 'testPass', debugTxHash: '0x64a4d4853ab7907712912cf2120ac2bfd2e08b4767b375250f0e907757546454', value: 'Equal address pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0xcf62fb76e3b2eb95d92aa2671a9e81e30fefb944f55e2fb8b97096c45fc74a38', value: 'Equal address fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalAddressFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1015:130:0', expected: '0x1c6637567229159d1eFD45f95A6675e77727E013', returned: '0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9' }, - { type: 'testPass', debugTxHash: '0x18ef613acc128a21282e09cf920b32ef3be648bb35c0299471ddbbbeeb0faf8c', value: 'Equal bytes32 pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x86fbf2f14e13d228f80a87a947841270d8c55073adddf78e8d4e2ba05d724ec6', value: 'Equal bytes32 fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBytes32FailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1670:48:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6979000000000000000000000000000000000000000000000000000000' }, - { type: 'testPass', debugTxHash: '0x80b3465f2504b74359790baa009237ba066685b24afa65a31814f1ad1bc4f99f', value: 'Equal string pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, - { type: 'testFailure', debugTxHash: '0x88b035a85c5f87f54a805334817f3e4599b4190d98f25947fe14d7804facd8b7', value: 'Equal string fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalStringFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1916:81:0', expected: 'remix-tests', returned: 'remix' } + { type: 'testPass', debugTxHash: '0x15b52a9081653c9d6d910378c30386f36c5740ffd25ad62e0c92c08e654560f5', value: 'Equal uint pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0xaaf89bcdad7a5cf283c122ed5e3d18fae5da45c2244ee225b3758822f89f3286', value: 'Equal uint fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalUintFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '273:57:0', expected: '2', returned: '1' }, + { type: 'testPass', debugTxHash: '0xffd078476f9d569cdc81ac9657d0cba539f2242bc55ea8e00c9a5f79202962bd', value: 'Equal int pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0xe5825fe29fc517cd5838bcdcd8d4b265ec7ea1e26f1436a2449358b7c4e6265f', value: 'Equal int fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalIntFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '493:45:0', expected: '2', returned: '-1' }, + { type: 'testPass', debugTxHash: '0xdf0c7136558a8575ffbacde38f66a510524871c1159983fb66971825f0ccf01c', value: 'Equal bool pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0xc4d5cb36e53aac58193d7811388f572eeedf67def1e73f7b99c30ec40a93f500', value: 'Equal bool fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBoolFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '708:52:0', expected: false, returned: true }, + { type: 'testPass', debugTxHash: '0xfe0f0c759c5dd07135dfbc249d4e963cc442e454bb46d760623642a2b7eb6dbe', value: 'Equal address pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0xf482704ba6fb1e3a21f014e085bd6974efea4be7a57a0d7998c2809c5aee65c4', value: 'Equal address fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalAddressFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1015:130:0', expected: '0x1c6637567229159d1eFD45f95A6675e77727E013', returned: '0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9' }, + { type: 'testPass', debugTxHash: '0x615af0a14d8e605944cdd8f938523e093c2d937d4715aee373650028c905385e', value: 'Equal bytes32 pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0x20ff637a0fb28c9f943d0eaa23d9391e9177810d77a9ac5478873f8838719fc5', value: 'Equal bytes32 fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBytes32FailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1670:48:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6979000000000000000000000000000000000000000000000000000000' }, + { type: 'testPass', debugTxHash: '0x2a7c3f1f5d87620d8f1f2a83984e2cae6ff985f25f9cb96a046b507b357941bb', value: 'Equal string pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' }, + { type: 'testFailure', debugTxHash: '0x7d9381986adb7e9e6d7d65191f82633cb453406556569f77f5e0b4aa39274324', value: 'Equal string fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalStringFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1916:81:0', expected: 'remix-tests', returned: 'remix' } ], ['time', 'web3']) }) }) @@ -202,19 +202,19 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertNotEqualTest', filename: __dirname + '/examples_0/assert_notEqual_test.sol' }, - { type: 'testPass', debugTxHash: '0xdef34ec7fbc6a3e6c6ef619b424bf8ebf16db16ed3f74500d56d8170d3aeca66', value: 'Not equal uint pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0xfcbd35bc5f460e22e885951d560171d687cf90ccdffc41fb5de1beb7075fe4e9', value: 'Not equal uint fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualUintFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '288:63:0', expected: '1', returned: '1' }, - { type: 'testPass', debugTxHash: '0x7f269855c3fc5c677eca416eb85665b8f10df00d3b7ec5dcc00cbf8e6364cba4', value: 'Not equal int pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x76555e218571d4ad69496d7d10ae46d30149c4bfd8c6e15ff2a58668ab6fba62', value: 'Not equal int fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualIntFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '525:52:0', expected: '-2', returned: '-2' }, - { type: 'testPass', debugTxHash: '0x5fe790b3f32b9580c1d5f9a2dbb0e10ddcb62846037d3f5800d47a51bb67cc91', value: 'Not equal bool pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x660d0a73395e6855aea8f6d3450e63640437dc15071842b417c39f40e1d7ae61', value: 'Not equal bool fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBoolFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '760:57:0', expected: true, returned: true }, - { type: 'testPass', debugTxHash: '0x6fddce5573bd6723acf5a3e4137d698ff78f695873a228939276c4323ddfb132', value: 'Not equal address pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testPass', debugTxHash: '0xebc513bd2625be3b228840ebb8f5a9ea53e4f32870f015c97d1b6f91b916b367', value: 'Not equal uint pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x2cb59a137f2d3a76eee28bce662cb8618ad9cb9068f6be51935bf24911f90b43', value: 'Not equal uint fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualUintFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '288:63:0', expected: '1', returned: '1' }, + { type: 'testPass', debugTxHash: '0xc66c3c63a74463e5ce77311393d143b014ea510fb49992a7a828a78f00f9ca51', value: 'Not equal int pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x1606fcdf8583d0e629125bcadf2f115334228fac79cbbb5d756fafd9ca6b8f06', value: 'Not equal int fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualIntFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '525:52:0', expected: '-2', returned: '-2' }, + { type: 'testPass', debugTxHash: '0x8cce267a27b4b7aedb9f50499017b971d4058cd11c7f61a267b9ff159126af6d', value: 'Not equal bool pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x2e08b9742bf3d99f08c5563600ab228a54c3b9e432a40912ab6d4a5f3779a671', value: 'Not equal bool fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBoolFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '760:57:0', expected: true, returned: true }, + { type: 'testPass', debugTxHash: '0x03cb41ad90b032bb94a994e62828bf03f8042c8b41f51c46f68171a3b4ab36a3', value: 'Not equal address pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, // eslint-disable-next-line @typescript-eslint/no-loss-of-precision - { type: 'testFailure', debugTxHash: '0x51479e46db802fb598c61ca0dd630345b9d70cc58667b5a80aa79e8119fa7787', value: 'Not equal address fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualAddressFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1084:136:0', expected: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9, returned: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9 }, - { type: 'testPass', debugTxHash: '0xbcaf6d8977b655fdedb280e0e9221d728706d41e85e0973d00c8da1d128022c7', value: 'Not equal bytes32 pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x34008ef0ea908fedbf80471424d801f5069e6e46221f8ee4a2ee16776a6eeef6', value: 'Not equal bytes32 fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBytes32FailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1756:54:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6978000000000000000000000000000000000000000000000000000000' }, - { type: 'testPass', debugTxHash: '0x8e0bc9dedea6e088ca7bd82b1e9fab516be5a52f7716a26ccca8197236aae105', value: 'Not equal string pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, - { type: 'testFailure', debugTxHash: '0x13c6d270c3609ef858dd6d0c79433ca0b43e47b485b2e40ffe363f18f2868ea8', value: 'Not equal string fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualStringFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '2026:81:0', expected: 'remix', returned: 'remix' }, + { type: 'testFailure', debugTxHash: '0xa68c8214a422e42335ba50407f5bc096332c289a9fede3d264ef27e02e6565af', value: 'Not equal address fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualAddressFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1084:136:0', expected: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9, returned: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9 }, + { type: 'testPass', debugTxHash: '0xb83a9e2dd2bc916b40a87f038b0c96b3be6b2796d65900bbdf1467027d0b4f96', value: 'Not equal bytes32 pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0x3a8e50586fee6fe4e5d765d1392151e550b28959ea8eb823e8fdf54627fa861e', value: 'Not equal bytes32 fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBytes32FailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1756:54:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6978000000000000000000000000000000000000000000000000000000' }, + { type: 'testPass', debugTxHash: '0x8b84801330bbd44f358aee9089263ad7400ffc738f2f1f9e6b06cf6af20816d1', value: 'Not equal string pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' }, + { type: 'testFailure', debugTxHash: '0xb2e1b7cdfdc622e1c39e436054168861ca68bb51147940bff6ebd075f8afd2da', value: 'Not equal string fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualStringFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '2026:81:0', expected: 'remix', returned: 'remix' }, ], ['time', 'web3']) }) }) @@ -241,14 +241,14 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertGreaterThanTest', filename: __dirname + '/examples_0/assert_greaterThan_test.sol' }, - { type: 'testPass', debugTxHash: '0xdc325916fd93227b76231131e52e67f8913d395098c5ac767032db9bd757a91c', value: 'Greater than uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0xf98eea22bb86f13e0bb4072df22b540289a46b332bdb203a1e488d7e14a1dcd4', value: 'Greater than uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '303:69:0', expected: '4', returned: '1' }, - { type: 'testPass', debugTxHash: '0xef5ef38329ba6aac2f868d53d803053c52b1895a2c25b704260435c141a63bfc', value: 'Greater than int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0x6b9430f3f12c12fb11e5a8d32fef849ab34614e644be20c6b41a25e510453440', value: 'Greater than int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '569:67:0', expected: '1', returned: '-1' }, - { type: 'testPass', debugTxHash: '0x4c6e10815a5e82bf2c60950606dc886317f680028a9229ba2dda17b5ea36325a', value: 'Greater than uint int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0x989c405c32c8e270a5dea69e6250a514c05dacd6fcf018365a241abc28c2497b', value: 'Greater than uint int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '845:71:0', expected: '2', returned: '1' }, - { type: 'testPass', debugTxHash: '0x9fed670ae2061929f71780835b7ea3eb7da6d4fb553cd2d5f62950c353165861', value: 'Greater than int uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, - { type: 'testFailure', debugTxHash: '0xcf394fd279293cdcf58efc42f3a443595fdb171769a45df01b0c84cd76b3a9a2', value: 'Greater than int uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '1125:76:0', expected: '115792089237316195423570985008687907853269984665640564039457584007913129639836', returned: '100' } + { type: 'testPass', debugTxHash: '0x563a5b040bd6403de8543a8c44f1b11bb99dbece557f428b966fb70d389e602b', value: 'Greater than uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0x14ec758d313b505d92b0505e0ce940df318bc0136ea6ab9b40bba1a94baeaca5', value: 'Greater than uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '303:69:0', expected: '4', returned: '1' }, + { type: 'testPass', debugTxHash: '0x2de3f6fdf2c93f50289dd5fe469a13e03c22961ddcf110e5f3819a7af39e3e25', value: 'Greater than int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0xa8b897e011895858be4bc37f20e1e17a490b964d0683df5b430ac648e992bd57', value: 'Greater than int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '569:67:0', expected: '1', returned: '-1' }, + { type: 'testPass', debugTxHash: '0xf52652ef6020ae091022455df8713d20cb00a35de8bf485e177128a457a50d6c', value: 'Greater than uint int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0x9f826060a0e5a8c0187d5e9ffe83a153080379a1e1fea0b267745eb8bd52fd6f', value: 'Greater than uint int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '845:71:0', expected: '2', returned: '1' }, + { type: 'testPass', debugTxHash: '0x04e1703c75cc4beb4b8c9ddfb79489192423fe745089382cadb1811cbf2d915c', value: 'Greater than int uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' }, + { type: 'testFailure', debugTxHash: '0xbb5c94a5fc46417a4d3c763994da78d4f27c83dbc5545cb0522a39c3c6017432', value: 'Greater than int uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '1125:76:0', expected: '115792089237316195423570985008687907853269984665640564039457584007913129639836', returned: '100' } ], ['time', 'web3']) }) }) @@ -276,14 +276,14 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'AssertLesserThanTest', filename: __dirname + '/examples_0/assert_lesserThan_test.sol' }, - { type: 'testPass', debugTxHash: '0x524fb46aa0e8a78bc11a99432908d422450c2933d837f858aeacba9b84706d5c', value: 'Lesser than uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0x0551a67b10b9e13182e8bdb4e530ed92466d5054ae959f999f2c558da2c39d22', value: 'Lesser than uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '298:67:0', expected: '2', returned: '4' }, - { type: 'testPass', debugTxHash: '0x6d63958d8c3230e837d0ca8335e57262c6e0c6b2c07a5b481842b9ad7329ac28', value: 'Lesser than int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0x38e96ef44f4e785db4d40a95862a9797e8cef6de0ce1d059da72ff42e2f3ca62', value: 'Lesser than int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '557:65:0', expected: '-1', returned: '1' }, - { type: 'testPass', debugTxHash: '0x699f9fc2bf7a14134e89b94cd9dc1c537b5d4581a1c26a34a0c3343ddede9608', value: 'Lesser than uint int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0xce1391dcfbfdc6c611e357e6c1c9f6cd9f257153ee400cb80bd36af6d239c342', value: 'Lesser than uint int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '826:71:0', expected: '-1', returned: '115792089237316195423570985008687907853269984665640564039457584007913129639935' }, - { type: 'testPass', debugTxHash: '0x7040e6664c13e6b35ef1daaef93a8cae36a62150d818183892096a98b921800c', value: 'Lesser than int uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, - { type: 'testFailure', debugTxHash: '0x8c58bb433ea41760dcf11114232407d703e8ebf7d5e9637e2923282eae5caee6', value: 'Lesser than int uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '1105:69:0', expected: '1', returned: '1' }, + { type: 'testPass', debugTxHash: '0x407c27582c0b6b2275d03372907c2efcafa1c9e8a1e6ea8d4e20f88e3222b6c7', value: 'Lesser than uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0x6e7fc07decdb5deb71b1177fc7b151bce5b8e42fc6137e1418b4e7277960d972', value: 'Lesser than uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '298:67:0', expected: '2', returned: '4' }, + { type: 'testPass', debugTxHash: '0x353c9bcf4b61abaf4b6ffaae02f18ea0a7fb38a8c3c7a915939561cdf97f2d72', value: 'Lesser than int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0xccab30c5a154c4c2e8ca9a8966b86a55f08188d606c3519a5c29534b4b64fb47', value: 'Lesser than int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '557:65:0', expected: '-1', returned: '1' }, + { type: 'testPass', debugTxHash: '0x8e90fb7f3b8343d037444275cd69d431f75a7fc6b46322c69397373463cee22a', value: 'Lesser than uint int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0x5db60fe115958b767f0defe81eeb6322ee18ec8df690abad0d1581175882136b', value: 'Lesser than uint int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '826:71:0', expected: '-1', returned: '115792089237316195423570985008687907853269984665640564039457584007913129639935' }, + { type: 'testPass', debugTxHash: '0x19f79e8c8ec360cd27beee6399e6853a4fe335af78364ed35c93f8fe39e3100c', value: 'Lesser than int uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' }, + { type: 'testFailure', debugTxHash: '0x520ecba457bf71f42d24f61432d872121da699af0374090e2e9098a6719cb0ce', value: 'Lesser than int uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '1105:69:0', expected: '1', returned: '1' }, ], ['time', 'web3']) }) }) @@ -311,10 +311,10 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_1/simple_storage_test.sol' }, - { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, - { type: 'testPass', debugTxHash: '0x79cae5c4f44edfd7ae3490e01c75df5741b107672cef5e69800e4d30d380a721', value: 'Initial value should not be200', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, - { type: 'testFailure', debugTxHash: '0x24a20f7643e88f891e469ef495911ab0b75f99e2b09b9b091e688674910d1506', value: 'Should trigger one fail', filename: __dirname + '/examples_1/simple_storage_test.sol', errMsg: 'uint test 1 fails', context: 'MyTest', assertMethod: 'equal', location: '532:51:1', expected: '2', returned: '1' }, - { type: 'testPass', debugTxHash: '0x08b1f60c908b7e6cf2dd24fc166c755f0fe5336aebfb325cae4ce00ea9bbf932', value: 'Should trigger one pass', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' } + { type: 'testPass', debugTxHash: '0xd86dbe1efaf707981475a9a4762826c6852cce3e5b0e987827027602d6d6d734', value: 'Initial value should be100', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, + { type: 'testPass', debugTxHash: '0x1b5cce7f93b78f8c97ba915e24648127b7f28e86008668d20a4c20fd0fde40bc', value: 'Initial value should not be200', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }, + { type: 'testFailure', debugTxHash: '0xdb9cbef289c9e53f4119ad60c1f3e29770de930091e17ab987529c7057013628', value: 'Should trigger one fail', filename: __dirname + '/examples_1/simple_storage_test.sol', errMsg: 'uint test 1 fails', context: 'MyTest', assertMethod: 'equal', location: '532:51:1', expected: '2', returned: '1' }, + { type: 'testPass', debugTxHash: '0x21472600af5de67cd53a489f2435169fdfbe83d7b7dd43c8a0150725fd91e254', value: 'Should trigger one pass', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' } ], ['time', 'web3']) }) }) @@ -342,8 +342,8 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'MyTest', filename: __dirname + '/examples_2/simple_storage_test.sol' }, - { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' }, - { type: 'testPass', debugTxHash: '0x8ed5b4858405b43ad4052f5690b4b711c0f6cdeb67a64f54084417d43bc54308', value: 'Value is set200', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' } + { type: 'testPass', debugTxHash: '0xd86dbe1efaf707981475a9a4762826c6852cce3e5b0e987827027602d6d6d734', value: 'Initial value should be100', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' }, + { type: 'testPass', debugTxHash: '0xa447f168cd1ce406635ea2368b61828b107473905e270957b7ee38b94a12e055', value: 'Value is set200', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' } ], ['time', 'web3']) }) }) @@ -368,8 +368,8 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'StringTest', filename: __dirname + '/examples_3/simple_string_test.sol' }, - { type: 'testPass', debugTxHash: '0x3567da76ffbec37e3b43a41987a7ff3e61b41b4c544f35c010d2d4b39568d6d4', value: 'Initial value should be hello world', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' }, - { type: 'testPass', debugTxHash: '0x8619b743ccc99be7d5347a064732474b2d1b69844be65b0e7754c6ac1340d275', value: 'Value should not be hello wordl', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' } + { type: 'testPass', debugTxHash: '0x0f988e614ae6e9a5f560734e8b63f835de14460a5b797e16fa5c68091452d2c5', value: 'Initial value should be hello world', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' }, + { type: 'testPass', debugTxHash: '0x713ec0ad3cd02ffcd64f54e45b4da5498983d18b5a696ea34e9fb5d01928cb3f', value: 'Value should not be hello wordl', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' } ], ['time', 'web3']) }) }) @@ -394,9 +394,9 @@ describe('testRunner', function () { deepEqualExcluding(tests, [ { type: 'accountList', value: accounts }, { type: 'contract', value: 'StorageResolveTest', filename: __dirname + '/examples_5/test/simple_storage_test.sol' }, - { type: 'testPass', debugTxHash: '0xed5b6898331119c6e3d1185b9de65d87ad7329cc629a8f2d43b966cf180a5dc1', value: 'Initial value should be100', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, - { type: 'testPass', debugTxHash: '0x6893fe4f5a83cc51f03c9237ab93b93ffd826236167d58e20666be4c1b3128a4', value: 'Check if even', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, - { type: 'testPass', debugTxHash: '0x64e600b32be681b68926660042ddd96f22d07949b424959811b8acb56e72f719', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' } + { type: 'testPass', debugTxHash: '0xd86dbe1efaf707981475a9a4762826c6852cce3e5b0e987827027602d6d6d734', value: 'Initial value should be100', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, + { type: 'testPass', debugTxHash: '0xc9e1523f6f094cdd909b3977d1eef7c83284b15c22b17b9b0a4a632bf59881f6', value: 'Check if even', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }, + { type: 'testPass', debugTxHash: '0xe3f415f2cade92243fd795b9988fc9e9c4318983933c0a0b103e968f31c40f55', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' } ], ['time', 'web3']) }) }) diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index 300d418360..f3fd27a798 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -858,6 +858,9 @@ export const EditorUI = (props: EditorUIProps) => { monacoRef.current.languages.register({ id: 'remix-move' }) monacoRef.current.languages.register({ id: 'remix-circom' }) + // Allow JSON schema requests + monacoRef.current.languages.json.jsonDefaults.setDiagnosticsOptions({enableSchemaRequest: true}) + // Register a tokens provider for the language monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any) monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig as any) diff --git a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts index cac2138f6b..68ea85b72d 100644 --- a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts +++ b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts @@ -1,4 +1,4 @@ -import * as ethJSUtil from '@ethereumjs/util' +import { bytesToHex, toChecksumAddress } from '@ethereumjs/util' export const extractNameFromKey = (key: string): string => { if (!key) return @@ -97,12 +97,12 @@ export const shortenAddress = (address, etherBalance?) => { export const addressToString = (address) => { if (!address) return null if (typeof address !== 'string') { - address = address.toString('hex') + address = bytesToHex(address) } if (address.indexOf('0x') === -1) { address = '0x' + address } - return ethJSUtil.toChecksumAddress(address) + return toChecksumAddress(address) } export const is0XPrefixed = (value) => { diff --git a/libs/remix-ui/panel/src/lib/plugins/panel.css b/libs/remix-ui/panel/src/lib/plugins/panel.css index f0cc402b55..437471fabb 100644 --- a/libs/remix-ui/panel/src/lib/plugins/panel.css +++ b/libs/remix-ui/panel/src/lib/plugins/panel.css @@ -104,7 +104,5 @@ iframe { height: 2rem !important; } - .terminal-wrap.minimized.desktop { - height: 4.5rem !important; } diff --git a/libs/remix-ui/run-tab/src/lib/actions/actions.ts b/libs/remix-ui/run-tab/src/lib/actions/actions.ts index 16f144560c..127993af20 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/actions.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/actions.ts @@ -1,5 +1,5 @@ import { ContractData } from "@remix-project/core-plugin" -import { addNewInstance, addProvider, clearAllInstances, clearRecorderCount, hidePopUp, newProxyDeployment, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentContract, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setSelectedAccount, setSendUnit, setSendValue } from "./payload" +import { addNewInstance, addNewSavedInstance, addProvider, clearAllInstances, clearAllSavedInstances, clearRecorderCount, hidePopUp, newProxyDeployment, removeExistingInstance, removeProvider, setBaseFeePerGas, setConfirmSettings, setCurrentContract, setExecutionEnvironment, setExternalEndpoint, setGasLimit, setGasPrice, setGasPriceStatus, setMatchPassphrase, setMaxFee, setMaxPriorityFee, setNetworkName, setPassphrase, setPathToScenario, setSelectedAccount, setSendUnit, setSendValue } from "./payload" export const setAccount = (dispatch: React.Dispatch, account: string) => { dispatch(setSelectedAccount(account)) @@ -70,8 +70,13 @@ export const addInstance = (dispatch: React.Dispatch, instance: { contractD dispatch(addNewInstance(instance)) } -export const removeInstance = (dispatch: React.Dispatch, index: number) => { - dispatch(removeExistingInstance(index)) +export const addSavedInstance = (dispatch: React.Dispatch, instance: { contractData?: ContractData, address: string, name: string, abi?: any, decodedResponse?: Record, savedOn?: number, filePath?: string }) => { + instance.decodedResponse = {} + dispatch(addNewSavedInstance(instance)) +} + +export const removeInstance = (dispatch: React.Dispatch, index: number, isSavedContract: boolean, shouldDelete: boolean) => { + dispatch(removeExistingInstance(index, isSavedContract, shouldDelete)) } export const clearInstances = (dispatch: React.Dispatch) => { @@ -79,6 +84,11 @@ export const clearInstances = (dispatch: React.Dispatch) => { dispatch(clearRecorderCount()) } +export const clearSavedInstances = (dispatch: React.Dispatch) => { + dispatch(clearAllSavedInstances()) + dispatch(clearRecorderCount()) +} + export const setSelectedContract = (dispatch: React.Dispatch, contractName: string) => { dispatch(setCurrentContract(contractName)) } 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 08d3e10af4..c6b3418a00 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts @@ -250,7 +250,8 @@ export const loadAddress = (plugin: RunTab, dispatch: React.Dispatch, contr return dispatch(displayNotification('Alert', error, 'OK', null)) } if (loadType === 'abi') { - return addInstance(dispatch, { abi, address, name: '' }) + const contractData = { name: '', abi, contract: {file: plugin.REACT_API.contracts.currentFile}} as ContractData + return addInstance(dispatch, { contractData, address, name: '' }) } else if (loadType === 'instance') { if (!contract) return dispatch(displayPopUp('No compiled contracts found.')) const currentFile = plugin.REACT_API.contracts.currentFile @@ -282,6 +283,7 @@ export const runTransactions = ( plugin: RunTab, dispatch: React.Dispatch, instanceIndex: number, + isSavedContract: boolean, lookupOnly: boolean, funcABI: FuncABI, inputsValues: string, @@ -318,7 +320,7 @@ export const runTransactions = ( (returnValue) => { const response = txFormat.decodeResponse(returnValue, funcABI) - dispatch(setDecodedResponse(instanceIndex, response, funcIndex)) + dispatch(setDecodedResponse(instanceIndex, response, funcIndex, isSavedContract)) }, (network, tx, gasEstimation, continueTxExecution, cancelCb) => { confirmationHandler(plugin, dispatch, mainnetPrompt, network, tx, gasEstimation, 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 fddd679885..ab2188847b 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/events.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/events.ts @@ -1,8 +1,8 @@ import { envChangeNotification } from "@remix-ui/helper" import { RunTab } from "../types/run-tab" import { setExecutionContext, setFinalContext, updateAccountBalances, fillAccountsList } from "./account" -import { addExternalProvider, addInstance, addNewProxyDeployment, removeExternalProvider, setNetworkNameFromProvider } from "./actions" -import { addDeployOption, clearAllInstances, clearRecorderCount, fetchContractListSuccess, resetProxyDeployments, resetUdapp, setCurrentContract, setCurrentFile, setLoadType, setRecorderCount, setRemixDActivated, setSendValue, fetchAccountsListSuccess } from "./payload" +import { addExternalProvider, addInstance, addSavedInstance, addNewProxyDeployment, removeExternalProvider, setNetworkNameFromProvider } from "./actions" +import { addDeployOption, clearAllInstances, clearAllSavedInstances, clearRecorderCount, fetchContractListSuccess, resetProxyDeployments, resetUdapp, setCurrentContract, setCurrentFile, setLoadType, setRecorderCount, setRemixDActivated, setSendValue, fetchAccountsListSuccess } from "./payload" import { updateInstanceBalance } from './deploy' import { CompilerAbstract } from '@remix-project/remix-solidity' import BN from 'bn.js' @@ -79,8 +79,16 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch) => { dispatch(clearAllInstances()) }) - plugin.on('udapp', 'addInstanceReducer', (address, abi, name) => { - addInstance(dispatch, { abi, address, name }) + plugin.on('udapp', 'clearAllSavedInstancesReducer', () => { + dispatch(clearAllSavedInstances()) + }) + + plugin.on('udapp', 'addInstanceReducer', (address, abi, name, contractData?) => { + addInstance(dispatch, { contractData, abi, address, name }) + }) + + plugin.on('udapp', 'addSavedInstanceReducer', (address, abi, name, savedOn, filePath) => { + addSavedInstance(dispatch, { abi, address, name, savedOn, filePath}) }) plugin.on('filePanel', 'setWorkspace', () => { 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 a2b44f9897..7c40c0f65a 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/index.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/index.ts @@ -48,9 +48,9 @@ export const setGasPriceStatus = (status: boolean) => updateGasPriceStatus(dispa export const setMaxFee = (fee: string) => updateMaxFee(dispatch, fee) export const setMaxPriorityFee = (fee: string) => updateMaxPriorityFee(dispatch, fee) export const removeInstances = () => clearInstances(dispatch) -export const removeSingleInstance = (index: number) => removeInstance(dispatch, index) +export const removeSingleInstance = (index: number, isSavedContract: boolean, shouldDelete: boolean) => removeInstance(dispatch, index, isSavedContract, shouldDelete) export const getExecutionContext = () => getContext(plugin) -export const executeTransactions = (instanceIndex: number, lookupOnly: boolean, funcABI: FuncABI, inputsValues: string, contractName: string, contractABI, contract, address, logMsg:string, mainnetPrompt: MainnetPrompt, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, funcIndex?: number) => runTransactions(plugin, dispatch, instanceIndex, lookupOnly, funcABI, inputsValues, contractName, contractABI, contract, address, logMsg, mainnetPrompt, gasEstimationPrompt, passphrasePrompt, funcIndex) +export const executeTransactions = (instanceIndex: number, isSavedContract: boolean, lookupOnly: boolean, funcABI: FuncABI, inputsValues: string, contractName: string, contractABI, contract, address, logMsg:string, mainnetPrompt: MainnetPrompt, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, funcIndex?: number) => runTransactions(plugin, dispatch, instanceIndex, isSavedContract, lookupOnly, funcABI, inputsValues, contractName, contractABI, contract, address, logMsg, mainnetPrompt, gasEstimationPrompt, passphrasePrompt, funcIndex) export const loadFromAddress = (contract: ContractData, address: string) => loadAddress(plugin, dispatch, contract, address) export const storeNewScenario = async (prompt: (msg: string, defaultValue: string) => JSX.Element) => storeScenario(plugin, dispatch, prompt) export const runScenario = (liveMode: boolean, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => runCurrentScenario(liveMode, plugin, dispatch, gasEstimationPrompt, passphrasePrompt, confirmDialogContent) 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 dfa8fc4b16..8d2e9a8366 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/payload.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/payload.ts @@ -1,5 +1,5 @@ import { ContractData } from '@remix-project/core-plugin' -import { ADD_DEPLOY_OPTION, ADD_INSTANCE, UPDATE_INSTANCES_BALANCE, 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_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_REMIXD_ACTIVATED, FETCH_PROXY_DEPLOYMENTS, NEW_PROXY_DEPLOYMENT, RESET_PROXY_DEPLOYMENTS, EXTRACT_COMPILER_VERSION } from '../constants' +import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_SAVED_INSTANCE, UPDATE_INSTANCES_BALANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_SAVED_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_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_REMIXD_ACTIVATED, FETCH_PROXY_DEPLOYMENTS, NEW_PROXY_DEPLOYMENT, RESET_PROXY_DEPLOYMENTS, EXTRACT_COMPILER_VERSION } from '../constants' import { ContractList, DeployOptions } from '../types' export const fetchAccountsListRequest = () => { @@ -230,10 +230,21 @@ export const addNewInstance = (instance: { contractData?: ContractData, address: } } -export const removeExistingInstance = (index: number) => { +export const addNewSavedInstance = (instance: { contractData?: ContractData, address: string, name: string, abi?: any, savedOn?: number }) => { + return { + type: ADD_SAVED_INSTANCE, + payload: instance + } +} + +export const removeExistingInstance = (index: number, isSavedContract: boolean, shouldDelete: boolean) => { return { type: REMOVE_INSTANCE, - payload: index + payload: { + index, + isSavedContract, + shouldDelete + } } } @@ -243,13 +254,20 @@ export const clearAllInstances = () => { } } -export const setDecodedResponse = (instanceIndex: number, response, funcIndex?: number) => { +export const clearAllSavedInstances = () => { + return { + type: CLEAR_SAVED_INSTANCES + } +} + +export const setDecodedResponse = (instanceIndex: number, response, funcIndex?: number, isSavedContract?: boolean) => { return { type: SET_DECODED_RESPONSE, payload: { instanceIndex, funcIndex, - response + response, + isSavedContract } } } diff --git a/libs/remix-ui/run-tab/src/lib/components/account.tsx b/libs/remix-ui/run-tab/src/lib/components/account.tsx index 1ea87b2d12..d1ab6b5552 100644 --- a/libs/remix-ui/run-tab/src/lib/components/account.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/account.tsx @@ -34,7 +34,7 @@ export function AccountUI(props: AccountProps) { }) break - case 'vm-merge': + case 'vm-paris': setPlusOpt({ classList: '', title: intl.formatMessage({id: 'udapp.createNewAccount'}) diff --git a/libs/remix-ui/run-tab/src/lib/components/environment.tsx b/libs/remix-ui/run-tab/src/lib/components/environment.tsx index 9ef3082d43..04ba57383d 100644 --- a/libs/remix-ui/run-tab/src/lib/components/environment.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/environment.tsx @@ -14,7 +14,7 @@ export function EnvironmentUI(props: EnvironmentProps) { const currentProvider = props.providers.providerList.find((exEnv) => exEnv.value === props.selectedEnv) const bridges = { - 'injected-optimism-provider': 'https://www.optimism.io/apps/bridges', + 'injected-optimism-provider': 'https://app.optimism.io/bridge/deposit', 'injected-arbitrum-one-provider': 'https://bridge.arbitrum.io/' } diff --git a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx index 4296867c67..7fcc0b4970 100644 --- a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx @@ -1,22 +1,113 @@ // eslint-disable-next-line no-use-before-define import { CustomTooltip } from '@remix-ui/helper' -import React from 'react' -import { FormattedMessage, useIntl } from 'react-intl' +import React, {useEffect, useRef} from 'react' +import { FormattedMessage } from 'react-intl' import { InstanceContainerProps } from '../types' import { UniversalDappUI } from './universalDappUI' export function InstanceContainerUI(props: InstanceContainerProps) { const { instanceList } = props.instances + const enableSave = useRef(false) + const chainId = useRef() + + useEffect(() => { + const fetchSavedContracts = async () => { + if (props.plugin.REACT_API.selectExEnv && props.plugin.REACT_API.selectExEnv.startsWith('vm-')) enableSave.current = false + else enableSave.current = true + if (enableSave.current) { + const { network } = await props.plugin.call('blockchain', 'getCurrentNetworkStatus') + chainId.current = network.id + // Move contract saved in localstorage to Remix FE + const allSavedContracts = localStorage.getItem('savedContracts') + if (allSavedContracts) { + const savedContracts = JSON.parse(allSavedContracts) + for (const networkId in savedContracts) { + if (savedContracts[networkId].length > 0) { + for (const contractDetails of savedContracts[networkId]) { + const objToSave = { + name: contractDetails.name, + address: contractDetails.address, + abi: contractDetails.abi || contractDetails.contractData.abi, + filePath: contractDetails.filePath, + pinnedAt: contractDetails.savedOn + } + await props.plugin.call('fileManager', 'writeFile', `.deploys/pinned-contracts/${networkId}/${contractDetails.address}.json`, JSON.stringify(objToSave, null, 2)) + } + } + } + localStorage.removeItem('savedContracts') + } + // Clear existing saved instance state + await props.plugin.call('udapp', 'clearAllSavedInstances') + // Load contracts from FE + const isPinnedAvailable = await props.plugin.call('fileManager', 'exists', `.deploys/pinned-contracts/${chainId.current}`) + if (isPinnedAvailable) { + try { + const list = await props.plugin.call('fileManager', 'readdir', `.deploys/pinned-contracts/${chainId.current}`) + const filePaths = Object.keys(list) + for (const file of filePaths) { + const pinnedContract = await props.plugin.call('fileManager', 'readFile', file) + const pinnedContractObj = JSON.parse(pinnedContract) + if (pinnedContractObj) await props.plugin.call('udapp', 'addSavedInstance', pinnedContractObj.address, pinnedContractObj.abi, pinnedContractObj.name, pinnedContractObj.pinnedAt, pinnedContractObj.filePath) + } + } catch(err) { + console.log(err) + } + } + } + } + fetchSavedContracts() + }, [props.plugin.REACT_API.selectExEnv, props.plugin.REACT_API.networkName]) const clearInstance = () => { props.clearInstances() } - const intl = useIntl() - return (
-
+ { enableSave.current ? ( +
+ }> + + +
) : null } + { enableSave.current ? ( + props.savedInstances.instanceList.length > 0 ? ( +
+ {' '} + {props.savedInstances.instanceList.map((instance, index) => { + return ( + + ) + })} +
+ ) : ( + + + + ) + ) : null } + +
}>