diff --git a/.env b/.env.local similarity index 65% rename from .env rename to .env.local index 1a2b47f98f..fb35ddc973 100644 --- a/.env +++ b/.env.local @@ -2,4 +2,4 @@ gist_token= account_passphrase= account_password= NODE_OPTIONS=--max-old-space-size=2048 -# WALLET_CONNECT_PROJECT_ID= +WALLET_CONNECT_PROJECT_ID= diff --git a/.gitignore b/.gitignore index 7955bf6455..ab74544426 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ soljson.js *_group*.ts stats.json release +.env # compiled output diff --git a/apps/remix-dapp/src/locales/en/udapp.json b/apps/remix-dapp/src/locales/en/udapp.json index 0d3952cc1a..8fbb32f619 100644 --- a/apps/remix-dapp/src/locales/en/udapp.json +++ b/apps/remix-dapp/src/locales/en/udapp.json @@ -50,12 +50,15 @@ "udapp.hash": "hash", "udapp.signature": "signature", "udapp.forkStateTitle": "Fork VM state", - "udapp.forkStateLabel": "State Name", + "udapp.forkStateLabel": "New environment name", + "udapp.forkVmStateDesc1":"Forking state will create a new environment with same state as selected environment", + "udapp.forkVmStateDesc2":"New environment will be pinned, which can be unpinned or deleted using Envionment Explorer", "udapp.fork": "Fork", - "udapp.deleteVmStateTitle": "Delete VM state", - "udapp.deleteVmStateDesc1": "Deleting the state of this VM will delete the associated transaction details in this Workspace.", - "udapp.deleteVmStateDesc2": "It will also delete the data of contracts deployed and pinned in this Workspace.", - "udapp.deleteVmStateDesc3": "Do you want to continue?", + "udapp.resetVmStateTitle": "Reset VM state", + "udapp.resetVmStateDesc1": "Resetting the state of this VM will delete the associated transaction details in this Workspace.", + "udapp.resetVmStateDesc2": "It will also delete the data of contracts deployed and pinned in this Workspace.", + "udapp.resetVmStateDesc3": "Do you want to continue?", + "udapp.reset": "Reset", "udapp.delete": "Delete", "udapp.injectedTitle": "Unfortunately it's not possible to create an account using injected provider. Please create the account directly from your provider (i.e metamask or other of the same type).", "udapp.createNewAccount": "Create a new account", diff --git a/apps/remix-ide-e2e/src/tests/remixd.test.ts b/apps/remix-ide-e2e/src/tests/remixd.test.ts index 31dc3c686b..6a0dedd582 100644 --- a/apps/remix-ide-e2e/src/tests/remixd.test.ts +++ b/apps/remix-ide-e2e/src/tests/remixd.test.ts @@ -527,7 +527,7 @@ async function installFoundry(): Promise { server.stdout.on('data', function (data) { console.log(data.toString()) if ( - data.toString().includes("foundryup: done!") + data.toString().includes("foundryup: use - chisel 0.3.0") ) { console.log('resolving') resolve() diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index 77bb44f548..e16676e181 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -195,7 +195,7 @@ module.exports = { .journalChildIncludes('inside getOwner', { shouldHaveOnlyOneOccurrence: true }) }, - 'Emit 2 similar events and check the filtering is done properly #group4': function (browser: NightwatchBrowser) { + 'Emit 2 similar events and check the filtering is done properly #group11': function (browser: NightwatchBrowser) { let addressRef: string browser .addFile('contracts/contract_with_event.sol', { content: contract_with_event }) @@ -204,7 +204,6 @@ module.exports = { .clickLaunchIcon('solidity') .click('*[data-id="compilerContainerCompileBtn"]') .clickLaunchIcon('udapp') - .click('*[data-id="deployAndRunClearInstances"]') .selectContract('Example') .createContract('') .getAddressAtPosition(0, (address) => { diff --git a/apps/remix-ide-e2e/src/tests/vm_state.test.ts b/apps/remix-ide-e2e/src/tests/vm_state.test.ts index 45dc3e979c..2c02750859 100644 --- a/apps/remix-ide-e2e/src/tests/vm_state.test.ts +++ b/apps/remix-ide-e2e/src/tests/vm_state.test.ts @@ -33,7 +33,7 @@ const tests = { .click('*[data-id="delete-state-icon"]') .waitForElementVisible( { - selector: "//*[@data-shared='tooltipPopup' and contains(.,'State not available to delete')]", + selector: "//*[@data-shared='tooltipPopup' and contains(.,'State not available to reset')]", locateStrategy: 'xpath' } ) @@ -65,7 +65,7 @@ const tests = { // check toaster for forked state .waitForElementVisible( { - selector: '//*[@data-shared="tooltipPopup" and contains(.,"VM state \'forkedState_1\' forked")]', + selector: '//*[@data-shared="tooltipPopup" and contains(.,"New environment \'forkedState_1\' created with forked state.")]', locateStrategy: 'xpath' } ) @@ -105,7 +105,7 @@ const tests = { .setValue('input[data-id="modalDialogForkState"]', 'forkedState_2') .modalFooterOKClick('udappNotify') .waitForElementVisible('*[data-shared="tooltipPopup"]', 10000) - .assert.textContains('*[data-shared="tooltipPopup"]', `VM state 'forkedState_2' forked and selected as current environment.`) + .assert.textContains('*[data-shared="tooltipPopup"]', `New environment 'forkedState_2' created with forked state.`) // check if 'forkedState_2' is selected as current environment .assert.elementPresent('*[data-id="selected-provider-vm-fs-forkedState_2"]') // check if 'forkedState_2' is present in environment explorer @@ -140,7 +140,7 @@ const tests = { .modalFooterOKClick('udappNotify') .waitForElementVisible('*[data-shared="tooltipPopup"]', 10000) // check if toaster is shown - .assert.textContains('*[data-shared="tooltipPopup"]', `VM state deleted successfully.`) + .assert.textContains('*[data-shared="tooltipPopup"]', `VM state reset successfully.`) // check that there are no instances .assert.textContains('*[data-id="deployedContractsBadge"]', '0') // check if state file is deleted diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index dfb2f651a3..5b3e70ae4a 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -26,7 +26,7 @@ const profile = { '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', 'copyFolderToJson', 'diff', - 'hasGitSubmodules' + 'hasGitSubmodules', 'getOpenedFiles' ], kind: 'file-system' } diff --git a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx index df1d15f6bd..14ba934f3b 100644 --- a/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx +++ b/apps/remix-ide/src/app/plugins/remixAIPlugin.tsx @@ -3,7 +3,7 @@ import { ViewPlugin } from '@remixproject/engine-web' import { Plugin } from '@remixproject/engine'; import { RemixAITab, ChatApi } from '@remix-ui/remix-ai' import React, { useCallback } from 'react'; -import { ICompletions, IModel, RemoteInferencer, IRemoteModel, IParams, GenerationParams, CodeExplainAgent } from '@remix/remix-ai-core'; +import { ICompletions, IModel, RemoteInferencer, IRemoteModel, IParams, GenerationParams, CodeExplainAgent, SecurityAgent } from '@remix/remix-ai-core'; import { CustomRemixApi } from '@remix-api' import { PluginViewWrapper } from '@remix-ui/helper' import { CodeCompletionAgent } from '@remix/remix-ai-core'; @@ -18,9 +18,8 @@ const profile = { displayName: 'RemixAI', methods: ['code_generation', 'code_completion', "solidity_answer", "code_explaining", - "code_insertion", "error_explaining", - "initialize", 'chatPipe', 'ProcessChatRequestBuffer', - 'isChatRequestPending'], + "code_insertion", "error_explaining", "vulnerability_check", + "initialize", 'chatPipe', 'ProcessChatRequestBuffer', 'isChatRequestPending'], events: [], icon: 'assets/img/remix-logo-blue.png', description: 'RemixAI provides AI services to Remix IDE.', @@ -39,7 +38,8 @@ export class RemixAIPlugin extends ViewPlugin { remoteInferencer:RemoteInferencer = null isInferencing: boolean = false chatRequestBuffer: chatRequestBufferT = null - agent: CodeExplainAgent + codeExpAgent: CodeExplainAgent + securityAgent: SecurityAgent useRemoteInferencer:boolean = false dispatch: any completionAgent: CodeCompletionAgent @@ -47,8 +47,8 @@ export class RemixAIPlugin extends ViewPlugin { constructor(inDesktop:boolean) { super(profile) this.isOnDesktop = inDesktop - this.agent = new CodeExplainAgent(this) - // user machine dont use resource for remote inferencing + this.codeExpAgent = new CodeExplainAgent(this) + // user machine dont use ressource for remote inferencing } onActivation(): void { @@ -70,6 +70,9 @@ export class RemixAIPlugin extends ViewPlugin { console.log('Indexing workspace') this.completionAgent.indexWorkspace() }, 60000) + + + this.securityAgent = new SecurityAgent(this) } async initialize(model1?:IModel, model2?:IModel, remoteModel?:IRemoteModel, useRemote?:boolean){ @@ -105,11 +108,6 @@ export class RemixAIPlugin extends ViewPlugin { } async code_generation(prompt: string): Promise { - if (this.isInferencing) { - this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) - return - } - if (this.isOnDesktop && !this.useRemoteInferencer) { return await this.call(this.remixDesktopPluginName, 'code_generation', prompt) } else { @@ -130,17 +128,8 @@ export class RemixAIPlugin extends ViewPlugin { } async solidity_answer(prompt: string, params: IParams=GenerationParams): Promise { - if (this.isInferencing) { - this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) - return - } - if (prompt.trimStart().startsWith('gpt') || prompt.trimStart().startsWith('sol-gpt')) { - params.terminal_output = true - params.stream_result = false - params.return_stream_response = false - } + const newPrompt = await this.codeExpAgent.chatCommand(prompt) - const newPrompt = await this.agent.chatCommand(prompt) let result if (this.isOnDesktop && !this.useRemoteInferencer) { result = await this.call(this.remixDesktopPluginName, 'solidity_answer', newPrompt) @@ -154,11 +143,6 @@ export class RemixAIPlugin extends ViewPlugin { } async code_explaining(prompt: string, context: string, params: IParams=GenerationParams): Promise { - if (this.isInferencing) { - this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) - return - } - let result if (this.isOnDesktop && !this.useRemoteInferencer) { result = await this.call(this.remixDesktopPluginName, 'code_explaining', prompt, context, params) @@ -171,11 +155,6 @@ export class RemixAIPlugin extends ViewPlugin { } async error_explaining(prompt: string, context: string="", params: IParams=GenerationParams): Promise { - if (this.isInferencing) { - this.call('terminal', 'log', { type: 'aitypewriterwarning', value: "RemixAI is already busy!" }) - return - } - let result if (this.isOnDesktop && !this.useRemoteInferencer) { result = await this.call(this.remixDesktopPluginName, 'error_explaining', prompt) @@ -186,6 +165,22 @@ export class RemixAIPlugin extends ViewPlugin { return result } + async vulnerability_check(prompt: string, params: IParams=GenerationParams): Promise { + let result + if (this.isOnDesktop && !this.useRemoteInferencer) { + result = await this.call(this.remixDesktopPluginName, 'vulnerability_check', prompt) + + } else { + result = await this.remoteInferencer.vulnerability_check(prompt, params) + } + if (result && params.terminal_output) this.call('terminal', 'log', { type: 'aitypewriterwarning', value: result }) + return result + } + + getVulnerabilityReport(file: string): any { + return this.securityAgent.getReport(file) + } + async code_insertion(msg_pfx: string, msg_sfx: string): Promise { if (this.completionAgent.indexer == null || this.completionAgent.indexer == undefined) await this.completionAgent.indexWorkspace() @@ -210,11 +205,12 @@ export class RemixAIPlugin extends ViewPlugin { if (fn === "code_explaining") ChatApi.composer.send("Explain the current code") else if (fn === "error_explaining") ChatApi.composer.send("Explain the error") else if (fn === "solidity_answer") ChatApi.composer.send("Answer the following question") - else console.log("chatRequestBuffer is not empty. First process the last request.") + else if (fn === "vulnerability_check") ChatApi.composer.send("Is there any vulnerability in the pasted code?") + else console.log("chatRequestBuffer function name not recognized.") } } else { - console.log("chatRequestBuffer is not empty. First process the last request.") + console.log("chatRequestBuffer is not empty. First process the last request.", this.chatRequestBuffer) } _paq.push(['trackEvent', 'ai', 'remixAI_chat', 'askFromTerminal']) } diff --git a/apps/remix-ide/src/app/providers/environment-explorer.tsx b/apps/remix-ide/src/app/providers/environment-explorer.tsx index a38c0d74d3..dab429a671 100644 --- a/apps/remix-ide/src/app/providers/environment-explorer.tsx +++ b/apps/remix-ide/src/app/providers/environment-explorer.tsx @@ -78,6 +78,15 @@ export class EnvironmentExplorer extends ViewPlugin { } } + async deleteForkedState (provider) { + const providerName = await this.call('blockchain', 'getProvider') + if (providerName !== provider.name) { + await this.call('fileManager', 'remove', `.states/forked_states/${provider.displayName}.json`) + await this.call('blockchain', 'removeProvider', provider.name) + this.call('notification', 'toast', `Environment "${provider.displayName}" deleted successfully.`) + } else this.call('notification', 'toast', 'Cannot delete the current selected environment') + } + renderComponent() { this.dispatch({ ...this.state @@ -86,7 +95,12 @@ export class EnvironmentExplorer extends ViewPlugin { updateComponent(state: EnvironmentExplorerState) { return (<> - + ) } } diff --git a/apps/remix-ide/src/app/providers/style/environment-explorer.css b/apps/remix-ide/src/app/providers/style/environment-explorer.css index b5770fba42..8e69456504 100644 --- a/apps/remix-ide/src/app/providers/style/environment-explorer.css +++ b/apps/remix-ide/src/app/providers/style/environment-explorer.css @@ -1,5 +1,5 @@ .EECellStyle { - min-height: 6rem; + min-height: 8rem; max-width: 12rem; - min-width: 10rem; + min-width: 12rem; } diff --git a/apps/remix-ide/src/app/tabs/locale-module.js b/apps/remix-ide/src/app/tabs/locale-module.js index f4b3252df1..5ce5048ab1 100644 --- a/apps/remix-ide/src/app/tabs/locale-module.js +++ b/apps/remix-ide/src/app/tabs/locale-module.js @@ -77,6 +77,7 @@ export class LocaleModule extends Plugin { const next = localeCode || this.active // Name if (next === this.active) return // --> exit out of this method _paq.push(['trackEvent', 'localeModule', 'switchTo', next]) + const nextLocale = this.locales[next] // Locale if (!this.forced) this._deps.config.set('settings/locale', next) diff --git a/apps/remix-ide/src/app/tabs/locales/en/editor.json b/apps/remix-ide/src/app/tabs/locales/en/editor.json index 6f0f367555..b5a06a18ad 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/editor.json +++ b/apps/remix-ide/src/app/tabs/locales/en/editor.json @@ -28,6 +28,7 @@ "editor.explainFunctionByAI": "```\n{content}\n```\nExplain the function {currentFunction}", "editor.explainFunctionByAISol": "```\n{content}\n```\nExplain the function {currentFunction}", "editor.ExplainPipeMessage": "```\n {content}\n```\nExplain the snipped above", + "editor.PastedCodeSafety": "```\n {content}\n```\n\nReply in a short manner: Does this code contain major security vulnerabilities leading to a scam or loss of funds?", "editor.executeFreeFunction": "Run a free function", "editor.executeFreeFunction2": "Run the free function \"{name}\"", "editor.toastText1": "This can only execute free function", 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 00e49554fb..c4a376cd20 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/en/udapp.json @@ -50,12 +50,15 @@ "udapp.hash": "hash", "udapp.signature": "signature", "udapp.forkStateTitle": "Fork VM state", - "udapp.forkStateLabel": "State Name", + "udapp.forkStateLabel": "New environment name", + "udapp.forkVmStateDesc1":"Forking state will create a new environment with same state as selected environment", + "udapp.forkVmStateDesc2":"After forking, new environment will be pinned and selected automatically. It can be unpinned or deleted using Envionment Explorer", "udapp.fork": "Fork", - "udapp.deleteVmStateTitle": "Delete VM state", - "udapp.deleteVmStateDesc1": "Deleting the state of this VM will delete the associated transaction details in this Workspace.", - "udapp.deleteVmStateDesc2": "It will also delete the data of contracts deployed and pinned in this Workspace.", - "udapp.deleteVmStateDesc3": "Do you want to continue?", + "udapp.resetVmStateTitle": "Reset VM state", + "udapp.resetVmStateDesc1": "Resetting the state of this VM will delete the associated transaction details in this Workspace.", + "udapp.resetVmStateDesc2": "It will also delete the data of contracts deployed and pinned in this Workspace.", + "udapp.resetVmStateDesc3": "Do you want to continue?", + "udapp.reset": "Reset", "udapp.delete": "Delete", "udapp.injectedTitle": "Unfortunately it's not possible to create an account using injected provider. Please create the account directly from your provider (i.e metamask or other of the same type).", "udapp.createNewAccount": "Create new account", diff --git a/apps/remix-ide/src/app/tabs/script-runner-ui.tsx b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx index 7e108c71ca..dd79bc79e5 100644 --- a/apps/remix-ide/src/app/tabs/script-runner-ui.tsx +++ b/apps/remix-ide/src/app/tabs/script-runner-ui.tsx @@ -71,7 +71,6 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { }) this.plugin.on('fileManager', 'fileSaved', async (file: string) => { - if (file === configFileName && this.enableCustomScriptRunner) { await this.loadCustomConfig() this.renderComponent() @@ -114,7 +113,8 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { activateCustomScriptRunner={this.activateCustomScriptRunner.bind(this)} saveCustomConfig={this.saveCustomConfig.bind(this)} openCustomConfig={this.openCustomConfig.bind(this)} - loadScriptRunner={this.selectScriptRunner.bind(this)} /> + loadScriptRunner={this.selectScriptRunner.bind(this)} + /> ) } @@ -184,7 +184,6 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { this.setIsLoading(config.name, false) this.renderComponent() return result - } async execute(script: string, filePath: string) { @@ -289,7 +288,6 @@ export class ScriptRunnerUIPlugin extends ViewPlugin { } } } - } async openCustomConfig() { diff --git a/apps/remix-ide/src/app/tabs/theme-module.js b/apps/remix-ide/src/app/tabs/theme-module.js index 53bb7f542d..fb9cfd6cff 100644 --- a/apps/remix-ide/src/app/tabs/theme-module.js +++ b/apps/remix-ide/src/app/tabs/theme-module.js @@ -118,7 +118,7 @@ export class ThemeModule extends Plugin { } const next = themeName || this.active // Name if (next === this.active) return // --> exit out of this method - _paq.push(['trackEvent', 'themeModule', 'switchTo', next]) + _paq.push(['trackEvent', 'themeModule', 'switchThemeTo', next]) const nextTheme = this.themes[next] // Theme if (!this.forced) this._deps.config.set('settings/theme', next) document.getElementById('theme-link') ? document.getElementById('theme-link').remove() : null diff --git a/apps/remix-ide/src/blockchain/blockchain.tsx b/apps/remix-ide/src/blockchain/blockchain.tsx index d1c0d478f0..ffca9e766b 100644 --- a/apps/remix-ide/src/blockchain/blockchain.tsx +++ b/apps/remix-ide/src/blockchain/blockchain.tsx @@ -157,7 +157,7 @@ export class Blockchain extends Plugin { _paq.push(['trackEvent', 'blockchain', 'providerPinned', name]) this.emit('providersChanged') this.changeExecutionContext({ context: name }, null, null, null) - this.call('notification', 'toast', `VM state '${providerName}' forked and selected as current environment.`) + this.call('notification', 'toast', `New environment '${providerName}' created with forked state.`) }) this.on('environmentExplorer', 'providerUnpinned', (name, provider) => { @@ -687,6 +687,7 @@ export class Blockchain extends Plugin { } removeProvider(name) { + if (this.pinnedProviders.includes(name)) this.emit('shouldRemoveProviderFromUdapp', name, this.getProviderObjByName(name)) this.executionContext.removeProvider(name) this.emit('providersChanged') } diff --git a/apps/remixdesktop/src/lib/InferenceServerManager.ts b/apps/remixdesktop/src/lib/InferenceServerManager.ts index 4587e822db..ffd6342bca 100644 --- a/apps/remixdesktop/src/lib/InferenceServerManager.ts +++ b/apps/remixdesktop/src/lib/InferenceServerManager.ts @@ -6,7 +6,7 @@ import { EventEmitter } from 'events'; import { ICompletions, IModel, IParams, InsertionParams, CompletionParams, GenerationParams, ModelType, AIRequestType, IStreamResponse, ChatHistory, downloadLatestReleaseExecutable, - buildSolgptPromt } from "@remix/remix-ai-core" + buildSolgptPrompt } from "@remix/remix-ai-core" import { platform } from 'os'; class ServerStatusTimer { @@ -517,7 +517,7 @@ export class InferenceManager implements ICompletions { modelOP = model.modelOP } } - const prompt = buildSolgptPromt(userPrompt, modelOP) + const prompt = buildSolgptPrompt(userPrompt, modelOP) if (params.stream_result) { return this._streamInferenceRequest('solidity_answer', { prompt, ...params }) diff --git a/apps/remixdesktop/test/tests/app/foundry.test.ts b/apps/remixdesktop/test/tests/app/foundry.test.ts index 3aa370882a..f4d2615f93 100644 --- a/apps/remixdesktop/test/tests/app/foundry.test.ts +++ b/apps/remixdesktop/test/tests/app/foundry.test.ts @@ -89,7 +89,7 @@ async function installFoundry(): Promise { server.stdout.on('data', function (data) { console.log(data.toString()) if ( - data.toString().includes("foundryup: done!") + data.toString().includes("foundryup: use - chisel 0.3.0") ) { console.log('resolving') resolve() diff --git a/apps/walletconnect/webpack.config.js b/apps/walletconnect/webpack.config.js index c1ae621262..8da8a91246 100644 --- a/apps/walletconnect/webpack.config.js +++ b/apps/walletconnect/webpack.config.js @@ -46,7 +46,7 @@ module.exports = composePlugins(withNx(), (config) => { process: 'process/browser' }) ) - + console.log(process.env.WALLET_CONNECT_PROJECT_ID) // set the define plugin to load the WALLET_CONNECT_PROJECT_ID config.plugins.push( new webpack.DefinePlugin({ diff --git a/libs/ghaction-helper/package.json b/libs/ghaction-helper/package.json index e5f1e55389..3db00ed619 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.42", + "version": "0.1.43", "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.48", + "@remix-project/remix-solidity": "^0.5.49", "@types/chai": "^4.3.4", "typescript": "^4.9.3" }, "dependencies": { "@ethereum-waffle/chai": "^3.4.4", - "@remix-project/remix-simulator": "^0.2.62", + "@remix-project/remix-simulator": "^0.2.63", "chai": "^4.3.7", "ethers": "^5.7.2", "web3": "^4.1.1" }, "types": "./src/index.d.ts", - "gitHead": "baa8abc9b912288b7a2546bedc717a9229f652c4" + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551" } \ No newline at end of file diff --git a/libs/remix-ai-core/src/agents/securityAgent.ts b/libs/remix-ai-core/src/agents/securityAgent.ts index 9af6723f0e..a11f4408f3 100644 --- a/libs/remix-ai-core/src/agents/securityAgent.ts +++ b/libs/remix-ai-core/src/agents/securityAgent.ts @@ -1,28 +1,189 @@ // security checks import * as fs from 'fs'; -class SecurityAgent { - private codebase: string[]; // list of codebase files +interface SecurityReport { + compiled: boolean; + vulnerabilities: string[]; + isNotSafe: string; + fileName: string; + reportTimestamp: string; + recommendations: string[]; + fileModifiedSinceLastReport: boolean; + hasPastedCode: boolean; +} + +class WorkspaceWatcher { + private intervalId: NodeJS.Timeout | null = null; + public interval: number; + private task: () => void; + + constructor(task: () => void, interval: number) { + this.task = task; + this.interval = interval; + } + + start(): void { + if (this.intervalId === null) { + this.intervalId = setInterval(() => { + this.task(); + }, this.interval); + } + } + + stop(): void { + if (this.intervalId !== null) { + clearInterval(this.intervalId); + this.intervalId = null; + } + } + + isRunning(): boolean { + return this.intervalId !== null; + } +} + +export class SecurityAgent { public currentFile: string; + public openedFiles: any; + private basePlugin: any; + private watcher: WorkspaceWatcher; + public reports: SecurityReport[] = []; + + constructor(plugin) { + this.basePlugin = plugin; + + this.basePlugin.on('fileManager', 'fileAdded', (path) => { }); + this.basePlugin.on('fileManager', 'fileChanged', (path) => { //this.modifiedFile(path) + }); + + this.basePlugin.on('fileManager', 'fileRemoved', (path) => { this.removeFileFromReport(path) }); + this.basePlugin.on('fileManager', 'fileRenamed', (oldName, newName) => { + this.removeFileFromReport(oldName); + this.addFileToReport(newName); + }); + + // disable for now the compilationFinished event + // this.basePlugin.on('solidity', 'compilationFinished', async (fileName, source, languageVersion, data) => { this.onCompilationFinished(fileName) }); + // this.basePlugin.on('vyper', 'compilationFinished', async (fileName, source, languageVersion, data) => { this.onCompilationFinished(fileName) }); + // this.basePlugin.on('hardhat', 'compilationFinished', async (fileName, source, languageVersion, data) => { this.onCompilationFinished(fileName) }); + // this.basePlugin.on('foundry', 'compilationFinished', async (fileName, source, languageVersion, data) => { this.onCompilationFinished(fileName) }); + + this.watcher = new WorkspaceWatcher(async () => { + try { + this.currentFile = await this.basePlugin.call('fileManager', 'getCurrentFile'); + this.openedFiles = await this.basePlugin.call('fileManager', 'getOpenedFiles'); + + Object.keys(this.openedFiles).forEach(key => { + this.addFileToReport(this.openedFiles[key]); + }); + } catch (error) { + // no file selected or opened currently + } + }, 10000); + this.watcher.start(); + } + + addFileToReport(file: string): void { + const report = this.reports.find((r) => r.fileName === file); + if (report) { + // nothing to do + } else { + this.reports.push({ + compiled: false, + isNotSafe: 'No', + vulnerabilities: [], + fileName: file, + reportTimestamp: null, + recommendations: [], + fileModifiedSinceLastReport: false, + hasPastedCode: false + }); + } - constructor(codebasePath: string) { - // git or fs - this.codebase = this.loadCodebase(codebasePath); } - private loadCodebase(path: string): string[] { - const files = fs.readdirSync(path); - return files - .filter(file => file.endsWith('.ts')) - .flatMap(file => fs.readFileSync(`${path}/${file}`, 'utf-8').split('\n')); + async onCompilationFinished(file: string) { + let report = this.reports.find((r) => r.fileName === file); + if (report) { + report.compiled = true; + report.fileModifiedSinceLastReport = false; + } else { + report = { + compiled: true, + isNotSafe: 'No', + vulnerabilities: [], + fileName: file, + reportTimestamp: null, + recommendations: [], + fileModifiedSinceLastReport: false, + hasPastedCode: false + } + this.reports.push(report); + } + + try { + this.processFile(file); + console.log('Checking for vulnerabilities after compilation', this.reports); + } catch (error) { + console.error('Error checking for vulnerabilities after compilation: ', error); + } + + // check for security vulnerabilities } - public update(currentFile, lineNumber){ + removeFileFromReport(file: string): void { + const index = this.reports.findIndex((r) => r.fileName === file); + if (index !== -1) { + this.reports.splice(index, 1); + } + } + + modifiedFile(file: string): void { + const report = this.reports.find((r) => r.fileName === file); + if (report) { + report.fileModifiedSinceLastReport = true; + } + } + + async processFile(file: string) { + try { + const report = this.reports.find((r) => r.fileName === file); + if (report) { } + else { + this.reports.push({ + compiled: false, + isNotSafe: 'No', + vulnerabilities: [], + fileName: file, + reportTimestamp: null, + recommendations: [], + fileModifiedSinceLastReport: false, + hasPastedCode: false + }); + } + + if (!report.reportTimestamp || report.fileModifiedSinceLastReport) { + const content = await this.basePlugin.call('fileManager', 'getFile', file); + const prompt = "```\n" + content + "\n```\n\nReply in a short manner: Does this code contain major security vulnerabilities leading to a scam or loss of funds?" + + let result = await this.basePlugin.call('remixAI', 'vulnerability_check', prompt) + result = JSON.parse(result); + report.vulnerabilities = result.Reason; + report.recommendations = result.Suggestion; + report.isNotSafe = result.Answer; + report.reportTimestamp = new Date().toISOString(); + } + + } catch (error) { + console.error('Error processing file: ', error); + } + } + getReport(file: string): SecurityReport { + return this.reports.find((r) => r.fileName === file); } public getRecommendations(currentLine: string, numSuggestions: number = 3): string[] { - // process the codebase highlighting security vulnerabilities and deliver recommendations const suggestions: string[] = []; return suggestions; } diff --git a/libs/remix-ai-core/src/helpers/streamHandler.ts b/libs/remix-ai-core/src/helpers/streamHandler.ts index d2e18624b6..5d58519568 100644 --- a/libs/remix-ai-core/src/helpers/streamHandler.ts +++ b/libs/remix-ai-core/src/helpers/streamHandler.ts @@ -46,6 +46,7 @@ export const HandleStreamResponse = async (streamResponse, } catch (error) { console.error('Error parsing JSON:', error); + return { 'generateText': 'Try again!', 'isGenerating': false } } } if (done_cb) { @@ -54,7 +55,7 @@ export const HandleStreamResponse = async (streamResponse, } catch (error) { console.error('Error parsing JSON:', error); - return { 'generateText': '', 'isGenerating': false } + return { 'generateText': 'Try again!', 'isGenerating': false } } } diff --git a/libs/remix-ai-core/src/index.ts b/libs/remix-ai-core/src/index.ts index 1b06d57138..d24ac6abae 100644 --- a/libs/remix-ai-core/src/index.ts +++ b/libs/remix-ai-core/src/index.ts @@ -6,7 +6,7 @@ import { IModel, IModelResponse, IModelRequest, InferenceModel, ICompletions, import { ModelType } from './types/constants' import { DefaultModels, InsertionParams, CompletionParams, GenerationParams } from './types/models' import { getCompletionPrompt, getInsertionPrompt } from './prompts/completionPrompts' -import { buildSolgptPromt, PromptBuilder } from './prompts/promptBuilder' +import { buildSolgptPrompt, PromptBuilder } from './prompts/promptBuilder' import { RemoteInferencer } from './inferencers/remote/remoteInference' import { ChatHistory } from './prompts/chat' import { downloadLatestReleaseExecutable } from './helpers/inferenceServerReleases' @@ -14,7 +14,7 @@ import { downloadLatestReleaseExecutable } from './helpers/inferenceServerReleas export { IModel, IModelResponse, IModelRequest, InferenceModel, ModelType, DefaultModels, ICompletions, IParams, IRemoteModel, - getCompletionPrompt, getInsertionPrompt, IStreamResponse, buildSolgptPromt, + getCompletionPrompt, getInsertionPrompt, IStreamResponse, buildSolgptPrompt, RemoteInferencer, InsertionParams, CompletionParams, GenerationParams, ChatEntry, AIRequestType, RemoteBackendOPModel, ChatHistory, downloadLatestReleaseExecutable } @@ -22,4 +22,5 @@ export { export * from './types/types' export * from './helpers/streamHandler' export * from './agents/codeExplainAgent' -export * from './agents/completionAgent' \ No newline at end of file +export * from './agents/completionAgent' +export * from './agents/securityAgent' diff --git a/libs/remix-ai-core/src/inferencers/remote/remoteInference.ts b/libs/remix-ai-core/src/inferencers/remote/remoteInference.ts index 430c5ec348..a8fa702fc7 100644 --- a/libs/remix-ai-core/src/inferencers/remote/remoteInference.ts +++ b/libs/remix-ai-core/src/inferencers/remote/remoteInference.ts @@ -1,6 +1,6 @@ import { ICompletions, IParams, AIRequestType, RemoteBackendOPModel, JsonStreamParser } from "../../types/types"; import { GenerationParams, CompletionParams, InsertionParams } from "../../types/models"; -import { buildSolgptPromt } from "../../prompts/promptBuilder"; +import { buildSolgptPrompt } from "../../prompts/promptBuilder"; import EventEmitter from "events"; import { ChatHistory } from "../../prompts/chat"; import axios from 'axios'; @@ -129,7 +129,7 @@ export class RemoteInferencer implements ICompletions { } async solidity_answer(prompt, options:IParams=GenerationParams): Promise { - const main_prompt = buildSolgptPromt(prompt, this.model_op) + const main_prompt = buildSolgptPrompt(prompt, this.model_op) const payload = { 'prompt': main_prompt, "endpoint":"solidity_answer", ...options } if (options.stream_result) return this._streamInferenceRequest(payload, AIRequestType.GENERAL) else return this._makeRequest(payload, AIRequestType.GENERAL) diff --git a/libs/remix-ai-core/src/prompts/promptBuilder.ts b/libs/remix-ai-core/src/prompts/promptBuilder.ts index fea867e36c..e22088bc6a 100644 --- a/libs/remix-ai-core/src/prompts/promptBuilder.ts +++ b/libs/remix-ai-core/src/prompts/promptBuilder.ts @@ -7,7 +7,7 @@ export const PromptBuilder = (inst, answr, modelop) => { if (modelop === RemoteBackendOPModel.MISTRAL) return "" } -export const buildSolgptPromt = (userPrompt:string, modelOP:RemoteBackendOPModel) => { +export const buildSolgptPrompt = (userPrompt:string, modelOP:RemoteBackendOPModel) => { if (modelOP === undefined) { console.log('WARNING: modelOP is undefined. Provide a valid model OP for chat history') return userPrompt diff --git a/libs/remix-analyzer/package.json b/libs/remix-analyzer/package.json index 0910832f0a..9bda03e259 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.71", + "version": "0.5.72", "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" @@ -25,8 +25,8 @@ "@ethereumjs/tx": "5.4.0", "@ethereumjs/util": "9.1.0", "@ethereumjs/vm": "8.1.1", - "@remix-project/remix-astwalker": "^0.0.92", - "@remix-project/remix-lib": "^0.5.69", + "@remix-project/remix-astwalker": "^0.0.93", + "@remix-project/remix-lib": "^0.5.70", "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": "baa8abc9b912288b7a2546bedc717a9229f652c4", + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551", "main": "./src/index.js" } \ No newline at end of file diff --git a/libs/remix-api/src/lib/plugins/remixAIDesktop-api.ts b/libs/remix-api/src/lib/plugins/remixAIDesktop-api.ts index 91b13c4bac..9e4cdd4716 100644 --- a/libs/remix-api/src/lib/plugins/remixAIDesktop-api.ts +++ b/libs/remix-api/src/lib/plugins/remixAIDesktop-api.ts @@ -10,7 +10,7 @@ export interface IRemixAID { } & StatusEvents, methods: { - code_completion(context: string): Promise + code_completion(context: string): Promise code_insertion(msg_pfx: string, msg_sfx: string): Promise, code_generation(prompt: string): Promise, code_explaining(code: string, context?: string): Promise, diff --git a/libs/remix-astwalker/package.json b/libs/remix-astwalker/package.json index 9382a9003d..9d24820510 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.92", + "version": "0.0.93", "description": "Tool to walk through Solidity AST", "main": "src/index.js", "scripts": { @@ -37,7 +37,7 @@ "@ethereumjs/tx": "5.4.0", "@ethereumjs/util": "9.1.0", "@ethereumjs/vm": "8.1.1", - "@remix-project/remix-lib": "^0.5.69", + "@remix-project/remix-lib": "^0.5.70", "@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": "baa8abc9b912288b7a2546bedc717a9229f652c4", + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-debug/package.json b/libs/remix-debug/package.json index 2c385f3ec3..7dc8e0c507 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.62", + "version": "0.5.63", "description": "Tool to debug Ethereum transactions", "contributors": [ { @@ -26,10 +26,10 @@ "@ethereumjs/tx": "5.4.0", "@ethereumjs/util": "9.1.0", "@ethereumjs/vm": "8.1.1", - "@remix-project/remix-astwalker": "^0.0.92", - "@remix-project/remix-lib": "^0.5.69", - "@remix-project/remix-simulator": "^0.2.62", - "@remix-project/remix-solidity": "^0.5.48", + "@remix-project/remix-astwalker": "^0.0.93", + "@remix-project/remix-lib": "^0.5.70", + "@remix-project/remix-simulator": "^0.2.63", + "@remix-project/remix-solidity": "^0.5.49", "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": "baa8abc9b912288b7a2546bedc717a9229f652c4", + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-lib/package.json b/libs/remix-lib/package.json index 101fef7923..6a0e2b3484 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.69", + "version": "0.5.70", "description": "Library to various Remix tools", "contributors": [ { @@ -55,6 +55,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme", "typings": "src/index.d.ts", - "gitHead": "baa8abc9b912288b7a2546bedc717a9229f652c4", + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-lib/src/execution/txRunner.ts b/libs/remix-lib/src/execution/txRunner.ts index ee2ee5ed2d..e875ff6713 100644 --- a/libs/remix-lib/src/execution/txRunner.ts +++ b/libs/remix-lib/src/execution/txRunner.ts @@ -16,7 +16,7 @@ export type Transaction = { export class TxRunner { event pendingTxs - queusTxs + queueTxs opt internalRunner constructor (internalRunner, opt) { @@ -25,7 +25,7 @@ export class TxRunner { this.event = new EventManager() this.pendingTxs = {} - this.queusTxs = [] + this.queueTxs = [] } rawRun (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, cb) { @@ -42,14 +42,14 @@ export class TxRunner { function run (self, tx: Transaction, stamp, confirmationCb, gasEstimationForceSend = null, promptCb = null, callback = null) { if (Object.keys(self.pendingTxs).length) { - return self.queusTxs.push({ tx, stamp, confirmationCb, gasEstimationForceSend, promptCb, callback }) + return self.queueTxs.push({ tx, stamp, confirmationCb, gasEstimationForceSend, promptCb, callback }) } self.pendingTxs[stamp] = tx self.execute(tx, confirmationCb, gasEstimationForceSend, promptCb, function (error, result) { delete self.pendingTxs[stamp] if (callback && typeof callback === 'function') callback(error, result) - if (self.queusTxs.length) { - const next = self.queusTxs.pop() + if (self.queueTxs.length) { + const next = self.queueTxs.pop() run(self, next.tx, next.stamp, next.confirmationCb, next.gasEstimationForceSend, next.promptCb, next.callback) } }) diff --git a/libs/remix-lib/src/execution/txRunnerVM.ts b/libs/remix-lib/src/execution/txRunnerVM.ts index 12bfd3636e..f326c9dc8e 100644 --- a/libs/remix-lib/src/execution/txRunnerVM.ts +++ b/libs/remix-lib/src/execution/txRunnerVM.ts @@ -24,7 +24,7 @@ export class TxRunnerVM { blockNumber pendingTxs vmaccounts - queusTxs + queueTxs blocks: Uint8Array[] logsManager commonContext @@ -41,7 +41,7 @@ export class TxRunnerVM { this.commonContext = this.getVMObject().common this.pendingTxs = {} this.vmaccounts = vmaccounts - this.queusTxs = [] + this.queueTxs = [] /* 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) diff --git a/libs/remix-simulator/package.json b/libs/remix-simulator/package.json index 6151383671..e1ce6bfc72 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.62", + "version": "0.2.63", "description": "Ethereum IDE and tools for the web", "contributors": [ { @@ -23,7 +23,7 @@ "@ethereumjs/util": "9.1.0", "@ethereumjs/vm": "8.1.1", "@metamask/eth-sig-util": "^7.0.2", - "@remix-project/remix-lib": "^0.5.69", + "@remix-project/remix-lib": "^0.5.70", "ansi-gray": "^0.1.1", "async": "^3.1.0", "body-parser": "^1.18.2", @@ -71,6 +71,6 @@ }, "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme", "typings": "src/index.d.ts", - "gitHead": "baa8abc9b912288b7a2546bedc717a9229f652c4", + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551", "types": "./src/index.d.ts" } \ No newline at end of file diff --git a/libs/remix-simulator/src/provider.ts b/libs/remix-simulator/src/provider.ts index 8fae322c6b..a9be59dacb 100644 --- a/libs/remix-simulator/src/provider.ts +++ b/libs/remix-simulator/src/provider.ts @@ -54,7 +54,6 @@ export class Provider { pendingRequests: Array constructor (options: ProviderOptions = {} as ProviderOptions) { - console.log(options) this.options = options this.connected = true this.vmContext = new VMContext(options['fork'], options['nodeUrl'], options['blockNumber'], options['stateDb'], options['blocks']) diff --git a/libs/remix-solidity/package.json b/libs/remix-solidity/package.json index 9f8170c08b..be9838cb9b 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.48", + "version": "0.5.49", "description": "Tool to load and run Solidity compiler", "main": "src/index.js", "types": "src/index.d.ts", @@ -19,7 +19,7 @@ "@ethereumjs/tx": "5.4.0", "@ethereumjs/util": "9.1.0", "@ethereumjs/vm": "8.1.1", - "@remix-project/remix-lib": "^0.5.69", + "@remix-project/remix-lib": "^0.5.70", "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": "baa8abc9b912288b7a2546bedc717a9229f652c4" + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551" } \ No newline at end of file diff --git a/libs/remix-tests/package.json b/libs/remix-tests/package.json index 5c7edc1e42..cab15ebaa0 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.62", + "version": "0.2.63", "description": "Tool to test Solidity smart contracts", "main": "src/index.js", "types": "./src/index.d.ts", @@ -41,9 +41,9 @@ "@ethereumjs/tx": "5.4.0", "@ethereumjs/util": "9.1.0", "@ethereumjs/vm": "8.1.1", - "@remix-project/remix-lib": "^0.5.69", - "@remix-project/remix-simulator": "^0.2.62", - "@remix-project/remix-solidity": "^0.5.48", + "@remix-project/remix-lib": "^0.5.70", + "@remix-project/remix-simulator": "^0.2.63", + "@remix-project/remix-solidity": "^0.5.49", "@remix-project/remix-url-resolver": "^0.0.42", "ansi-gray": "^0.1.1", "async": "^2.6.0", @@ -89,5 +89,5 @@ "@ethereumjs/trie": "6.2.1" }, "typings": "src/index.d.ts", - "gitHead": "baa8abc9b912288b7a2546bedc717a9229f652c4" + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551" } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts b/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts index 674d069f1c..5f15022b10 100644 --- a/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts @@ -9,7 +9,7 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli props: EditorUIProps monaco: any completionEnabled: boolean - task: string + task: string = 'code_completion' currentCompletion: any private lastRequestTime: number = 0; private readonly minRequestInterval: number = 200; @@ -60,13 +60,6 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli endColumn: getTextAtLine(model.getLineCount()).length + 1, }); - if (!word.endsWith(' ') && - !word.endsWith('.') && - !word.endsWith('"') && - !word.endsWith('(')) { - return; - } - try { const split = word.split('\n') if (split.length < 2) return 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 c9f2c37da7..e330efb78a 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -2,7 +2,7 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint import { FormattedMessage, useIntl } from 'react-intl' import { isArray } from 'lodash' import Editor, { DiffEditor, loader, Monaco } from '@monaco-editor/react' -import { AlertModal } from '@remix-ui/app' +import { AppModal } from '@remix-ui/app' import { ConsoleLogs, QueryParams } from '@remix-project/remix-lib' import { reducerActions, reducerListener, initialState } from './actions/editor' import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity' @@ -664,11 +664,26 @@ export const EditorUI = (props: EditorUIProps) => { } }) - editor.onDidPaste((e) => { + editor.onDidPaste(async (e) => { if (!pasteCodeRef.current && e && e.range && e.range.startLineNumber >= 0 && e.range.endLineNumber >= 0 && e.range.endLineNumber - e.range.startLineNumber > 10) { - const modalContent: AlertModal = { + // get the file name + const pastedCode = editor.getModel().getValueInRange(e.range) + const pastedCodePrompt = intl.formatMessage({ id: 'editor.PastedCodeSafety' }, { content:pastedCode }) + + const modalContent: AppModal = { id: 'newCodePasted', - title: intl.formatMessage({ id: 'editor.title1' }), + title: "New code pasted", + okLabel: 'Ask RemixAI', + cancelLabel: 'Close', + cancelFn: () => {}, + okFn: async () => { + await props.plugin.call('popupPanel', 'showPopupPanel', true) + setTimeout(async () => { + props.plugin.call('remixAI', 'chatPipe', 'vulnerability_check', pastedCodePrompt) + }, 500) + // add matamo event + _paq.push(['trackEvent', 'ai', 'remixAI', 'vulnerability_check_pasted_code']) + }, message: (
{' '} @@ -699,10 +714,9 @@ export const EditorUI = (props: EditorUIProps) => {
- ), + ) } - props.plugin.call('notification', 'alert', modalContent) - pasteCodeRef.current = true + props.plugin.call('notification', 'modal', modalContent) _paq.push(['trackEvent', 'editor', 'onDidPaste', 'more_than_10_lines']) } }) diff --git a/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts b/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts index fda352456e..67da59e597 100644 --- a/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts +++ b/libs/remix-ui/editor/src/lib/syntaxes/solidity.ts @@ -1236,7 +1236,7 @@ export const solidityTokensProvider = { 'abstract', 'payable', 'nonpayable', - 'constants', + 'constant', 'immutable', 'assert', 'require', diff --git a/libs/remix-ui/environment-explorer/src/lib/components/environment-explorer-ui.tsx b/libs/remix-ui/environment-explorer/src/lib/components/environment-explorer-ui.tsx index 0bd67a5c18..b4f374276d 100644 --- a/libs/remix-ui/environment-explorer/src/lib/components/environment-explorer-ui.tsx +++ b/libs/remix-ui/environment-explorer/src/lib/components/environment-explorer-ui.tsx @@ -102,6 +102,19 @@ export const EnvironmentExplorerUI = (props: environmentExplorerUIProps) => { }} >
{(section.descriptionFn && section.descriptionFn(provider)) || provider.description}
+ { provider.isForkedState && + props.deleteForkedState(provider)} + className="btn btn-sm mt-1 border border-danger" + > + Delete Environment + + + } ))} diff --git a/libs/remix-ui/environment-explorer/src/lib/types/index.ts b/libs/remix-ui/environment-explorer/src/lib/types/index.ts index 706e577edd..812a86a6bf 100644 --- a/libs/remix-ui/environment-explorer/src/lib/types/index.ts +++ b/libs/remix-ui/environment-explorer/src/lib/types/index.ts @@ -8,6 +8,7 @@ export type environmentExplorerUIProps = { providersFlat: { [key: string]: Provider } pinnedProviders: string[] } + deleteForkedState (provider: Provider): Promise pinStateCallback (provider: Provider, pinned: boolean): Promise profile: Profile } diff --git a/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.css b/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.css index 3226d3c53f..00454ec7ce 100644 --- a/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.css +++ b/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.css @@ -27,9 +27,6 @@ .remixui_grid_cell_pin { width: 1rem; height: 1rem; - position: relative; - right: 0.9rem; - top: -0.7rem; background: transparent; z-index: 1000; } diff --git a/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.tsx b/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.tsx index 18e9f7af86..d1b0190390 100644 --- a/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.tsx +++ b/libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.tsx @@ -86,8 +86,8 @@ export const RemixUIGridCell = (props: RemixUIGridCellProps) => { { anyEnabled &&
-
- { !props.hideTitle && - { filterCon.showPin && } + { props.tagList && +
}
diff --git a/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts b/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts index d65054171d..0f43f1a225 100644 --- a/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts +++ b/libs/remix-ui/home-tab/src/lib/components/types/carouselTypes.ts @@ -34,7 +34,7 @@ export interface CarouselProps { beforeChange?: (nextSlide: number, state: StateCallBack) => void; // Change callback before sliding every time. `(previousSlide, currentState) => ...` sliderClass?: string; // Use this to style your own track list. itemClass?: string; // Use this to style your own Carousel item. For example add padding-left and padding-right - itemAriaLabel?: string; // Use this to add your own Carousel item aria-label.if it is not defined the child aria label will be applied if the child dont have one than a default empty string will be applied + itemAriaLabel?: string; // Use this to add your own Carousel item aria-label.if it is not defined the child aria label will be applied if the child doesn't have one, then a default empty string will be applied containerClass?: string; // Use this to style the whole container. For example add padding to allow the "dots" or "arrows" to go to other places without being overflown. className?: string; // Use this to style the whole container with styled-components dotListClass?: string; // Use this to style the dot list. 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 f8ba1ae56e..37fbca3d27 100644 --- a/libs/remix-ui/run-tab/src/lib/actions/deploy.ts +++ b/libs/remix-ui/run-tab/src/lib/actions/deploy.ts @@ -22,7 +22,7 @@ const txHelper = remixLib.execution.txHelper const txFormat = remixLib.execution.txFormat const loadContractFromAddress = (plugin: RunTab, address, confirmCb, cb) => { - if (/.(.abi)$/.exec(plugin.config.get('currentFile'))) { + if (/\.(abi)$/.exec(plugin.config.get('currentFile'))) { confirmCb(() => { let abi try { 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 e3faf9938e..092ef3d6ef 100644 --- a/libs/remix-ui/run-tab/src/lib/components/environment.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/environment.tsx @@ -31,6 +31,10 @@ export function EnvironmentUI(props: EnvironmentProps) { const forkStatePrompt = (defaultName: string) => { return (
+
    +
  • +
  • +
@@ -49,10 +53,10 @@ export function EnvironmentUI(props: EnvironmentProps) { return (
    -
  • -
  • +
  • +
- +
) } @@ -83,15 +87,15 @@ export function EnvironmentUI(props: EnvironmentProps) { } else props.runTabPlugin.call('notification', 'toast', `State not available to fork, as no transactions have been made for selected environment & selected workspace.`) } - const deleteVmState = async() => { + const resetVmState = async() => { _paq.push(['trackEvent', 'udapp', 'deleteState', `deleteState clicked`]) const context = currentProvider.name const contextExists = await props.runTabPlugin.call('fileManager', 'exists', `.states/${context}/state.json`) if (contextExists) { props.modal( - intl.formatMessage({ id: 'udapp.deleteVmStateTitle' }), + intl.formatMessage({ id: 'udapp.resetVmStateTitle' }), deleteVmStatePrompt(), - intl.formatMessage({ id: 'udapp.delete' }), + intl.formatMessage({ id: 'udapp.reset' }), async () => { const currentProvider = await props.runTabPlugin.call('blockchain', 'getCurrentProvider') // Reset environment blocks and account data @@ -103,35 +107,28 @@ export function EnvironmentUI(props: EnvironmentProps) { // If there are pinned contracts, delete pinned contracts folder const isPinnedContracts = await props.runTabPlugin.call('fileManager', 'exists', `.deploys/pinned-contracts/${context}`) if (isPinnedContracts) await props.runTabPlugin.call('fileManager', 'remove', `.deploys/pinned-contracts/${context}`) - props.runTabPlugin.call('notification', 'toast', `VM state deleted successfully.`) - _paq.push(['trackEvent', 'udapp', 'deleteState', `VM state deleted`]) + props.runTabPlugin.call('notification', 'toast', `VM state reset successfully.`) + _paq.push(['trackEvent', 'udapp', 'deleteState', `VM state reset`]) }, intl.formatMessage({ id: 'udapp.cancel' }), null ) - } else props.runTabPlugin.call('notification', 'toast', `State not available to delete, as no transactions have been made for selected environment & selected workspace.`) + } else props.runTabPlugin.call('notification', 'toast', `State not available to reset, as no transactions have been made for selected environment & selected workspace.`) } const isL2 = (providerDisplayName: string) => providerDisplayName && (providerDisplayName.startsWith('L2 - Optimism') || providerDisplayName.startsWith('L2 - Arbitrum')) return (
- - - - - }> - - - - { currentProvider && currentProvider.isVM && isSaveEvmStateChecked && }> } - { currentProvider && currentProvider.isVM && isSaveEvmStateChecked && }> - + { currentProvider && currentProvider.isVM && isSaveEvmStateChecked && }> + + + Reset State + }
diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index c6eb716af6..d352347eb9 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -235,6 +235,16 @@ export function RunTabUI(props: RunTabProps) { } }, [runTab.popup]) + useEffect(() => { + if (runTab.selectExEnv.includes('injected') && + Object.entries(runTab.accounts.loadedAccounts).length === 0 && + runTab.accounts.selectedAccount.length > 0) { + // switch to vm-cancum because no account is loaded from injected provider + const context = plugin.blockchain.defaultPinnedProviders[0] // vm-cancun + setExecutionEnvironment({ context, fork: '' }) + } + }, [runTab.accounts.loadedAccounts]) + const setCheckIpfs = (value: boolean) => { dispatch(setIpfsCheckedState(value)) } diff --git a/libs/remix-ui/run-tab/src/lib/types/blockchain.d.ts b/libs/remix-ui/run-tab/src/lib/types/blockchain.d.ts index 428c782a8e..d47d0d89f7 100644 --- a/libs/remix-ui/run-tab/src/lib/types/blockchain.d.ts +++ b/libs/remix-ui/run-tab/src/lib/types/blockchain.d.ts @@ -25,6 +25,7 @@ export class Blockchain extends Plugin { }; setupProviders(): void; providers: any; + defaultPinnedProviders: string[]; getCurrentProvider(): any; /** Return the list of accounts */ getAccounts(cb?: any): any; diff --git a/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx index 52692be16f..a241f2713d 100644 --- a/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx +++ b/libs/remix-ui/scriptrunner/src/lib/script-runner-ui.tsx @@ -5,6 +5,7 @@ import { faCaretDown, faCaretRight, faCheck, faExclamationCircle, faRedoAlt, faT import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { CustomScriptRunner } from "./custom-script-runner"; import { CustomTooltip } from "@remix-ui/helper"; +const _paq = (window._paq = window._paq || []) // eslint-disable-line export interface ScriptRunnerUIProps { loadScriptRunner: (config: ProjectConfiguration) => void; @@ -62,12 +63,18 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => {
} {!config.isLoading && config.errorStatus && config.error && -
loadScriptRunner(config)} className="pointer px-2"> +
{ + loadScriptRunner(config) + _paq.push(['trackEvent', 'scriptRunnerPlugin', 'loadScriptRunnerConfig', config.name]) + }} + className="pointer px-2" + >
} {!config.isLoading && !config.errorStatus && !config.error &&
loadScriptRunner(config)} className="pointer px-2"> - {activeConfig && activeConfig.name !== config.name ? + { activeConfig && activeConfig.name !== config.name ? : } @@ -87,7 +94,9 @@ export const ScriptRunnerUI = (props: ScriptRunnerUIProps) => { ))} -
))} + +
)) + } {enableCustomScriptRunner && { publishedConfigurations={configurations.filter((config) => config.publish)} />}
- ); -}; + ) +} diff --git a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx index 244e83b799..1854646896 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -486,6 +486,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => { compileIcon.current.classList.remove('remixui_spinningIcon') compileIcon.current.classList.remove('remixui_bouncingIcon') if (!state.autoCompile || (state.autoCompile && state.matomoAutocompileOnce)) { + _paq.push(['trackEvent', 'compiler', 'compiled', 'solCompilationFinishedTriggeredByUser']) _paq.push(['trackEvent', 'compiler', 'compiled', 'with_config_file_' + state.useFileConfiguration]) _paq.push(['trackEvent', 'compiler', 'compiled', 'with_version_' + _retrieveVersion()]) if (state.autoCompile && state.matomoAutocompileOnce) { diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx index 4e0d224c1b..e2f281ed4b 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx @@ -235,12 +235,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { try { if (script.trim().startsWith('git')) { // await this.call('git', 'execute', script) code might be used in the future - // TODO: rm gpt or redirect gpt to sol-pgt - } else if (script.trim().startsWith('gpt')) { - call('terminal', 'log',{ type: 'warn', value: `> ${script}` }) - await call('remixAI', 'solidity_answer', script) // No streaming supported in terminal - _paq.push(['trackEvent', 'ai', 'remixAI', 'askFromTerminal']) - } else if (script.trim().startsWith('sol-gpt')) { + } else if (script.trim().startsWith('gpt') || script.trim().startsWith('sol-gpt')) { call('terminal', 'log',{ type: 'warn', value: `> ${script}` }) await call('remixAI', 'solidity_answer', script) // No streaming supported in terminal _paq.push(['trackEvent', 'ai', 'remixAI', 'askFromTerminal']) diff --git a/libs/remix-url-resolver/package.json b/libs/remix-url-resolver/package.json index 57b368e350..a14bc260c2 100644 --- a/libs/remix-url-resolver/package.json +++ b/libs/remix-url-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-url-resolver", - "version": "0.0.91", + "version": "0.0.92", "description": "Solidity import url resolver engine", "main": "src/index.js", "types": "src/index.d.ts", @@ -41,5 +41,5 @@ "typescript": "^3.1.6" }, "typings": "src/index.d.ts", - "gitHead": "baa8abc9b912288b7a2546bedc717a9229f652c4" + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551" } \ No newline at end of file diff --git a/libs/remix-ws-templates/package.json b/libs/remix-ws-templates/package.json index 3da2144d9b..e9f6efaa57 100644 --- a/libs/remix-ws-templates/package.json +++ b/libs/remix-ws-templates/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remix-ws-templates", - "version": "1.0.56", + "version": "1.0.57", "description": "Create a Remix IDE workspace using different templates", "main": "src/index.js", "types": "src/index.d.ts", @@ -24,5 +24,5 @@ "ethers": "^5.4.2", "web3": "^4.1.1" }, - "gitHead": "baa8abc9b912288b7a2546bedc717a9229f652c4" + "gitHead": "a92b8aa830067f879c96b41e301bc3174b87e551" } \ No newline at end of file diff --git a/libs/remixd/package.json b/libs/remixd/package.json index f984bfcc9e..c6006fe2b0 100644 --- a/libs/remixd/package.json +++ b/libs/remixd/package.json @@ -1,6 +1,6 @@ { "name": "@remix-project/remixd", - "version": "0.6.42", + "version": "0.6.43", "description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)", "main": "index.js", "types": "./index.d.ts", diff --git a/releaseDetails.json b/releaseDetails.json index ef07d71106..518103ed3b 100644 --- a/releaseDetails.json +++ b/releaseDetails.json @@ -1,11 +1,10 @@ { - "version": "v0.59.0", + "version": "v0.60.0", "title": "RELEASE HIGHLIGHTS", - "highlight1": "Fork & delete VM state", - "highlight2": "Syntax highlighting for Noir(.nr) files", - "highlight3": "", + "highlight1": "Delete forked state environments", + "highlight2": "Scan code pasted into editor using AI", + "highlight3": "Added Linea chain deployment environment", "highlight4": "", "more": "Read More", - "moreLink": "https://medium.com/remix-ide/remix-release-v0-59-0-306881e41984?source=friends_link&sk=be2390827519bf2d7530f013021160b9" + "moreLink": "https://medium.com/remix-ide/remix-release-v0-60-0-a86fb7649d62?source=friends_link&sk=63c8c24be0b818dbd855947f2804aad2" } - \ No newline at end of file