From ebb31b7151affadb4747625d4c684e6b6b9a2ba1 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 12:45:41 +0200 Subject: [PATCH 01/34] slow etherscan test --- apps/remix-ide-e2e/src/tests/url.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index 2bc6fe778f..db483832eb 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -90,7 +90,7 @@ module.exports = { .pause(5000) .url('http://127.0.0.1:8080/#address=0x56db08fb78bc6689a1ef66efd079083fed0e4915') .refresh() - .pause(7000) + .pause(20000) .currentWorkspaceIs('etherscan-code-sample') .assert.elementPresent('*[data-id=treeViewLitreeViewItemropsten]') .assert.elementPresent('*[data-id=treeViewLitreeViewItemrinkeby]') @@ -102,7 +102,7 @@ module.exports = { }) .url('http://127.0.0.1:8080/#address=0xdac17f958d2ee523a2206206994597c13d831ec7') .refresh() - .pause(7000) + .pause(20000) .currentWorkspaceIs('etherscan-code-sample') .assert.elementPresent('*[data-id=treeViewLitreeViewItemmainnet]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7"]') From bbf4e1509df1086a4c5faa9f74b50398f8f4ff39 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 12:49:17 +0200 Subject: [PATCH 02/34] set flaky --- .circleci/config.yml | 2 +- apps/remix-ide-e2e/src/tests/url.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1c13a22045..2532090a56 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ version: 2.1 parameters: run_flaky_tests: type: boolean - default: false + default: true orbs: browser-tools: circleci/browser-tools@1.3.0 jobs: diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index db483832eb..c3942d42b0 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -85,7 +85,7 @@ module.exports = { }) }, - 'Should load Etherscan verified contractss from URL "address" param) #group2': function (browser: NightwatchBrowser) { + 'Should load Etherscan verified contractss from URL "address" param) #group2 #flaky': function (browser: NightwatchBrowser) { browser .pause(5000) .url('http://127.0.0.1:8080/#address=0x56db08fb78bc6689a1ef66efd079083fed0e4915') From ff627c6fdfca1762db791bb8abac62d2d9c8fe5a Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 13:03:51 +0200 Subject: [PATCH 03/34] add logs --- apps/remix-ide-e2e/src/tests/url.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index c3942d42b0..41af75ffb7 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -87,6 +87,9 @@ module.exports = { 'Should load Etherscan verified contractss from URL "address" param) #group2 #flaky': function (browser: NightwatchBrowser) { browser + .captureBrowserConsoleLogs((event) => { + console.log(event.type, event.timestamp, event.args[0].value); + }) .pause(5000) .url('http://127.0.0.1:8080/#address=0x56db08fb78bc6689a1ef66efd079083fed0e4915') .refresh() From 63f5fc7ca55fe914dd87c91c9cc457ec4a6f8c20 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 13:39:18 +0200 Subject: [PATCH 04/34] disable test --- .circleci/config.yml | 2 +- apps/remix-ide-e2e/src/tests/url.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2532090a56..1c13a22045 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ version: 2.1 parameters: run_flaky_tests: type: boolean - default: true + default: false orbs: browser-tools: circleci/browser-tools@1.3.0 jobs: diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index 41af75ffb7..99668ca2fc 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -85,7 +85,7 @@ module.exports = { }) }, - 'Should load Etherscan verified contractss from URL "address" param) #group2 #flaky': function (browser: NightwatchBrowser) { + 'Should load Etherscan verified contracts from URL "address" param) #group2': !function (browser: NightwatchBrowser) { browser .captureBrowserConsoleLogs((event) => { console.log(event.type, event.timestamp, event.args[0].value); From bf66a6d7e95929ebcff48eacb7fe8a45e7db6391 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 13:47:02 +0200 Subject: [PATCH 05/34] mv test --- apps/remix-ide-e2e/src/tests/url.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index 99668ca2fc..daf67e230b 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -87,9 +87,6 @@ module.exports = { 'Should load Etherscan verified contracts from URL "address" param) #group2': !function (browser: NightwatchBrowser) { browser - .captureBrowserConsoleLogs((event) => { - console.log(event.type, event.timestamp, event.args[0].value); - }) .pause(5000) .url('http://127.0.0.1:8080/#address=0x56db08fb78bc6689a1ef66efd079083fed0e4915') .refresh() @@ -251,7 +248,7 @@ module.exports = { .openFile('contracts/governance/UnionGovernor.sol') }, - 'Should execute function call from URL parameters #group2': function (browser: NightwatchBrowser) { + 'Should execute function call from URL parameters #group1': function (browser: NightwatchBrowser) { browser .switchWorkspace('default_workspace') .url('http://127.0.0.1:8080?calls=fileManager//open//contracts/3_Ballot.sol///terminal//log//log') From 4582a1099ba23cb4e29d2248610e3c05999db6e9 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 13:48:19 +0200 Subject: [PATCH 06/34] update time --- apps/remix-ide-e2e/src/tests/url.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index daf67e230b..9e8e83c39a 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -90,7 +90,7 @@ module.exports = { .pause(5000) .url('http://127.0.0.1:8080/#address=0x56db08fb78bc6689a1ef66efd079083fed0e4915') .refresh() - .pause(20000) + .pause(7000) .currentWorkspaceIs('etherscan-code-sample') .assert.elementPresent('*[data-id=treeViewLitreeViewItemropsten]') .assert.elementPresent('*[data-id=treeViewLitreeViewItemrinkeby]') @@ -102,7 +102,7 @@ module.exports = { }) .url('http://127.0.0.1:8080/#address=0xdac17f958d2ee523a2206206994597c13d831ec7') .refresh() - .pause(20000) + .pause(7000) .currentWorkspaceIs('etherscan-code-sample') .assert.elementPresent('*[data-id=treeViewLitreeViewItemmainnet]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7"]') From 4f8d1ed5f837231494b781cc618f1adb2af2323b Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 13:49:22 +0200 Subject: [PATCH 07/34] rm group --- apps/remix-ide-e2e/src/tests/url.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/tests/url.test.ts b/apps/remix-ide-e2e/src/tests/url.test.ts index 9e8e83c39a..af78de69ea 100644 --- a/apps/remix-ide-e2e/src/tests/url.test.ts +++ b/apps/remix-ide-e2e/src/tests/url.test.ts @@ -85,7 +85,7 @@ module.exports = { }) }, - 'Should load Etherscan verified contracts from URL "address" param) #group2': !function (browser: NightwatchBrowser) { + 'Should load Etherscan verified contracts from URL "address" param)': !function (browser: NightwatchBrowser) { browser .pause(5000) .url('http://127.0.0.1:8080/#address=0x56db08fb78bc6689a1ef66efd079083fed0e4915') From b7059bb52c853e15d24bab5326acc4db10c11a7a Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 1 Sep 2022 16:10:29 +0200 Subject: [PATCH 08/34] fix feeding monaco with current file dependencies --- apps/remix-ide/src/app/editor/editor.js | 51 +++++++++++---------- apps/remix-ide/src/app/files/fileManager.ts | 7 +++ 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index e0df3b7d0b..32e565ae8d 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -213,6 +213,33 @@ class Editor extends Plugin { return ext && this.modes[ext] ? this.modes[ext] : this.modes.txt } + async handleTypeScriptDependenciesOf (path, content, readFile) { + if (path.endsWith('.ts')) { + // extract the import, resolve their content + // and add the imported files to Monaco through the `addModel` + // so Monaco can provide auto completion + const paths = path.split('/') + paths.pop() + const fromPath = paths.join('/') // get current execution context path + for (const match of content.matchAll(/import\s+.*\s+from\s+(?:"(.*?)"|'(.*?)')/g)) { + let pathDep = match[2] + if (pathDep.startsWith('./') || pathDep.startsWith('../')) pathDep = resolve(fromPath, pathDep) + if (pathDep.startsWith('/')) pathDep = pathDep.substring(1) + if (!pathDep.endsWith('.ts')) pathDep = pathDep + '.ts' + try { + // we can't use the fileManager plugin call directly + // because it's itself called in a plugin context, and that causes a timeout in the plugin stack + const contentDep = await readFile(pathDep) + console.log(contentDep) + if (contentDep !== null) { + this.emit('addModel', contentDep, 'typescript', pathDep, false) + } + } catch (e) {} + } + + } + } + /** * Create an editor session * @param {string} path path of the file @@ -222,30 +249,6 @@ class Editor extends Plugin { async _createSession (path, content, mode) { if (!this.activated) return - if (path.endsWith('.ts')) { - try { - // extract the import, resolve their content - // and add the imported files to Monaco through the `addModel` - // so Monaco can provide auto completion - let content = await this.call('fileManager', 'readFile', path) - const paths = path.split('/') - paths.pop() - const fromPath = paths.join('/') // get current execution context path - for (const match of content.matchAll(/import\s+.*\s+from\s+(?:"(.*?)"|'(.*?)')/g)) { - let path = match[2] - if (path.startsWith('./') || path.startsWith('../')) path = resolve(fromPath, path) - if (path.startsWith('/')) path = path.substring(1) - if (!path.endsWith('.ts')) path = path + '.ts' - if (await this.call('fileManager', 'exists', path)) { - content = await this.call('fileManager', 'readFile', path) - this.emit('addModel', content, 'typescript', path, false) - } - } - } catch (e) { - console.log('unable to resolve dependency of', path, e) - } - } - this.emit('addModel', content, mode, path, false) return { path, diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 8070762119..32892f9136 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -632,6 +632,13 @@ class FileManager extends Plugin { console.log(error) throw error } + try { + // This make sure dependencies are loaded in the editor context. + // This ensure monaco is aware of deps artifacts, so it can provide basic features like "go to" symbols. + await this.editor.handleTypeScriptDependenciesOf(file, content, path => this.readFile(path)) + } catch (e) { + console.log('unable to handle TypeScript dependencies of', file) + } if (provider.isReadOnly(file)) { await this.editor.openReadOnly(file, content) } else { From 129907fba7c7cd58855ebd4f1fa97fe26329266c Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 1 Sep 2022 16:53:17 +0200 Subject: [PATCH 09/34] fix giving ref to types --- libs/remix-ui/editor/src/lib/web-types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/remix-ui/editor/src/lib/web-types.ts b/libs/remix-ui/editor/src/lib/web-types.ts index 757c183330..f64efb89bd 100644 --- a/libs/remix-ui/editor/src/lib/web-types.ts +++ b/libs/remix-ui/editor/src/lib/web-types.ts @@ -227,9 +227,9 @@ export const loadTypes = async (monaco) => { // @ts-ignore const chaiType = await import('raw-loader!@types/chai/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(chaiType.default) + monaco.languages.typescript.typescriptDefaults.addExtraLib(chaiType.default, `file:///node_modules/@types/chai/index.d.ts`) // @ts-ignore const mochaType = await import('raw-loader!@types/mocha/index.d.ts') - monaco.languages.typescript.typescriptDefaults.addExtraLib(mochaType.default) + monaco.languages.typescript.typescriptDefaults.addExtraLib(mochaType.default, `file:///node_modules/@types/mocha/index.d.ts`) } \ No newline at end of file From 6bcd13c4bc312e3fd7a3e6794e62f9f3a93d3fb5 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 1 Sep 2022 17:18:49 +0200 Subject: [PATCH 10/34] fix linting --- apps/remix-ide/src/app/editor/editor.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 32e565ae8d..3ffe9c8468 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -234,7 +234,9 @@ class Editor extends Plugin { if (contentDep !== null) { this.emit('addModel', contentDep, 'typescript', pathDep, false) } - } catch (e) {} + } catch (e) { + console.log(e) + } } } From 2cbef82c3d4f6494d3a35f54f2efa5df99dfcec8 Mon Sep 17 00:00:00 2001 From: lianahus Date: Fri, 2 Sep 2022 11:28:53 +0200 Subject: [PATCH 11/34] fixing the selected color for autocomplete --- libs/remix-ui/editor/src/lib/remix-ui-editor.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 de1f7ff863..38f9a5f7ac 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -262,7 +262,8 @@ export const EditorUI = (props: EditorUIProps) => { // see https://code.visualstudio.com/api/references/theme-color for more settings 'editor.background': textbackground, 'editorSuggestWidget.background': lightColor, - 'editorSuggestWidget.selectedBackground': lightColor, + 'editorSuggestWidget.selectedBackground': secondaryColor, + 'editorSuggestWidget.selectedForeground': textColor, 'editorSuggestWidget.highlightForeground': infoColor, 'editor.lineHighlightBorder': secondaryColor, 'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor, From 596cc592e6ac857902b99e244d5c9abe46177d2e Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 14:33:03 +0200 Subject: [PATCH 12/34] fix drag drop --- apps/remix-ide/src/app/files/fileManager.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 32892f9136..9d45788cd0 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -872,14 +872,15 @@ class FileManager extends Plugin { await this._handleIsDir(src, `Cannot move ${src}. Path is not directory.`) await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`) const dirName = helper.extractNameFromKey(src) - - if (await this.exists(dest + '/' + dirName)) { + if (await this.exists(dest + '/' + dirName) || src === dest) { throw createError({ code: 'ENOENT', message: `Cannot move ${src}. Folder already exists at destination ${dest}`}) } + console.log('mv files') await this.copyDir(src, dest, dirName) await this.remove(src) } catch (e) { + console.log('mv dir error', e) throw new Error(e) } } From b2833812db6afa164ab4e4a6051aff0ec65743b5 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 14:35:58 +0200 Subject: [PATCH 13/34] fix --- apps/remix-ide/src/app/files/fileManager.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 9d45788cd0..6453fb58b2 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -875,12 +875,11 @@ class FileManager extends Plugin { if (await this.exists(dest + '/' + dirName) || src === dest) { throw createError({ code: 'ENOENT', message: `Cannot move ${src}. Folder already exists at destination ${dest}`}) } - console.log('mv files') + await this.copyDir(src, dest, dirName) await this.remove(src) } catch (e) { - console.log('mv dir error', e) throw new Error(e) } } From 49647177220d82552987b514b7f3dcb0ea0909d3 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 14:41:01 +0200 Subject: [PATCH 14/34] update EEXIST --- apps/remix-ide/src/app/files/fileManager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 6453fb58b2..c76cf34ff4 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -845,7 +845,7 @@ class FileManager extends Plugin { const fileName = helper.extractNameFromKey(src) if (await this.exists(dest + '/' + fileName)) { - throw createError({ code: 'ENOENT', message: `Cannot move ${src}. File already exists at destination ${dest}`}) + throw createError({ code: 'EEXIST', message: `Cannot move ${src}. File already exists at destination ${dest}`}) } await this.copyFile(src, dest, fileName) await this.remove(src) @@ -873,9 +873,9 @@ class FileManager extends Plugin { await this._handleIsDir(dest, `Cannot move content into ${dest}. Path is not directory.`) const dirName = helper.extractNameFromKey(src) if (await this.exists(dest + '/' + dirName) || src === dest) { - throw createError({ code: 'ENOENT', message: `Cannot move ${src}. Folder already exists at destination ${dest}`}) + throw createError({ code: 'EEXIST', message: `Cannot move ${src}. Folder already exists at destination ${dest}`}) } - + await this.copyDir(src, dest, dirName) await this.remove(src) From b2dee707753a58585f2752858175eebada16d352 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 14:41:31 +0200 Subject: [PATCH 15/34] rm space --- apps/remix-ide/src/app/files/fileManager.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index c76cf34ff4..49135860bf 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -875,7 +875,6 @@ class FileManager extends Plugin { if (await this.exists(dest + '/' + dirName) || src === dest) { throw createError({ code: 'EEXIST', message: `Cannot move ${src}. Folder already exists at destination ${dest}`}) } - await this.copyDir(src, dest, dirName) await this.remove(src) From f1eb432a73ddb1e691a2872de59316e9e9472604 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 2 Sep 2022 15:58:56 +0200 Subject: [PATCH 16/34] rm log --- apps/remix-ide/src/app/editor/editor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 3ffe9c8468..10dab21ecc 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -230,7 +230,6 @@ class Editor extends Plugin { // we can't use the fileManager plugin call directly // because it's itself called in a plugin context, and that causes a timeout in the plugin stack const contentDep = await readFile(pathDep) - console.log(contentDep) if (contentDep !== null) { this.emit('addModel', contentDep, 'typescript', pathDep, false) } From b76d5cd8d75a96708fe2059820bca2c3fa841ed6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 5 Sep 2022 15:02:39 +0200 Subject: [PATCH 17/34] better electron support --- apps/remix-ide/src/app.js | 4 ---- libs/remix-ui/workspace/src/lib/actions/index.ts | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 0a4f982fa4..b001164e9c 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -354,10 +354,6 @@ class AppComponent { const queryParams = new QueryParams() const params = queryParams.get() - if (isElectron()) { - this.appManager.activatePlugin('remixd') - } - try { this.engine.register(await this.appManager.registeredPlugins()) } catch (e) { diff --git a/libs/remix-ui/workspace/src/lib/actions/index.ts b/libs/remix-ui/workspace/src/lib/actions/index.ts index afc401c744..8627c25c7e 100644 --- a/libs/remix-ui/workspace/src/lib/actions/index.ts +++ b/libs/remix-ui/workspace/src/lib/actions/index.ts @@ -8,6 +8,7 @@ import { createWorkspaceTemplate, getWorkspaces, loadWorkspacePreset, setPlugin, import { QueryParams } from '@remix-project/remix-lib' import { fetchContractFromEtherscan } from '@remix-project/core-plugin' // eslint-disable-line import JSZip from 'jszip' +import isElectron from 'is-electron' export * from './events' export * from './workspace' @@ -111,6 +112,8 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React. await basicWorkspaceInit(workspaces, workspaceProvider) } } else await basicWorkspaceInit(workspaces, workspaceProvider) + } else if (isElectron()) { + await plugin.call('manager', 'activatePlugin', 'remixd') } else if (localStorage.getItem("currentWorkspace")) { const index = workspaces.findIndex(element => element.name == localStorage.getItem("currentWorkspace")) if (index !== -1) { From 5bf47e08b234e66d832012cce329ef271781f804 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Mon, 5 Sep 2022 15:29:03 +0200 Subject: [PATCH 18/34] fix calls --- .../src/app/plugins/parser/code-parser.tsx | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx index c9caf3b70c..42d7c866bf 100644 --- a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx +++ b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx @@ -87,6 +87,18 @@ export class CodeParser extends Plugin { } } + async handleChangeEvents() { + const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion') + if (completionSettings) { + await this.antlrService.getCurrentFileAST() + } + const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas') + const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors') + if(showGasSettings || showErrorSettings) { + await this.compilerService.compile() + } + } + async onActivation() { this.gasService = new CodeParserGasService(this) @@ -102,8 +114,7 @@ export class CodeParser extends Plugin { this.on('editor', 'didChangeFile', async (file) => { await this.call('editor', 'discardLineTexts') - await this.antlrService.getCurrentFileAST() - await this.compilerService.compile() + await this.handleChangeEvents() }) this.on('filePanel', 'setWorkspace', async () => { @@ -113,8 +124,7 @@ export class CodeParser extends Plugin { this.on('fileManager', 'currentFileChanged', async () => { await this.call('editor', 'discardLineTexts') - await this.antlrService.getCurrentFileAST() - await this.compilerService.compile() + await this.handleChangeEvents() }) this.on('solidity', 'loadingCompiler', async (url) => { @@ -188,10 +198,10 @@ export class CodeParser extends Plugin { const index = {} const contractName: string = contractNode.name const callback = (node) => { - if(inScope && node.scope !== contractNode.id + if (inScope && node.scope !== contractNode.id && !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition')) return - if(inScope) node.isClassNode = true; + if (inScope) node.isClassNode = true; node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult) node.functionName = node.name + this._getInputParams(node) node.contractName = contractName @@ -227,11 +237,11 @@ export class CodeParser extends Plugin { if ((node.scope && node.scope === baseContract.id) || node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' - ) { + ) { baseNodesWithBaseContractScope[node.id] = node } - if(node.members){ - for(const member of node.members){ + if (node.members) { + for (const member of node.members) { member.contractName = (baseContract as any).name member.contractId = (baseContract as any).id member.isBaseNode = true; @@ -249,7 +259,7 @@ export class CodeParser extends Plugin { if (node.nodeType === 'ImportDirective') { const imported = await this.resolveImports(node, {}) - + for (const importedNode of (Object.values(imported) as any)) { if (importedNode.nodes) for (const subNode of importedNode.nodes) { From 06cd09fa83ad5a56ad12fc8d017f09e84b25e86d Mon Sep 17 00:00:00 2001 From: filip mertens Date: Mon, 5 Sep 2022 15:31:08 +0200 Subject: [PATCH 19/34] disable by default --- libs/remix-ui/settings/src/lib/remix-ui-settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index 0a24d3b038..55f25bb3d8 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -148,7 +148,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { const isEditorWrapChecked = props.config.get('settings/text-wrap') || false const isPersonalChecked = props.config.get('settings/personal-mode') || false const isMatomoChecked = props.config.get('settings/matomo-analytics') || false - const isAutoCompleteChecked = props.config.get('settings/auto-completion') === null ? true:props.config.get('settings/auto-completion') + const isAutoCompleteChecked = props.config.get('settings/auto-completion') === null ? false:props.config.get('settings/auto-completion') const isShowGasInEditorChecked = props.config.get('settings/show-gas') === null ? true:props.config.get('settings/show-gas') const displayErrorsChecked = props.config.get('settings/display-errors') === null ? true:props.config.get('settings/display-errors') return ( From 203201449ff5b8dc1ae6bd077a4004dc643ec11e Mon Sep 17 00:00:00 2001 From: filip mertens Date: Mon, 5 Sep 2022 15:40:10 +0200 Subject: [PATCH 20/34] turn off by default --- libs/remix-ui/settings/src/lib/remix-ui-settings.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index 55f25bb3d8..4b5da79895 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -41,13 +41,13 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { if (javascriptVM === null || javascriptVM === undefined) ethereumVM(props.config, true, dispatch) const useAutoComplete = props.config.get('settings/auto-completion') - if (useAutoComplete === null || useAutoComplete === undefined) useAutoCompletion(props.config, true, dispatch) + if (useAutoComplete === null || useAutoComplete === undefined) useAutoCompletion(props.config, false, dispatch) const displayErrors = props.config.get('settings/display-errors') - if (displayErrors === null || displayErrors === undefined) useDisplayErrors(props.config, true, dispatch) + if (displayErrors === null || displayErrors === undefined) useDisplayErrors(props.config, false, dispatch) const useShowGas = props.config.get('settings/show-gas') - if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, true, dispatch) + if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, false, dispatch) } useEffect(() => initValue(), [resetState, props.config]) useEffect(() => initValue(), []) @@ -148,7 +148,8 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { const isEditorWrapChecked = props.config.get('settings/text-wrap') || false const isPersonalChecked = props.config.get('settings/personal-mode') || false const isMatomoChecked = props.config.get('settings/matomo-analytics') || false - const isAutoCompleteChecked = props.config.get('settings/auto-completion') === null ? false:props.config.get('settings/auto-completion') + + const isAutoCompleteChecked = props.config.get('settings/auto-completion') || false const isShowGasInEditorChecked = props.config.get('settings/show-gas') === null ? true:props.config.get('settings/show-gas') const displayErrorsChecked = props.config.get('settings/display-errors') === null ? true:props.config.get('settings/display-errors') return ( From 566fe0d906c48786408682b942b4eecdc0a4ee3c Mon Sep 17 00:00:00 2001 From: filip mertens Date: Mon, 5 Sep 2022 15:41:17 +0200 Subject: [PATCH 21/34] turn off by default --- libs/remix-ui/settings/src/lib/remix-ui-settings.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index 4b5da79895..e8a15d3a78 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -150,8 +150,8 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => { const isMatomoChecked = props.config.get('settings/matomo-analytics') || false const isAutoCompleteChecked = props.config.get('settings/auto-completion') || false - const isShowGasInEditorChecked = props.config.get('settings/show-gas') === null ? true:props.config.get('settings/show-gas') - const displayErrorsChecked = props.config.get('settings/display-errors') === null ? true:props.config.get('settings/display-errors') + const isShowGasInEditorChecked = props.config.get('settings/show-gas') || false + const displayErrorsChecked = props.config.get('settings/display-errors') || false return (
From 2f3c68d4d3d857ad7a1bffb27ff13ce67fa40e41 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Mon, 5 Sep 2022 16:15:02 +0200 Subject: [PATCH 22/34] e2e --- apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts | 9 +++++++++ apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts | 8 ++++++++ apps/remix-ide-e2e/src/tests/editorReferences.test.ts | 7 +++++++ apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts | 7 +++++++ apps/remix-ide-e2e/src/tests/editor_line_text.test.ts | 7 +++++++ libs/remix-ui/settings/src/lib/remix-ui-settings.tsx | 6 +++--- 6 files changed, 41 insertions(+), 3 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts index d30c942b59..171ad97b6a 100644 --- a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts @@ -12,6 +12,15 @@ module.exports = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', false) }, + + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, + 'Should add test and base files #group1': function (browser: NightwatchBrowser) { browser.addFile(examples.testContract.name, examples.testContract) .addFile(examples.baseContract.name, examples.baseContract) diff --git a/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts b/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts index db86224b11..f9f5eef0c4 100644 --- a/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts @@ -18,6 +18,14 @@ module.exports = { init(browser, done, 'http://127.0.0.1:8080', false) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, + 'Should load the test file': function (browser: NightwatchBrowser) { browser.openFile('contracts') .openFile('contracts/3_Ballot.sol') diff --git a/apps/remix-ide-e2e/src/tests/editorReferences.test.ts b/apps/remix-ide-e2e/src/tests/editorReferences.test.ts index 86856a9822..f63837233e 100644 --- a/apps/remix-ide-e2e/src/tests/editorReferences.test.ts +++ b/apps/remix-ide-e2e/src/tests/editorReferences.test.ts @@ -20,6 +20,13 @@ module.exports = { init(browser, done, 'http://127.0.0.1:8080', false) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, 'Should load the test file': function (browser: NightwatchBrowser) { browser.openFile('contracts') .openFile('contracts/3_Ballot.sol') diff --git a/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts index 8d8cba54e0..daa5456982 100644 --- a/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts +++ b/apps/remix-ide-e2e/src/tests/editor_error_marker.test.ts @@ -8,6 +8,13 @@ module.exports = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', true) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, 'Should add error marker': function (browser: NightwatchBrowser) { browser .openFile('contracts') diff --git a/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts index afff1c8f28..24eb0540e1 100644 --- a/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts +++ b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts @@ -8,6 +8,13 @@ module.exports = { before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done, 'http://127.0.0.1:8080', true) }, + 'Should enable settings': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('settings') + .click('[data-id="settingsAutoCompleteLabel"]') + .click('[data-id="settingsShowGasLabel"]') + .click('[data-id="displayErrorsLabel"]') + }, 'Should add line texts': function (browser: NightwatchBrowser) { browser .openFile('contracts') diff --git a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx index e8a15d3a78..b31313726d 100644 --- a/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx +++ b/libs/remix-ui/settings/src/lib/remix-ui-settings.tsx @@ -189,19 +189,19 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
-
-
-
From eda9c69b367abf2a6740450573d287f08f7b9ddd Mon Sep 17 00:00:00 2001 From: filip mertens Date: Mon, 5 Sep 2022 16:22:56 +0200 Subject: [PATCH 23/34] fix setting --- apps/remix-ide/src/app/plugins/parser/code-parser.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx index 42d7c866bf..b763f81a3c 100644 --- a/apps/remix-ide/src/app/plugins/parser/code-parser.tsx +++ b/apps/remix-ide/src/app/plugins/parser/code-parser.tsx @@ -94,7 +94,7 @@ export class CodeParser extends Plugin { } const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas') const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors') - if(showGasSettings || showErrorSettings) { + if(showGasSettings || showErrorSettings || completionSettings) { await this.compilerService.compile() } } From a2df3b47897aa6c4a56c5b0a59d0a8def569cbdc Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 5 Sep 2022 16:23:56 +0200 Subject: [PATCH 24/34] create default workspace for desktop --- libs/remix-ui/workspace/src/lib/actions/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/remix-ui/workspace/src/lib/actions/index.ts b/libs/remix-ui/workspace/src/lib/actions/index.ts index 8627c25c7e..912347a197 100644 --- a/libs/remix-ui/workspace/src/lib/actions/index.ts +++ b/libs/remix-ui/workspace/src/lib/actions/index.ts @@ -113,6 +113,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React. } } else await basicWorkspaceInit(workspaces, workspaceProvider) } else if (isElectron()) { + await basicWorkspaceInit(workspaces, workspaceProvider) await plugin.call('manager', 'activatePlugin', 'remixd') } else if (localStorage.getItem("currentWorkspace")) { const index = workspaces.findIndex(element => element.name == localStorage.getItem("currentWorkspace")) From ea84447ad36bfbd77ab4597c85032029619b1a2b Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 5 Sep 2022 16:32:15 +0200 Subject: [PATCH 25/34] add toast --- libs/remix-ui/workspace/src/lib/actions/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/remix-ui/workspace/src/lib/actions/index.ts b/libs/remix-ui/workspace/src/lib/actions/index.ts index 912347a197..aa06fb7840 100644 --- a/libs/remix-ui/workspace/src/lib/actions/index.ts +++ b/libs/remix-ui/workspace/src/lib/actions/index.ts @@ -113,6 +113,7 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React. } } else await basicWorkspaceInit(workspaces, workspaceProvider) } else if (isElectron()) { + plugin.call('notification', 'toast', `connecting to localhost...`) await basicWorkspaceInit(workspaces, workspaceProvider) await plugin.call('manager', 'activatePlugin', 'remixd') } else if (localStorage.getItem("currentWorkspace")) { From 72244ac01c76b2ec109478ca1b077bca83ff53c8 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 6 Sep 2022 10:49:56 +0200 Subject: [PATCH 26/34] use local version.json for remix live --- apps/remix-ide/src/assets/js/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide/src/assets/js/loader.js b/apps/remix-ide/src/assets/js/loader.js index ac49d44e3d..4e60dbc36d 100644 --- a/apps/remix-ide/src/assets/js/loader.js +++ b/apps/remix-ide/src/assets/js/loader.js @@ -45,7 +45,7 @@ function isElectron() { return false } -const versionUrl = isElectron() ? 'https://remix.ethereum.org/assets/version.json' : 'assets/version.json' +const versionUrl = 'assets/version.json' fetch(versionUrl, { cache: "no-store" }).then(response => { response.text().then(function (data) { const version = JSON.parse(data); From e2d04177c129fe2a4935415a4596dee343040a4e Mon Sep 17 00:00:00 2001 From: Aniket-Engg Date: Wed, 7 Sep 2022 17:00:31 +0530 Subject: [PATCH 27/34] use oz wizard npm package --- libs/remix-ws-templates/package.json | 3 +++ .../ozerc20/contracts/SampleERC20.sol | 14 -------------- .../src/templates/ozerc20/index.ts | 7 ++++--- .../ozerc20/scripts/deploy_with_ethers.ts | 2 +- .../ozerc20/scripts/deploy_with_web3.ts | 2 +- .../templates/ozerc20/tests/MyToken_test.sol | 18 ++++++++++++++++++ .../ozerc20/tests/SampleERC20_test.sol | 18 ------------------ package.json | 2 ++ yarn.lock | 12 ++++++++++++ 9 files changed, 41 insertions(+), 37 deletions(-) delete mode 100644 libs/remix-ws-templates/src/templates/ozerc20/contracts/SampleERC20.sol create mode 100644 libs/remix-ws-templates/src/templates/ozerc20/tests/MyToken_test.sol delete mode 100644 libs/remix-ws-templates/src/templates/ozerc20/tests/SampleERC20_test.sol diff --git a/libs/remix-ws-templates/package.json b/libs/remix-ws-templates/package.json index 3a27e44a26..16a4d66db1 100644 --- a/libs/remix-ws-templates/package.json +++ b/libs/remix-ws-templates/package.json @@ -22,8 +22,11 @@ "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-ws-templates#readme", "typings": "./src/index.d.ts", "dependencies": { + "@openzeppelin/contracts": "^4.7.3", + "@openzeppelin/wizard": "^0.1.1", "ethers": "^5.4.2", "web3": "^1.5.1" + }, "gitHead": "0c1957c9b2f890076a5062309bc81b41f93af57c" } \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/ozerc20/contracts/SampleERC20.sol b/libs/remix-ws-templates/src/templates/ozerc20/contracts/SampleERC20.sol deleted file mode 100644 index 56a7708413..0000000000 --- a/libs/remix-ws-templates/src/templates/ozerc20/contracts/SampleERC20.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity >=0.7.0 <0.9.0; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -/** - * @title SampleERC20 - * @dev Create a sample ERC20 standard token - */ -contract SampleERC20 is ERC20 { - - constructor(string memory tokenName, string memory tokenSymbol) ERC20(tokenName, tokenSymbol) {} -} \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/ozerc20/index.ts b/libs/remix-ws-templates/src/templates/ozerc20/index.ts index 48ffb45341..f44f812bb2 100644 --- a/libs/remix-ws-templates/src/templates/ozerc20/index.ts +++ b/libs/remix-ws-templates/src/templates/ozerc20/index.ts @@ -1,7 +1,8 @@ +import { erc20 } from '@openzeppelin/wizard'; + export default async () => { return { - // @ts-ignore - 'contracts/SampleERC20.sol': (await import('raw-loader!./contracts/SampleERC20.sol')).default, + 'contracts/MyToken.sol': erc20.print(), // @ts-ignore 'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default, // @ts-ignore @@ -11,6 +12,6 @@ export default async () => { // @ts-ignore 'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default, // @ts-ignore - 'tests/SampleERC20_test.sol': (await import('raw-loader!./tests/SampleERC20_test.sol')).default + 'tests/MyToken_test.sol': (await import('raw-loader!./tests/MyToken_test.sol')).default } } \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_ethers.ts b/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_ethers.ts index c12ce0dfcd..a6c8cf30e5 100644 --- a/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_ethers.ts +++ b/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_ethers.ts @@ -2,7 +2,7 @@ import { deploy } from './ethers-lib' (async () => { try { - const result = await deploy('SampleERC20', ['testToken', 'TST']) + const result = await deploy('MyToken', []) console.log(`address: ${result.address}`) } catch (e) { console.log(e.message) diff --git a/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_web3.ts b/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_web3.ts index 3a19d6a068..b22b119246 100644 --- a/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_web3.ts +++ b/libs/remix-ws-templates/src/templates/ozerc20/scripts/deploy_with_web3.ts @@ -2,7 +2,7 @@ import { deploy } from './web3-lib' (async () => { try { - const result = await deploy('SampleERC20', ['testToken', 'TST']) + const result = await deploy('MyToken', []) console.log(`address: ${result.address}`) } catch (e) { console.log(e.message) diff --git a/libs/remix-ws-templates/src/templates/ozerc20/tests/MyToken_test.sol b/libs/remix-ws-templates/src/templates/ozerc20/tests/MyToken_test.sol new file mode 100644 index 0000000000..0fb1d12117 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/ozerc20/tests/MyToken_test.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; +import "remix_tests.sol"; +import "../contracts/MyToken.sol"; + +contract MyTokenTest { + + MyToken s; + function beforeAll () public { + s = new MyToken(); + } + + function testTokenNameAndSymbol () public { + Assert.equal(s.name(), "MyToken", "token name did not match"); + Assert.equal(s.symbol(), "MTK", "token symbol did not match"); + } +} \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/ozerc20/tests/SampleERC20_test.sol b/libs/remix-ws-templates/src/templates/ozerc20/tests/SampleERC20_test.sol deleted file mode 100644 index 19eb11d97d..0000000000 --- a/libs/remix-ws-templates/src/templates/ozerc20/tests/SampleERC20_test.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity >=0.7.0 <0.9.0; -import "remix_tests.sol"; -import "../contracts/SampleERC20.sol"; - -contract SampleERC20Test { - - SampleERC20 s; - function beforeAll () public { - s = new SampleERC20("TestToken", "TST"); - } - - function testTokenNameAndSymbol () public { - Assert.equal(s.name(), "TestToken", "token name did not match"); - Assert.equal(s.symbol(), "TST", "token symbol did not match"); - } -} \ No newline at end of file diff --git a/package.json b/package.json index b13b797c38..8399c040bc 100644 --- a/package.json +++ b/package.json @@ -154,6 +154,8 @@ "@ethersphere/bee-js": "^3.2.0", "@isomorphic-git/lightning-fs": "^4.4.1", "@monaco-editor/react": "4.4.5", + "@openzeppelin/contracts": "^4.7.3", + "@openzeppelin/wizard": "^0.1.1", "@remixproject/engine": "^0.3.31", "@remixproject/engine-web": "^0.3.31", "@remixproject/plugin": "^0.3.31", diff --git a/yarn.lock b/yarn.lock index e36042dda0..3809c46cb6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3912,6 +3912,18 @@ dependencies: "@octokit/openapi-types" "^11.2.0" +"@openzeppelin/contracts@^4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e" + integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw== + +"@openzeppelin/wizard@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/wizard/-/wizard-0.1.1.tgz#8c183e2c5748869bc3a5317c0330aa36a9ad44fe" + integrity sha512-AGyvn3PIh1vCgAEoRKAXKhtlk4fkA8AHE7G4PyzLnYcASClYCWpSf43WLJCs6S/LORvTZADX1flvF8x2LciJIg== + dependencies: + array.prototype.flatmap "^1.2.4" + "@pmmmwh/react-refresh-webpack-plugin@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz#1eec460596d200c0236bf195b078a5d1df89b766" From e5e17806089804e6c015c4093a624f2d27099808 Mon Sep 17 00:00:00 2001 From: Aniket-Engg Date: Wed, 7 Sep 2022 17:29:11 +0530 Subject: [PATCH 28/34] e2e fix --- apps/remix-ide-e2e/src/tests/workspace.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index 3e15a18248..d7314e3c5f 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -124,7 +124,7 @@ module.exports = { .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC20.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') // check js and ts files are not transformed @@ -156,7 +156,7 @@ module.exports = { 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/SampleERC20_test.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/MyToken_test.sol"]') }, 'Should create ERC721 workspace with files #group1': function (browser: NightwatchBrowser) { From 36be826def77e24493bb2e4dc97aad2ce215cf0e Mon Sep 17 00:00:00 2001 From: Aniket-Engg Date: Wed, 7 Sep 2022 19:12:25 +0530 Subject: [PATCH 29/34] generate Erc721 contracts --- .../ozerc721/contracts/SampleERC721.sol | 14 -------------- .../src/templates/ozerc721/index.ts | 7 ++++--- .../ozerc721/scripts/deploy_with_ethers.ts | 2 +- .../ozerc721/scripts/deploy_with_web3.ts | 2 +- .../templates/ozerc721/tests/MyToken_test.sol | 18 ++++++++++++++++++ .../ozerc721/tests/SampleERC721_test.sol | 18 ------------------ 6 files changed, 24 insertions(+), 37 deletions(-) delete mode 100644 libs/remix-ws-templates/src/templates/ozerc721/contracts/SampleERC721.sol create mode 100644 libs/remix-ws-templates/src/templates/ozerc721/tests/MyToken_test.sol delete mode 100644 libs/remix-ws-templates/src/templates/ozerc721/tests/SampleERC721_test.sol diff --git a/libs/remix-ws-templates/src/templates/ozerc721/contracts/SampleERC721.sol b/libs/remix-ws-templates/src/templates/ozerc721/contracts/SampleERC721.sol deleted file mode 100644 index 5a0d40c8e3..0000000000 --- a/libs/remix-ws-templates/src/templates/ozerc721/contracts/SampleERC721.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity >=0.7.0 <0.9.0; - -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -/** - * @title SampleERC721 - * @dev Create a sample ERC721 standard token - */ -contract SampleERC721 is ERC721 { - - constructor(string memory tokenName, string memory tokenSymbol) ERC721(tokenName, tokenSymbol) {} -} \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/ozerc721/index.ts b/libs/remix-ws-templates/src/templates/ozerc721/index.ts index 53b6d132e7..26a3acd59b 100644 --- a/libs/remix-ws-templates/src/templates/ozerc721/index.ts +++ b/libs/remix-ws-templates/src/templates/ozerc721/index.ts @@ -1,7 +1,8 @@ +import { erc721 } from '@openzeppelin/wizard'; + export default async () => { return { - // @ts-ignore - 'contracts/SampleERC721.sol': (await import('raw-loader!./contracts/SampleERC721.sol')).default, + 'contracts/MyToken.sol': erc721.print(), // @ts-ignore 'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default, // @ts-ignore @@ -11,6 +12,6 @@ export default async () => { // @ts-ignore 'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default, // @ts-ignore - 'tests/SampleERC721_test.sol': (await import('raw-loader!./tests/SampleERC721_test.sol')).default + 'tests/MyToken_test.sol': (await import('raw-loader!./tests/MyToken_test.sol')).default } } \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_ethers.ts b/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_ethers.ts index 185fd7a5fa..a6c8cf30e5 100644 --- a/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_ethers.ts +++ b/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_ethers.ts @@ -2,7 +2,7 @@ import { deploy } from './ethers-lib' (async () => { try { - const result = await deploy('SampleERC721', ['testNFT', 'TNFT']) + const result = await deploy('MyToken', []) console.log(`address: ${result.address}`) } catch (e) { console.log(e.message) diff --git a/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_web3.ts b/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_web3.ts index 2fec0c3b28..b22b119246 100644 --- a/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_web3.ts +++ b/libs/remix-ws-templates/src/templates/ozerc721/scripts/deploy_with_web3.ts @@ -2,7 +2,7 @@ import { deploy } from './web3-lib' (async () => { try { - const result = await deploy('SampleERC721', ['testToken', 'TST']) + const result = await deploy('MyToken', []) console.log(`address: ${result.address}`) } catch (e) { console.log(e.message) diff --git a/libs/remix-ws-templates/src/templates/ozerc721/tests/MyToken_test.sol b/libs/remix-ws-templates/src/templates/ozerc721/tests/MyToken_test.sol new file mode 100644 index 0000000000..0fb1d12117 --- /dev/null +++ b/libs/remix-ws-templates/src/templates/ozerc721/tests/MyToken_test.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; +import "remix_tests.sol"; +import "../contracts/MyToken.sol"; + +contract MyTokenTest { + + MyToken s; + function beforeAll () public { + s = new MyToken(); + } + + function testTokenNameAndSymbol () public { + Assert.equal(s.name(), "MyToken", "token name did not match"); + Assert.equal(s.symbol(), "MTK", "token symbol did not match"); + } +} \ No newline at end of file diff --git a/libs/remix-ws-templates/src/templates/ozerc721/tests/SampleERC721_test.sol b/libs/remix-ws-templates/src/templates/ozerc721/tests/SampleERC721_test.sol deleted file mode 100644 index 27a4c46826..0000000000 --- a/libs/remix-ws-templates/src/templates/ozerc721/tests/SampleERC721_test.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity >=0.7.0 <0.9.0; -import "remix_tests.sol"; -import "../contracts/SampleERC721.sol"; - -contract SampleERC721Test { - - SampleERC721 s; - function beforeAll () public { - s = new SampleERC721("TestNFT", "TNFT"); - } - - function testTokenNameAndSymbol () public { - Assert.equal(s.name(), "TestNFT", "token name did not match"); - Assert.equal(s.symbol(), "TNFT", "token symbol did not match"); - } -} \ No newline at end of file From e11ac5768a5cec13f9562f7464de43e9b4a56523 Mon Sep 17 00:00:00 2001 From: Aniket-Engg Date: Wed, 7 Sep 2022 19:14:05 +0530 Subject: [PATCH 30/34] e2e update --- apps/remix-ide-e2e/src/tests/workspace.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/workspace.test.ts b/apps/remix-ide-e2e/src/tests/workspace.test.ts index d7314e3c5f..477ed83b7b 100644 --- a/apps/remix-ide-e2e/src/tests/workspace.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace.test.ts @@ -172,7 +172,7 @@ module.exports = { .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC721.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemscripts/deploy_with_web3.ts"]') // check js and ts files are not transformed @@ -204,7 +204,7 @@ module.exports = { 'Incorrect content') }) .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/SampleERC721_test.sol"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemtests/MyToken_test.sol"]') }, // WORKSPACE TEMPLATES E2E END From 67ca3a5d190b005c43450c4c4163c7d351535f88 Mon Sep 17 00:00:00 2001 From: Aniket-Engg Date: Wed, 7 Sep 2022 19:26:11 +0530 Subject: [PATCH 31/34] e2e fix --- apps/remix-ide-e2e/src/tests/erc721.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/erc721.test.ts b/apps/remix-ide-e2e/src/tests/erc721.test.ts index b759d457a8..efc18810b1 100644 --- a/apps/remix-ide-e2e/src/tests/erc721.test.ts +++ b/apps/remix-ide-e2e/src/tests/erc721.test.ts @@ -26,19 +26,19 @@ module.exports = { .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .pause(100) .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/SampleERC721.sol"]') - .openFile('contracts/SampleERC721.sol') - .verifyContracts(['SampleERC721']) + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') + .openFile('contracts/MyToken.sol') + .verifyContracts(['MyToken']) // deploy contract .clickLaunchIcon('udapp') - .selectContract('SampleERC721') - .createContract('E,E') + .selectContract('MyToken') + .createContract('') .testFunction('last', { status: 'true Transaction mined and execution succeed', 'decoded input': { - 'string tokenName': 'E', - 'string tokenSymbol': 'E' + 'string tokenName': 'MyToken', + 'string tokenSymbol': 'MTK' } }).end() } From 0288b250ef72100e63523938721d7d56161f787e Mon Sep 17 00:00:00 2001 From: Aniket-Engg Date: Wed, 7 Sep 2022 19:46:24 +0530 Subject: [PATCH 32/34] fix input in e2e --- apps/remix-ide-e2e/src/tests/erc721.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/erc721.test.ts b/apps/remix-ide-e2e/src/tests/erc721.test.ts index efc18810b1..52535ebf3a 100644 --- a/apps/remix-ide-e2e/src/tests/erc721.test.ts +++ b/apps/remix-ide-e2e/src/tests/erc721.test.ts @@ -36,10 +36,7 @@ module.exports = { .testFunction('last', { status: 'true Transaction mined and execution succeed', - 'decoded input': { - 'string tokenName': 'MyToken', - 'string tokenSymbol': 'MTK' - } + 'decoded input': {} }).end() } } From 77d68dea37f0ddbff50492473b51f0ccaf82086f Mon Sep 17 00:00:00 2001 From: David Disu Date: Fri, 2 Sep 2022 15:25:15 +0100 Subject: [PATCH 33/34] Add workflow for running solidity unit tests --- .github/workflows/run-sut.yml | 20 +++++++++++++ .../remix-ide/contracts/tests/Ballot_test.sol | 28 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 .github/workflows/run-sut.yml create mode 100644 apps/remix-ide/contracts/tests/Ballot_test.sol diff --git a/.github/workflows/run-sut.yml b/.github/workflows/run-sut.yml new file mode 100644 index 0000000000..e6b8d98221 --- /dev/null +++ b/.github/workflows/run-sut.yml @@ -0,0 +1,20 @@ + +name: Running Solidity Unit Tests +on: [push] + +jobs: + run_sol_contracts_job: + runs-on: ubuntu-latest + name: A job to run solidity unit tests on github actions CI + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Environment Setup + uses: actions/setup-node@v3 + with: + node-version: 14.17.6 + - name: Run SUT Action + uses: EthereumRemix/sol-test@v1 + with: + test-path: 'apps/remix-ide/contracts/tests' + compiler-version: '0.8.15' \ No newline at end of file diff --git a/apps/remix-ide/contracts/tests/Ballot_test.sol b/apps/remix-ide/contracts/tests/Ballot_test.sol new file mode 100644 index 0000000000..452a5433b4 --- /dev/null +++ b/apps/remix-ide/contracts/tests/Ballot_test.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; +import "remix_tests.sol"; // this import is automatically injected by Remix. +import "hardhat/console.sol"; +import "../ballot.sol"; + +contract BallotTest { + + bytes32[] proposalNames; + + Ballot ballotToTest; + function beforeAll () public { + proposalNames.push(bytes32("candidate1")); + ballotToTest = new Ballot(proposalNames); + } + + function checkWinningProposal () public { + console.log("Running checkWinningProposal"); + ballotToTest.vote(0); + Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal"); + Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name"); + } + + function checkWinninProposalWithReturnValue () public view returns (bool) { + return ballotToTest.winningProposal() == 0; + } +} \ No newline at end of file From a40839f405d16864e31745f8945a05f804087a6c Mon Sep 17 00:00:00 2001 From: David Disu Date: Fri, 2 Sep 2022 15:28:10 +0100 Subject: [PATCH 34/34] Update ballot contract --- apps/remix-ide/contracts/ballot.sol | 151 ++++++++++++++-------------- 1 file changed, 73 insertions(+), 78 deletions(-) diff --git a/apps/remix-ide/contracts/ballot.sol b/apps/remix-ide/contracts/ballot.sol index d55734ed9a..ffcc6c3609 100644 --- a/apps/remix-ide/contracts/ballot.sol +++ b/apps/remix-ide/contracts/ballot.sol @@ -1,10 +1,13 @@ -pragma solidity ^0.4.0; +// SPDX-License-Identifier: GPL-3.0 -/// @title Voting with delegation. +pragma solidity >=0.7.0 <0.9.0; + +/** + * @title Ballot + * @dev Implements voting process along with vote delegation + */ contract Ballot { - // This declares a new complex type which will - // be used for variables later. - // It will represent a single voter. + struct Voter { uint weight; // weight is accumulated by delegation bool voted; // if true, that person already voted @@ -12,33 +15,31 @@ contract Ballot { uint vote; // index of the voted proposal } - // This is a type for a single proposal. struct Proposal { + // If you can limit the length to a certain number of bytes, + // always use one of bytes1 to bytes32 because they are much cheaper bytes32 name; // short name (up to 32 bytes) uint voteCount; // number of accumulated votes } address public chairperson; - // This declares a state variable that - // stores a \`Voter\` struct for each possible address. mapping(address => Voter) public voters; - // A dynamically-sized array of \`Proposal\` structs. Proposal[] public proposals; - /// Create a new ballot to choose one of \`proposalNames\`. - function Ballot(bytes32[] proposalNames) { + /** + * @dev Create a new ballot to choose one of 'proposalNames'. + * @param proposalNames names of proposals + */ + constructor(bytes32[] memory proposalNames) { chairperson = msg.sender; voters[chairperson].weight = 1; - // For each of the provided proposal names, - // create a new proposal object and add it - // to the end of the array. for (uint i = 0; i < proposalNames.length; i++) { - // \`Proposal({...})\` creates a temporary - // Proposal object and \`proposals.push(...)\` - // appends it to the end of \`proposals\`. + // 'Proposal({...})' creates a temporary + // Proposal object and 'proposals.push(...)' + // appends it to the end of 'proposals'. proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 @@ -46,98 +47,92 @@ contract Ballot { } } - // Give \`voter\` the right to vote on this ballot. - // May only be called by \`chairperson\`. - function giveRightToVote(address voter) { - if (msg.sender != chairperson || voters[voter].voted) { - // \`throw\` terminates and reverts all changes to - // the state and to Ether balances. It is often - // a good idea to use this if functions are - // called incorrectly. But watch out, this - // will also consume all provided gas. - throw; - } + /** + * @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'. + * @param voter address of voter + */ + function giveRightToVote(address voter) public { + require( + msg.sender == chairperson, + "Only chairperson can give right to vote." + ); + require( + !voters[voter].voted, + "The voter already voted." + ); + require(voters[voter].weight == 0); voters[voter].weight = 1; } - /// Delegate your vote to the voter \`to\`. - function delegate(address to) { - // assigns reference - Voter sender = voters[msg.sender]; - if (sender.voted) - throw; - - // Forward the delegation as long as - // \`to\` also delegated. - // In general, such loops are very dangerous, - // because if they run too long, they might - // need more gas than is available in a block. - // In this case, the delegation will not be executed, - // but in other situations, such loops might - // cause a contract to get "stuck" completely. - while ( - voters[to].delegate != address(0) && - voters[to].delegate != msg.sender - ) { + /** + * @dev Delegate your vote to the voter 'to'. + * @param to address to which vote is delegated + */ + function delegate(address to) public { + Voter storage sender = voters[msg.sender]; + require(!sender.voted, "You already voted."); + require(to != msg.sender, "Self-delegation is disallowed."); + + while (voters[to].delegate != address(0)) { to = voters[to].delegate; - } - // We found a loop in the delegation, not allowed. - if (to == msg.sender) { - throw; + // We found a loop in the delegation, not allowed. + require(to != msg.sender, "Found loop in delegation."); } - - // Since \`sender\` is a reference, this - // modifies \`voters[msg.sender].voted\` sender.voted = true; sender.delegate = to; - Voter delegate = voters[to]; - if (delegate.voted) { + Voter storage delegate_ = voters[to]; + if (delegate_.voted) { // If the delegate already voted, // directly add to the number of votes - proposals[delegate.vote].voteCount += sender.weight; + proposals[delegate_.vote].voteCount += sender.weight; } else { // If the delegate did not vote yet, // add to her weight. - delegate.weight += sender.weight; + delegate_.weight += sender.weight; } } - /// Give your vote (including votes delegated to you) - /// to proposal \`proposals[proposal].name\`. - function vote(uint proposal) { - Voter sender = voters[msg.sender]; - if (sender.voted) - throw; + /** + * @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'. + * @param proposal index of proposal in the proposals array + */ + function vote(uint proposal) public { + Voter storage sender = voters[msg.sender]; + require(sender.weight != 0, "Has no right to vote"); + require(!sender.voted, "Already voted."); sender.voted = true; sender.vote = proposal; - // If \`proposal\` is out of the range of the array, + // If 'proposal' is out of the range of the array, // this will throw automatically and revert all // changes. proposals[proposal].voteCount += sender.weight; } - /// @dev Computes the winning proposal taking all - /// previous votes into account. - function winningProposal() constant - returns (uint winningProposal) + /** + * @dev Computes the winning proposal taking all previous votes into account. + * @return winningProposal_ index of winning proposal in the proposals array + */ + function winningProposal() public view + returns (uint winningProposal_) { uint winningVoteCount = 0; for (uint p = 0; p < proposals.length; p++) { if (proposals[p].voteCount > winningVoteCount) { winningVoteCount = proposals[p].voteCount; - winningProposal = p; + winningProposal_ = p; } } } - - // Calls winningProposal() function to get the index - // of the winner contained in the proposals array and then - // returns the name of the winner - function winnerName() constant - returns (bytes32 winnerName) + + /** + * @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then + * @return winnerName_ the name of the winner + */ + function winnerName() public view + returns (bytes32 winnerName_) { - winnerName = proposals[winningProposal()].name; + winnerName_ = proposals[winningProposal()].name; } -} +} \ No newline at end of file