diff --git a/.github/workflows/pr-reminder.yml b/.github/workflows/pr-reminder.yml index 41f7db24e8..e455d557a7 100644 --- a/.github/workflows/pr-reminder.yml +++ b/.github/workflows/pr-reminder.yml @@ -3,7 +3,7 @@ name: PRs reviews reminder on: schedule: - cron: "0 8 * * 1-5" - - cron: '0 9 * * 1-5' + - cron: '58 9 * * 1-5' workflow_dispatch: jobs: @@ -19,7 +19,7 @@ jobs: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} freeze-date: '2024-11-04T18:00:00Z' - name: Reminder for standup - if: github.event.schedule == '0 9 * * 1-5' + if: github.event.schedule == '58 9 * * 1-5' uses: Aniket-Engg/pr-reviews-reminder-action@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx b/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx index c6f78c5f2f..eec16f8845 100644 --- a/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx +++ b/apps/contract-verification/src/app/components/SearchableChainDropdown.tsx @@ -37,7 +37,6 @@ export const SearchableChainDropdown: React.FC = ({ label, id, se const [searchTerm, setSearchTerm] = useState(selectedChain ? getChainDescriptor(selectedChain) : '') const [isOpen, setIsOpen] = useState(false) - const [filteredOptions, setFilteredOptions] = useState(dropdownChains) const dropdownRef = useRef(null) const fuse = new Fuse(dropdownChains, { @@ -45,14 +44,7 @@ export const SearchableChainDropdown: React.FC = ({ label, id, se threshold: 0.3, }) - useEffect(() => { - if (searchTerm === '') { - setFilteredOptions(dropdownChains) - } else { - const result = fuse.search(searchTerm) - setFilteredOptions(result.map(({ item }) => item)) - } - }, [searchTerm, dropdownChains]) + const filteredOptions = searchTerm ? fuse.search(searchTerm).map(({ item }) => item) : dropdownChains // Close dropdown when user clicks outside useEffect(() => { @@ -99,15 +91,13 @@ export const SearchableChainDropdown: React.FC = ({ label, id, se {/* Add ref here */} - {isOpen && ( -
    - {filteredOptions.map((chain) => ( -
  • handleOptionClick(chain)} className={`dropdown-item text-dark ${selectedChain?.chainId === chain.chainId ? 'active' : ''}`} style={{ cursor: 'pointer', whiteSpace: 'normal' }}> - {getChainDescriptor(chain)} -
  • - ))} -
- )} +
    + {filteredOptions.map((chain) => ( +
  • handleOptionClick(chain)} className={`dropdown-item text-dark ${selectedChain?.chainId === chain.chainId ? 'active' : ''}`} style={{ cursor: 'pointer', whiteSpace: 'normal' }}> + {getChainDescriptor(chain)} +
  • + ))} +
) } diff --git a/apps/contract-verification/src/app/views/VerifyView.tsx b/apps/contract-verification/src/app/views/VerifyView.tsx index 20a0022d47..a924a2a89a 100644 --- a/apps/contract-verification/src/app/views/VerifyView.tsx +++ b/apps/contract-verification/src/app/views/VerifyView.tsx @@ -68,9 +68,9 @@ export const VerifyView = () => { name: verifierId as VerifierIdentifier, } receipts.push({ verifierInfo, status: 'pending', contractId, isProxyReceipt: false, failedChecks: 0 }) - if (enabledVerifiers.Blockscout) await sendToMatomo('verify', "verifyWith: Blockscout On: " + selectedChain + " IsProxy: " + (hasProxy && !proxyAddress)) - if (enabledVerifiers.Etherscan) await sendToMatomo('verify', "verifyWithEtherscan On: " + selectedChain + " IsProxy: " + (hasProxy && !proxyAddress)) - if (enabledVerifiers.Sourcify) await sendToMatomo('verify', "verifyWithSourcify On: " + selectedChain + " IsProxy: " + (hasProxy && !proxyAddress)) + if (enabledVerifiers.Blockscout) await sendToMatomo('verify', "verifyWith: Blockscout On: " + selectedChain?.chainId + " IsProxy: " + (hasProxy && !proxyAddress)) + if (enabledVerifiers.Etherscan) await sendToMatomo('verify', "verifyWithEtherscan On: " + selectedChain?.chainId + " IsProxy: " + (hasProxy && !proxyAddress)) + if (enabledVerifiers.Sourcify) await sendToMatomo('verify', "verifyWithSourcify On: " + selectedChain?.chainId + " IsProxy: " + (hasProxy && !proxyAddress)) } const newSubmittedContract: SubmittedContract = { diff --git a/apps/remix-dapp/src/utils/txRunner.ts b/apps/remix-dapp/src/utils/txRunner.ts index 6f22ca6c13..1914302c02 100644 --- a/apps/remix-dapp/src/utils/txRunner.ts +++ b/apps/remix-dapp/src/utils/txRunner.ts @@ -267,7 +267,7 @@ export class TxRunner { }; } catch (error: any) { console.log( - `Send transaction failed: ${error.message} . if you use an injected provider, please check it is properly unlocked. ` + `Send transaction failed: ${error.message || error.error} . if you use an injected provider, please check it is properly unlocked. ` ); return { error }; } diff --git a/apps/remix-ide-e2e/src/commands/hideMetaMaskPopup.ts b/apps/remix-ide-e2e/src/commands/hideMetaMaskPopup.ts index 234a3a08cd..16aaa69962 100644 --- a/apps/remix-ide-e2e/src/commands/hideMetaMaskPopup.ts +++ b/apps/remix-ide-e2e/src/commands/hideMetaMaskPopup.ts @@ -5,7 +5,7 @@ class HideMetaMaskPopup extends EventEmitter { command(this: NightwatchBrowser) { browser .pause(5000) - .isVisible({ + .isPresent({ selector: 'button[data-testid="popover-close"]', locateStrategy: 'css selector', suppressNotFoundErrors: true, diff --git a/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts b/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts index 2b130875e3..dc52bcf307 100644 --- a/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts @@ -19,16 +19,17 @@ module.exports = { }, 'Should load the test file #group1': function (browser: NightwatchBrowser) { browser.openFile('contracts') - .openFile('contracts/3_Ballot.sol') - .waitForElementVisible('#editorView') - .setEditorValue(BallotWithARefToOwner) - .pause(4000) // wait for the compiler to finish + .addFile('contracts/3_BallotHover.sol', { + content: BallotWithARefToOwner + }) .scrollToLine(37) + .useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]") + }, 'Should show hover over contract in editor #group1': function (browser: NightwatchBrowser) { const path = "//*[contains(text(),'BallotHoverTest')]" checkEditorHoverContent(browser, path, 'contract BallotHoverTest is BallotHoverTest') - checkEditorHoverContent(browser, path, 'contracts/3_Ballot.sol 10:0') + checkEditorHoverContent(browser, path, 'contracts/3_BallotHover.sol 10:0') checkEditorHoverContent(browser, path, '@title Ballot') }, 'Should show hover over var definition in editor #group1': function (browser: NightwatchBrowser) { @@ -88,13 +89,13 @@ module.exports = { }, 'Add token file #group1': function (browser: NightwatchBrowser) { browser - .clickLaunchIcon('solidity') - .setSolidityCompilerVersion('soljson-v0.8.20+commit.a1b79de6.js') - .click('*[data-id="scConfigExpander"]') - .setValue('#evmVersionSelector', 'berlin') // set target EVM for parser to berlin - .addFile('contracts/mytoken.sol', { - content: myToken - }).useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]") + .clickLaunchIcon('solidity') + .setSolidityCompilerVersion('soljson-v0.8.20+commit.a1b79de6.js') + .click('*[data-id="scConfigExpander"]') + .setValue('#evmVersionSelector', 'berlin') // set target EVM for parser to berlin + .addFile('contracts/mytoken.sol', { + content: myToken + }).useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]") }, // here we change quickly between files to test the files being parsed correctly when switching between them 'Should show ERC20 hover over contract in editor #group1': function (browser: NightwatchBrowser) { @@ -103,8 +104,12 @@ module.exports = { const expectedContent = 'contract ERC20Burnable is ERC20Burnable, ERC20, IERC20Errors, IERC20Metadata, IERC20, Context' checkEditorHoverContent(browser, path, expectedContent, 25) }, - 'Go back to ballot file': function (browser: NightwatchBrowser) { - browser.openFile('contracts/3_Ballot.sol') + 'Go back to ballot file #group1': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible('*[data-id="treeViewDivDraggableItem.deps"]') + .click('*[data-id="treeViewDivDraggableItem.deps"]') + .openFile('contracts/3_BallotHover.sol') + .scrollToLine(37) .useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]") }, 'Should show hover over function in editor again #group1': function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide-e2e/src/tests/metamask.test.ts b/apps/remix-ide-e2e/src/tests/metamask.test.ts new file mode 100644 index 0000000000..0051c57d9c --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/metamask.test.ts @@ -0,0 +1,188 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +const passphrase = process.env.account_passphrase +const password = process.env.account_password +const extension_id = 'nkbihfbeogaeaoehlefnkodbefgpgknn' +const extension_url = `chrome-extension://${extension_id}/home.html` + +const checkBrowserIsChrome = function (browser: NightwatchBrowser) { + return browser.browserName.indexOf('chrome') > -1 +} + +const checkAlerts = function (browser: NightwatchBrowser) { + browser.isVisible({ + selector: '//*[contains(.,"not have enough")]', + locateStrategy: 'xpath', + suppressNotFoundErrors: true, + timeout: 3000 + }, (okVisible) => { + if (okVisible.value) { + browser.assert.fail('Not enough ETH in test account!!') + browser.end() + } + }) +} + +const tests = { + '@disabled': true, + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + + '@sources': function () { + return sources + }, + + 'Should connect to Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) { + if (!checkBrowserIsChrome(browser)) return + browser.waitForElementPresent('*[data-id="remixIdeSidePanel"]') + .setupMetamask(passphrase, password) + .useCss().switchBrowserTab(0) + .refreshPage() + .waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000) + .click('*[data-id="landingPageStartSolidity"]') + .clickLaunchIcon('udapp') + .switchEnvironment('injected-MetaMask') + .waitForElementPresent('*[data-id="settingsNetworkEnv"]') + .assert.containsText('*[data-id="settingsNetworkEnv"]', 'Sepolia (11155111) network') + .pause(5000) + .switchBrowserWindow(extension_url, 'MetaMask', (browser) => { + browser + .hideMetaMaskPopup() + .waitForElementVisible('*[data-testid="page-container-footer-next"]', 60000) + .click('*[data-testid="page-container-footer-next"]') // this connects the metamask account to remix + .pause(2000) + .waitForElementVisible('*[data-testid="page-container-footer-next"]', 60000) + .click('*[data-testid="page-container-footer-next"]') + // .waitForElementVisible('*[data-testid="popover-close"]') + // .click('*[data-testid="popover-close"]') + }) + .switchBrowserTab(0) // back to remix + }, + + 'Should add a contract file #group1': function (browser: NightwatchBrowser) { + if (!checkBrowserIsChrome(browser)) return + browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') + .clickLaunchIcon('filePanel') + .addFile('Greet.sol', sources[0]['Greet.sol']) + .clickLaunchIcon('udapp') + .waitForElementVisible('*[data-id="Deploy - transact (not payable)"]', 45000) // wait for the contract to compile + }, + + 'Should deploy contract on Sepolia Test Network using MetaMask #group1': function (browser: NightwatchBrowser) { + if (!checkBrowserIsChrome(browser)) return + browser.clearConsole().waitForElementPresent('*[data-id="runTabSelectAccount"] option', 45000) + .clickLaunchIcon('filePanel') + .openFile('Greet.sol') + .clickLaunchIcon('udapp') + .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') + .click('*[data-id="Deploy - transact (not payable)"]') + .pause(5000) + .clearConsole() + .perform((done) => { + browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => { + checkAlerts(browser) + browser + .maximizeWindow() + .hideMetaMaskPopup() + .waitForElementPresent('[data-testid="page-container-footer-next"]') + .click('[data-testid="page-container-footer-next"]') // approve the tx + .switchBrowserTab(0) // back to remix + .waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000) + .waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000) + .perform(() => done()) + }) + }) + }, + 'Should deploy faulty contract on Sepolia Test Network using MetaMask and show error in terminal #group1': function (browser: NightwatchBrowser) { + browser + .clearConsole() + .clickLaunchIcon('filePanel') + .addFile('faulty.sol', sources[0]['faulty.sol']) + .clickLaunchIcon('udapp') + .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') + .click('*[data-id="Deploy - transact (not payable)"]') + .pause(5000) + .saveScreenshot('./reports/screenshots/metamask_7.png') + .waitForElementVisible('*[data-id="udappNotifyModalDialogModalBody-react"]', 60000) + .click('[data-id="udappNotify-modal-footer-cancel-react"]') + .saveScreenshot('./reports/screenshots/metamask_8.png') + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: "//span[@class='text-log' and contains(., 'errored')]" + }) + }, + 'Should deploy contract on Sepolia Test Network using MetaMask again #group1': function (browser: NightwatchBrowser) { + if (!checkBrowserIsChrome(browser)) return + browser.clearConsole().waitForElementPresent('*[data-id="runTabSelectAccount"] option', 45000) + .clickLaunchIcon('filePanel') + .openFile('Greet.sol') + .clickLaunchIcon('udapp') + .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') + .click('*[data-id="Deploy - transact (not payable)"]') + .pause(5000) + .clearConsole() + .perform((done) => { + browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => { + checkAlerts(browser) + browser + .maximizeWindow() + .hideMetaMaskPopup() + .waitForElementPresent('[data-testid="page-container-footer-next"]') + .click('[data-testid="page-container-footer-next"]') // approve the tx + .switchBrowserTab(0) // back to remix + .waitForElementContainsText('*[data-id="terminalJournal"]', 'view on etherscan', 60000) + .waitForElementContainsText('*[data-id="terminalJournal"]', 'from: 0x76a...2708f', 60000) + .perform(() => done()) + }) + }) + } +} + +const branch = process.env.CIRCLE_BRANCH; +const isMasterBranch = (branch === 'master' || branch === 'remix_beta'); + +if (!checkBrowserIsChrome(browser)) { + module.exports = {} +} else { + module.exports = { + ...(branch ? (isMasterBranch ? tests : {}) : tests), + }; +} + + + +const sources = [ + { + 'Greet.sol': { + content: + ` + pragma solidity ^0.8.0; + contract HelloWorld { + string public message; + + fallback () external { + message = 'Hello World!'; + } + + function greet(string memory _message) public { + message = _message; + } + }` + }, + 'faulty.sol': { + content: `// SPDX-License-Identifier: GPL-3.0 + + pragma solidity >=0.8.2 <0.9.0; + + contract Test { + error O_o(uint256); + constructor() { + revert O_o(block.timestamp); + } + }` + } + } +] diff --git a/apps/remix-ide-e2e/src/tests/providers.test.ts b/apps/remix-ide-e2e/src/tests/providers.test.ts index 7fb52593e2..862fed757b 100644 --- a/apps/remix-ide-e2e/src/tests/providers.test.ts +++ b/apps/remix-ide-e2e/src/tests/providers.test.ts @@ -3,54 +3,119 @@ import { NightwatchBrowser } from 'nightwatch' import init from '../helpers/init' module.exports = { + '@disabled': true, before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', false) }, - 'Should switch to ganache provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) { + 'Should switch to ganache provider, set a custom URL and fail to connect #group1': function (browser: NightwatchBrowser) { browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000) .clickLaunchIcon('udapp') .switchEnvironment('ganache-provider') .waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]') + .execute(() => { (document.querySelector('*[data-id="ganache-providerModalDialogModalBody-react"] input') as any).focus() - }, [], () => {}) + }, [], () => { }) .clearValue('*[data-id="ganache-providerModalDialogModalBody-react"] input') .setValue('*[data-id="ganache-providerModalDialogModalBody-react"] input', 'http://127.0.0.1:8084') .modalFooterOKClick('ganache-provider') - .waitForElementContainsText('*[data-id="ganache-providerModalDialogModalBody-react"]', 'Error while connecting to the provider') - .modalFooterOKClick('ganache-provider') - .waitForElementNotVisible('*[data-id="ganache-providerModalDialogModalBody-react"]') + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: "//span[@class='text-danger' and contains(., 'missing response')]" + }) + .waitForElementPresent({ selector: `[data-id="selected-provider-ganache-provider"]`, timeout: 5000 }) .pause(1000) }, - 'Should switch to ganache provider, use the default ganache URL and succeed to connect': function (browser: NightwatchBrowser) { - browser.switchEnvironment('ganache-provider') - .waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]') - .modalFooterOKClick('ganache-provider') - .waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (') + 'Should switch to ganache provider, use the default ganache URL and succeed to connect #group1': function (browser: NightwatchBrowser) { + browser + .switchEnvironment('vm-cancun') + .pause(2000) + .switchEnvironment('ganache-provider') + .waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]') + .modalFooterOKClick('ganache-provider') + .waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (') + .waitForElementVisible({ selector: `[data-id="selected-provider-ganache-provider"]`, timeout: 5000 }) + }, - 'Should switch to foundry provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) { + 'Should switch to foundry provider, set a custom URL and fail to connect #group1': function (browser: NightwatchBrowser) { browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000) - .switchEnvironment('foundry-provider') - .waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]') - .execute(() => { - (document.querySelector('*[data-id="foundry-providerModalDialogModalBody-react"] input') as any).focus() - }, [], () => {}) - .clearValue('*[data-id="foundry-providerModalDialogModalBody-react"] input') - .setValue('*[data-id="foundry-providerModalDialogModalBody-react"] input', 'http://127.0.0.1:8084') - .modalFooterOKClick('foundry-provider') - .waitForElementContainsText('*[data-id="foundry-providerModalDialogModalBody-react"]', 'Error while connecting to the provider') - .modalFooterOKClick('foundry-provider') - .waitForElementNotVisible('*[data-id="foundry-providerModalDialogModalBody-react"]') - .pause(1000) - -}, - 'Should switch to foundry provider, use the default foundry URL and succeed to connect': function (browser: NightwatchBrowser) { + .switchEnvironment('foundry-provider') + .waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]') + .execute(() => { + (document.querySelector('*[data-id="foundry-providerModalDialogModalBody-react"] input') as any).focus() + }, [], () => { }) + .clearValue('*[data-id="foundry-providerModalDialogModalBody-react"] input') + .setValue('*[data-id="foundry-providerModalDialogModalBody-react"] input', 'http://127.0.0.1:8084') + .modalFooterOKClick('foundry-provider') + .pause(1000) + + }, + 'Should switch to foundry provider, use the default foundry URL and succeed to connect #group1': !function (browser: NightwatchBrowser) { browser.switchEnvironment('foundry-provider') - .waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]') - .modalFooterOKClick('foundry-provider') - .waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (') + .waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]') + .modalFooterOKClick('foundry-provider') + .waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (') + }, + + 'Should switch to custom provider #group2': function (browser: NightwatchBrowser) { + browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000) + .clickLaunchIcon('udapp') + .switchEnvironment('ganache-provider') + .waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]') + + .execute(() => { + (document.querySelector('*[data-id="ganache-providerModalDialogModalBody-react"] input') as any).focus() + }, [], () => { }) + .clearValue('*[data-id="ganache-providerModalDialogModalBody-react"] input') + .setValue('*[data-id="ganache-providerModalDialogModalBody-react"] input', 'https://scroll-rpc.publicnode.com') + .modalFooterOKClick('ganache-provider') + .pause(100) + .waitForElementPresent({ selector: `[data-id="selected-provider-ganache-provider"]`, timeout: 5000 }) + .pause(1000) + }, + + 'execute script #group2': function (browser: NightwatchBrowser) { + browser.clickLaunchIcon('filePanel') + .addFile('testScript.ts', { content: testScript }) + .clearConsole() + .pause(10000) + .waitForElementVisible('*[data-id="play-editor"]') + .click('*[data-id="play-editor"]') + .waitForElementVisible({ + locateStrategy: 'xpath', + selector: "//span[@class='text-danger' and contains(., 'exceed maximum block range')]" + }) + .waitForElementPresent({ selector: `[data-id="selected-provider-ganache-provider"]`, timeout: 5000 }) } } + + +const testScript = ` +// Importing necessary libraries from Ethers.js for interaction with Ethereum blockchain. +import { ethers } from "hardhat"; + +// https://scroll-rpc.publicnode.com +async function main() { + // Setting up provider (RPC URL) to interact with your chosen Ethereum chain, + const [deployer] = await ethers.getSigners(); + + try{ + let provider; + if(!provider){ + provider=ethers.provider; + } + + const contractAddress = "0x2bC16Bf30435fd9B3A3E73Eb759176C77c28308D"; // Replace with your smart contract's address. + + // Retrieving all events of a specific kind from the blockchain + let logs = await provider.getLogs({address:contractAddress, fromBlock: '0x332f23',toBlock: '0x384410', topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']}); + console.log("Got Logs ",logs) + }catch(error){ + } + +} + +main()` \ No newline at end of file diff --git a/apps/remix-ide/src/app/providers/abstract-provider.tsx b/apps/remix-ide/src/app/providers/abstract-provider.tsx index b00efc3d0a..6c0d866265 100644 --- a/apps/remix-ide/src/app/providers/abstract-provider.tsx +++ b/apps/remix-ide/src/app/providers/abstract-provider.tsx @@ -26,8 +26,8 @@ export type RejectRequest = (error: JsonDataResult) => void export type SuccessRequest = (data: JsonDataResult) => void export interface IProvider { - options: {[id: string]: any} - init(): Promise<{[id: string]: any}> + options: { [id: string]: any } + init(): Promise<{ [id: string]: any }> body(): JSX.Element sendAsync(data: JsonDataRequest): Promise } @@ -38,7 +38,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider { defaultUrl: string connected: boolean nodeUrl: string - options: {[id: string]: any} = {} + options: { [id: string]: any } = {} constructor(profile, blockchain, defaultUrl) { super(profile) @@ -102,24 +102,16 @@ export abstract class AbstractProvider extends Plugin implements IProvider { sendAsync(data: JsonDataRequest): Promise { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { - if (!this.provider) return reject({ jsonrpc: '2.0', id: data.id, error: { message: 'provider node set', code: -32603 } } as JsonDataResult) + if (!this.provider) return reject({ jsonrpc: '2.0', id: data.id, error: { message: 'provider not set', code: -32603 } } as JsonDataResult) this.sendAsyncInternal(data, resolve, reject) }) } - private async switchAway(showError) { + private async switchAway(showError: boolean, msg: string) { if (!this.provider) return - this.provider = null - this.connected = false if (showError) { - const modalContent: AlertModal = { - id: this.profile.name, - title: this.profile.displayName, - message: `Error while connecting to the provider, provider not connected` - } - this.call('notification', 'alert', modalContent) + this.call('terminal', 'log', { type: 'error', value: 'Error while querying the provider: ' + msg }) } - await this.call('udapp', 'setEnvironmentMode', { context: 'vm-cancun' }) return } @@ -130,10 +122,22 @@ export abstract class AbstractProvider extends Plugin implements IProvider { resolve({ jsonrpc: '2.0', result, id: data.id }) } catch (error) { if (error && error.message && error.message.includes('SERVER_ERROR')) { - this.switchAway(true) + try { + // replace escaped quotes with normal quotes + const errorString = String(error.message).replace(/\\"/g, '"'); + const messageMatches = Array.from(errorString.matchAll(/"message":"(.*?)"/g)); + // Extract the message values + const messages = messageMatches.map(match => match[1]); + if (messages && messages.length > 0) { + this.switchAway(true, messages[0]) + } else { + this.switchAway(true, error.message ? error.message : error.error ? error.error : error) + } + } catch (error) { + this.switchAway(true, error.message ? error.message : error.error ? error.error : error) + } } - error.code = -32603 - reject({ jsonrpc: '2.0', error, id: data.id }) + reject({ jsonrpc: '2.0', error: { message: error.message, code: -32603 }, id: data.id }) } } else { const result = data.method === 'net_listening' ? 'canceled' : [] diff --git a/apps/remix-ide/src/app/tabs/web3-provider.js b/apps/remix-ide/src/app/tabs/web3-provider.js index 41bd9d3c27..39e5543c2e 100644 --- a/apps/remix-ide/src/app/tabs/web3-provider.js +++ b/apps/remix-ide/src/app/tabs/web3-provider.js @@ -86,7 +86,7 @@ export class Web3ProviderModule extends Plugin { try { resultFn(null, await provider.sendAsync(payload)) } catch (e) { - resultFn(e.error ? new Error(e.error) : new Error(e)) + resultFn(e.error ? e.error : e) } } else { reject(new Error('User denied permission')) diff --git a/apps/remix-ide/src/app/udapp/run-tab.tsx b/apps/remix-ide/src/app/udapp/run-tab.tsx index b513a76c2e..983629cb81 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.tsx +++ b/apps/remix-ide/src/app/udapp/run-tab.tsx @@ -194,22 +194,7 @@ export class RunTab extends ViewPlugin { if (options['fork']) this.fork = options['fork'] } }, - provider: { - sendAsync (payload) { - return udapp.call(name, 'sendAsync', payload) - }, - async request (payload) { - try { - const requestResult = await udapp.call(name, 'sendAsync', payload) - if (requestResult.error) { - throw new Error(requestResult.error.message) - } - return requestResult.result - } catch (err) { - throw new Error(err.message) - } - } - } + provider: new Provider(udapp, name) }) } @@ -302,3 +287,43 @@ export class RunTab extends ViewPlugin { this.addInstance(address, contractObject.abi, contractObject.name) } } + +class Provider { + udapp: RunTab + name: string + constructor(udapp, name) { + this.udapp = udapp + this.name = name + } + sendAsync (payload) { + return this.udapp.call(this.name, 'sendAsync', payload) + } + request (payload): Promise { + return new Promise((resolve, reject) => { + this.udapp.call(this.name, 'sendAsync', payload).then((response) => { + if (response.error) { + reject(response.error.message) + } else { + resolve(response) + } + }).catch((err) => { + if (typeof err === 'string') { + reject(err) + } else if (err.error && err.error.message) { + reject(err.error.message) + } else if (err.error && typeof err.error === 'string') { + reject(err.error) + } else { + let e + try { + e = JSON.stringify(err) + } catch (e) { + reject('unknown error') + return + } + reject(e) + } + }) + }) + } +} diff --git a/apps/remix-ide/src/blockchain/blockchain.tsx b/apps/remix-ide/src/blockchain/blockchain.tsx index 3690b3c122..6ccbaed7ac 100644 --- a/apps/remix-ide/src/blockchain/blockchain.tsx +++ b/apps/remix-ide/src/blockchain/blockchain.tsx @@ -247,7 +247,7 @@ export class Blockchain extends Plugin { args, (error, data) => { if (error) { - return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) + return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error.error ? error.error : error}`) } statusCb(`creation of ${selectedContract.name} pending...`) @@ -272,7 +272,7 @@ export class Blockchain extends Plugin { selectedContract.bytecodeLinkReferences, (error, data) => { if (error) { - return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) + return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error.error ? error.error : error}`) } statusCb(`creation of ${selectedContract.name} pending...`) @@ -485,7 +485,7 @@ export class Blockchain extends Plugin { this.runTx({ data: data, useCall: false }, confirmationCb, continueCb, promptCb, (error, txResult, address) => { if (error) { - return finalCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) + return finalCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error.error ? error.error : error}`) } if (txResult.receipt.status === false || txResult.receipt.status === '0x0' || txResult.receipt.status === 0) { return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) @@ -614,7 +614,7 @@ export class Blockchain extends Plugin { callType, (error, data) => { if (error) { - return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`) + return logCallback(`${logMsg} errored: ${error.message ? error.message : error.error ? error.error : error}`) } if (!lookupOnly) { logCallback(`${logMsg} pending ... `) @@ -631,7 +631,7 @@ export class Blockchain extends Plugin { const useCall = funABI.stateMutability === 'view' || funABI.stateMutability === 'pure' this.runTx({ to: address, data, useCall }, confirmationCb, continueCb, promptCb, (error, txResult, _address, returnValue) => { if (error) { - return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`) + return logCallback(`${logMsg} errored: ${error.message ? error.message : error.error ? error.error : error}`) } if (lookupOnly) { outputCb(returnValue) diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts index 0cdb0ee978..b2c46feff4 100644 --- a/libs/remix-lib/src/execution/txRunnerWeb3.ts +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -66,7 +66,7 @@ export class TxRunnerWeb3 { const res = await (this.getWeb3() as any).eth.personal.sendTransaction({ ...tx, value }, { checkRevertBeforeSending: false, ignoreGasPricing: true }) cb(null, res.transactionHash) } catch (e) { - console.log(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) + console.log(`Send transaction failed: ${e.message || e.error} . if you use an injected provider, please check it is properly unlocked. `) // in case the receipt is available, we consider that only the execution failed but the transaction went through. // So we don't consider this to be an error. if (e.receipt) cb(null, e.receipt.transactionHash) @@ -82,6 +82,10 @@ export class TxRunnerWeb3 { const res = await this.getWeb3().eth.sendTransaction(tx, null, { checkRevertBeforeSending: false, ignoreGasPricing: true }) cb(null, res.transactionHash) } catch (e) { + if (!e.message) e.message = '' + if (e.error) { + e.message = e.message + ' ' + e.error + } console.log(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) // in case the receipt is available, we consider that only the execution failed but the transaction went through. // So we don't consider this to be an error. 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 ab8de7ec34..9a3957eb8c 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -825,10 +825,6 @@ export const EditorUI = (props: EditorUIProps) => { label: intl.formatMessage({ id: 'editor.explainFunction' }), contextMenuOrder: 1, // choose the order contextMenuGroupId: 'gtp', // create a new grouping - keybindings: [ - // Keybinding for Ctrl + Shift + E - monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyMod.Shift | monacoRef.current.KeyCode.KeyE - ], run: async () => { const file = await props.plugin.call('fileManager', 'getCurrentFile') const context = await props.plugin.call('fileManager', 'readFile', file) 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 dd08d62a36..2c2ee360c4 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts @@ -119,10 +119,19 @@ const getConfirmationCb = (plugin: RunTab, dispatch: React.Dispatch, confir export const continueHandler = (dispatch: React.Dispatch, gasEstimationPrompt: (msg: string) => JSX.Element, error, continueTxExecution, cancelCb) => { if (error) { - let msg = typeof error !== 'string' ? error.message : error + let msg = '' + if (typeof error === 'string') { + msg = error + } if (error && error.innerError) { msg += '\n' + error.innerError } + if (error && error.message) { + msg += '\n' + error.message + } + if (error && error.error) { + msg += '\n' + error.error + } if (msg.includes('invalid opcode')) msg += '\nThe EVM version used by the selected environment is not compatible with the compiler EVM version.'