diff --git a/.circleci/config.yml b/.circleci/config.yml index 118a058cc1..720f78a964 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -212,8 +212,9 @@ jobs: - run: npm run build:libs - run: npm run downloadsolc_root - run: npm run build - - run: ./apps/remix-ide/ci/build_and_publish_docker_images.sh + - run: ./apps/remix-ide/ci/copy_resources.sh - run: ./apps/remix-ide/ci/publishIpfs + - run: ./apps/remix-ide/ci/build_and_publish_docker_images.sh deploy-remix-alpha: docker: @@ -245,6 +246,36 @@ jobs: ./apps/remix-ide/ci/deploy_from_travis_remix-alpha.sh; fi + deploy-remix-beta: + docker: + # specify the version you desire here + - image: circleci/node:10.18.0-browsers + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + resource_class: xlarge + # documented at https://circleci.com/docs/2.0/circleci-images/ + # - image: circleci/mongo:3.4.4 + environment: + - COMMIT_AUTHOR_EMAIL: "yann@ethereum.org" + - COMMIT_AUTHOR: "Circle CI" + - FILES_TO_PACKAGE: "dist/apps/remix-ide/assets dist/apps/remix-ide/index.html dist/apps/remix-ide/main.js dist/apps/remix-ide/polyfills.js dist/apps/remix-ide/runtime.js dist/apps/remix-ide/vendor.js dist/apps/remix-ide/favicon.ico" + working_directory: ~/remix-project + + steps: + - checkout + - run: npm install + - run: npm run lint + - run: npm run build:libs + - run: npm run downloadsolc_root + - run: npm run build + - run: + name: Deploy + command: | + if [ "${CIRCLE_BRANCH}" == "remix_beta" ]; then + ./apps/remix-ide/ci/deploy_from_travis_remix-beta.sh; + fi + workflows: version: 2 build_all: @@ -279,4 +310,12 @@ workflows: - remix-ide-run-deploy filters: branches: - only: master \ No newline at end of file + only: master + - deploy-remix-beta: + requires: + - remix-ide-chrome + - remix-ide-firefox + - remix-ide-run-deploy + filters: + branches: + only: remix_beta diff --git a/.env b/.env index d3bca64c0b..8db17993d7 100644 --- a/.env +++ b/.env @@ -1,3 +1,4 @@ gist_token= account_passphrase= -account_password= \ No newline at end of file +account_password= +NODE_OPTIONS=--max-old-space-size=2048 \ No newline at end of file diff --git a/apps/remix-ide-e2e/seleniumConfig.js b/apps/remix-ide-e2e/seleniumConfig.js index 210c9b6fa6..eaece8bc7b 100644 --- a/apps/remix-ide-e2e/seleniumConfig.js +++ b/apps/remix-ide-e2e/seleniumConfig.js @@ -1,3 +1,4 @@ +/* eslint-disable */ module.exports = { version: '3.8.1', baseURL: 'https://selenium-release.storage.googleapis.com', diff --git a/apps/remix-ide-e2e/src/commands/checkVariableDebug.ts b/apps/remix-ide-e2e/src/commands/checkVariableDebug.ts index f899c6f013..8769339a1a 100644 --- a/apps/remix-ide-e2e/src/commands/checkVariableDebug.ts +++ b/apps/remix-ide-e2e/src/commands/checkVariableDebug.ts @@ -33,7 +33,7 @@ function checkDebug (browser: NightwatchBrowser, id: string, debugValue: Nightwa } const equal = deepequal(debugValue, value) if (!equal) { - browser.assert.fail('checkDebug on ' + id, 'info about error\n ' + JSON.stringify(debugValue) + '\n ' + JSON.stringify(value), '') + browser.assert.fail(JSON.stringify(value), 'info about error\n ' + JSON.stringify(debugValue) + '\n ' + JSON.stringify(value), '') } done() }) diff --git a/apps/remix-ide-e2e/src/commands/clickFunction.ts b/apps/remix-ide-e2e/src/commands/clickFunction.ts index 20e44766d4..12976f53d5 100644 --- a/apps/remix-ide-e2e/src/commands/clickFunction.ts +++ b/apps/remix-ide-e2e/src/commands/clickFunction.ts @@ -14,7 +14,7 @@ class ClickFunction extends EventEmitter { done() }) }) - .click('.instance button[title="' + fnFullName + '"]') + .scrollAndClick('.instance button[title="' + fnFullName + '"]') .pause(2000) .perform(() => { this.emit('complete') diff --git a/apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts b/apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts index 274abb15e7..0202bc2872 100644 --- a/apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts +++ b/apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts @@ -2,11 +2,8 @@ import { NightwatchBrowser } from 'nightwatch' import EventEmitter from "events" class GoToVmTraceStep extends EventEmitter { command (this: NightwatchBrowser, step: number, incr?: number): NightwatchBrowser { - this.api.perform((done) => { - goToVMtraceStep(this.api, step, incr, () => { - done() - this.emit('complete') - }) + goToVMtraceStep(this.api, step, incr, () => { + this.emit('complete') }) return this } diff --git a/apps/remix-ide-e2e/src/examples/example-contracts.ts b/apps/remix-ide-e2e/src/examples/example-contracts.ts index 1247244df0..a02bf6d608 100644 --- a/apps/remix-ide-e2e/src/examples/example-contracts.ts +++ b/apps/remix-ide-e2e/src/examples/example-contracts.ts @@ -1,6 +1,6 @@ 'use strict' -const storage = `pragma solidity >=0.4.22 <0.7.0; +const storage = `pragma solidity >=0.7.0 <0.8.0; /** * @title Storage @@ -27,7 +27,7 @@ contract Storage { } }` -const owner = `pragma solidity >=0.4.22 <0.7.0; +const owner = `pragma solidity >=0.7.0 <0.8.0; /** * @title Owner @@ -54,7 +54,7 @@ contract Owner { /** * @dev Set contract deployer as owner */ - constructor() public { + constructor() { owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor emit OwnerSet(address(0), owner); } @@ -77,7 +77,7 @@ contract Owner { } }` -const ballot = `pragma solidity >=0.4.22 <0.7.0; +const ballot = `pragma solidity >=0.7.0 <0.8.0; /** * @title Ballot @@ -109,7 +109,7 @@ contract Ballot { * @dev Create a new ballot to choose one of 'proposalNames'. * @param proposalNames names of proposals */ - constructor(bytes32[] memory proposalNames) public { + constructor(bytes32[] memory proposalNames) { chairperson = msg.sender; voters[chairperson].weight = 1; @@ -281,7 +281,7 @@ contract Ballot { } }` -const ballotTest = `pragma solidity >=0.4.22 <0.7.0; +const ballotTest = `pragma solidity >=0.4.22 <0.8.0; import "remix_tests.sol"; // this import is automatically injected by Remix. import "../3_Ballot.sol"; diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts index 242ec06d49..46cf2481c9 100644 --- a/apps/remix-ide-e2e/src/tests/debugger.test.ts +++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts @@ -16,7 +16,7 @@ module.exports = { 'Should launch debugger': function (browser: NightwatchBrowser) { browser.addFile('blah.sol', sources[0]['browser/blah.sol']) .clickLaunchIcon('udapp') - .waitForElementPresent('*[title="Deploy - transact (not payable)"]') + .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 45000) .click('*[title="Deploy - transact (not payable)"]') .debugTransaction(0) .assert.containsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER') @@ -42,7 +42,6 @@ module.exports = { .waitForElementVisible('*[data-id="slider"]') .click('*[data-id="slider"]') .setValue('*[data-id="slider"]', '50') - .pause(2000) .assert.containsText('*[data-id="solidityLocals"]', 'no locals') .assert.containsText('*[data-id="stepdetail"]', 'vm trace step:\n92') }, @@ -79,8 +78,10 @@ module.exports = { browser .clickLaunchIcon('solidity') .setSolidityCompilerVersion('soljson-v0.6.12+commit.27d51765.js') - .clickLaunchIcon('udapp') + .pause(2000) + .clickLaunchIcon('udapp') .testContracts('externalImport.sol', sources[1]['browser/externalImport.sol'], ['ERC20']) + .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 35000) .selectContract('ERC20') .createContract('"tokenName", "symbol"') .debugTransaction(2) @@ -93,6 +94,88 @@ module.exports = { _decimals = 18; }`) != -1, 'current displayed content is not from the ERC20 source code') + }) + }, + + 'Should display correct source highlighting while debugging a contract which has ABIEncoderV2': function (browser: NightwatchBrowser) { + /* + localVariable_step266_ABIEncoder and localVariable_step717_ABIEncoder + still contains unwanted values (related to decoding calldata types) + This is still an issue @todo(https://github.com/ethereum/remix-project/issues/481), so this test will fail when this issue is fixed + */ + browser + .clickLaunchIcon('solidity') + .setSolidityCompilerVersion('soljson-v0.6.12+commit.27d51765.js') + .clickLaunchIcon('udapp') + .testContracts('withABIEncoderV2.sol', sources[2]['browser/withABIEncoderV2.sol'], ['test']) + .selectContract('test') + .createContract('') + .clickInstance(2) + .clickFunction('test1 - transact (not payable)', {types: 'bytes userData', values: '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000015b38da6a701c568545dcfcb03fcb875f56beddc4'}) + .debugTransaction(4) + .pause(2000) + .goToVMTraceStep(261) + .pause(1000) + /* + for the test below: + source highlight should remain line `bytes32 idAsk = abi.decode(userData[:33], (bytes32));` + At this vmtrace index, the sourcemap has file = -1 because the execution is in the generated sources (ABIEncoderV2) + the atIndex of SourceLocationTracker was buggy and return an incorrect value, this is fixed + But the debugger uses now validSourcelocation, which means file is not -1. + In that case the source highlight at 261 should be the same as for step 262 + */ + .waitForElementPresent('.highlightLine7') + .goToVMTraceStep(266) + .pause(1000) + .checkVariableDebug('soliditylocals', localVariable_step266_ABIEncoder) // locals should not be initiated at this point, only idAsk should + .goToVMTraceStep(717) + .pause(5000) + .checkVariableDebug('soliditylocals', localVariable_step717_ABIEncoder) // all locals should be initiaed + .clickLaunchIcon('udapp') + .clickInstance(2) + }, + + 'Should load more solidity locals array': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('solidity') + .testContracts('locals.sol', sources[3]['browser/locals.sol'], ['testLocals']) + .clickLaunchIcon('udapp') + .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 40000) + .createContract('') + .pause(2000) + .clickInstance(3) + .clickFunction('t - transact (not payable)') + .pause(2000) + .debugTransaction(6) + .waitForElementVisible('*[data-id="slider"]') + .click('*[data-id="slider"]') + .setValue('*[data-id="slider"]', '5000') + .waitForElementPresent('*[data-id="treeViewTogglearray"]') + .click('*[data-id="treeViewTogglearray"]') + .waitForElementPresent('*[data-id="treeViewLoadMore"]') + .click('*[data-id="treeViewLoadMore"]') + .assert.containsText('*[data-id="solidityLocals"]', '149: 0 uint256') + .notContainsText('*[data-id="solidityLocals"]', '150: 0 uint256') + }, + + 'Should debug using generated sources': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('solidity') + .setSolidityCompilerVersion('soljson-v0.7.2+commit.51b20bc0.js') + .clickLaunchIcon('udapp') + .pause(2000) + .testContracts('withGeneratedSources.sol', sources[4]['browser/withGeneratedSources.sol'], ['A']) + .createContract('') + .clickInstance(4) + .clickFunction('f - transact (not payable)', {types: 'uint256[] ', values: '[]'}) + .debugTransaction(8) + .pause(2000) + .click('*[data-id="debuggerTransactionStartButton"]') // stop debugging + .click('*[data-id="debugGeneratedSourcesLabel"]') // select debug with generated sources + .click('*[data-id="debuggerTransactionStartButton"]') // start debugging + .pause(2000) + .getEditorValue((content) => { + browser.assert.ok(content.indexOf('if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }') != -1, 'current displayed content is not a generated source') }) .end() }, @@ -104,7 +187,7 @@ const sources = [ { 'browser/blah.sol': { content: ` - pragma solidity >=0.4.22 <0.6.0; + pragma solidity >=0.7.0 <0.8.0; contract Kickstarter { @@ -119,9 +202,9 @@ const sources = [ Project[] public projects; - constructor() public { + constructor() { - } + } function createProject(string memory name, uint goal) public { Project storage project = projects[projects.length]; @@ -130,13 +213,124 @@ const sources = [ project.state = State.Started; project.goal = goal; } - } + } ` } }, { 'browser/externalImport.sol': {content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol"; contract test7 {}'} + }, + { + 'browser/withABIEncoderV2.sol': {content: ` + pragma experimental ABIEncoderV2; + + contract test { + // 000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000015b38da6a701c568545dcfcb03fcb875f56beddc4 + // 0000000000000000000000000000000000000000000000000000000000000002 + function test1 (bytes calldata userData) external returns (bytes memory, bytes32, bytes32, uint) { + bytes32 idAsk = abi.decode(userData[:33], (bytes32)); + bytes32 idOffer = abi.decode(userData[32:64], (bytes32)); + + bytes memory ro = abi.encodePacked(msg.sender, msg.sender, idAsk, idOffer); + return (ro, idAsk, idOffer, userData.length); + } + + + function testgp (bytes calldata userData) external returns (bytes4) { + return abi.decode(userData[:4], (bytes4)); + } +} + `} + }, + { + 'browser/locals.sol': { + content: ` + pragma solidity ^0.7.0; + contract testLocals { + function t () public { + uint[] memory array = new uint[](150); + for (uint k = 0; k < 150; k++) { + array[k] = k; + } + } + } + ` + } + }, + { + 'browser/withGeneratedSources.sol': { + content: ` + // SPDX-License-Identifier: GPL-3.0 + pragma experimental ABIEncoderV2; + contract A { + function f(uint[] memory) public returns (uint256) { } + } + ` + } } ] +const localVariable_step266_ABIEncoder = { + "<1>": { + "length": "0xNaN", + "type": "bytes", + "value": "0x" + }, + "<2>": { + "type": "bytes32", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "<3>": { + "type": "bytes32", + "value": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "<4>": { + "type": "uint256", + "value": "0" + }, + "idAsk": { + "type": "bytes32", + "value": "0x0000000000000000000000000000000000000000000000000000000000000002" + }, + "userData": { + "error": "", + "type": "bytes" + } +} +const localVariable_step717_ABIEncoder = { + "<1>": { + "length": "0xd0", + "type": "bytes", + "value": "0x5b38da6a701c568545dcfcb03fcb875f56beddc45b38da6a701c568545dcfcb03fcb875f56beddc400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001" + }, + "<2>": { + "type": "bytes32", + "value": "0x0000000000000000000000000000000000000000000000000000000000000002" + }, + "<3>": { + "type": "bytes32", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "<4>": { + "type": "uint256", + "value": "84" + }, + "idAsk": { + "type": "bytes32", + "value": "0x0000000000000000000000000000000000000000000000000000000000000002" + }, + "idOffer": { + "type": "bytes32", + "value": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "ro": { + "length": "0xd0", + "type": "bytes", + "value": "0x5b38da6a701c568545dcfcb03fcb875f56beddc45b38da6a701c568545dcfcb03fcb875f56beddc400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001" + }, + "userData": { + "error": "", + "type": "bytes" + } +} diff --git a/apps/remix-ide-e2e/src/tests/fileManager_api.test.ts b/apps/remix-ide-e2e/src/tests/fileManager_api.test.ts index f9416e4cff..ea48ae6f47 100644 --- a/apps/remix-ide-e2e/src/tests/fileManager_api.test.ts +++ b/apps/remix-ide-e2e/src/tests/fileManager_api.test.ts @@ -55,7 +55,7 @@ module.exports = { .addFile('copyFile.js', { content: executeCopyFile }) .executeScript(`remix.exeCurrent()`) .pause(2000) - .journalLastChildIncludes('pragma solidity >=0.4.22 <0.7.0;') + .journalLastChildIncludes('pragma solidity >=0.7.0 <0.8.0;') }, 'Should execute `rename` api from file manager external api': function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts index a3ddde4d1d..cff0a64686 100644 --- a/apps/remix-ide-e2e/src/tests/generalSettings.test.ts +++ b/apps/remix-ide-e2e/src/tests/generalSettings.test.ts @@ -178,8 +178,8 @@ const remixIdeThemes = { primary: '#2A9FD6', secondary: '#555', success: '#77B300', - info: '#9933CC', - warning: '#FF8800', - danger: '#CC0000' + info: '#93C', + warning: '#F80', + danger: '#C00' } } diff --git a/apps/remix-ide-e2e/src/tests/runAndDeploy.ts b/apps/remix-ide-e2e/src/tests/runAndDeploy.ts index ce3d664b99..532fbd2027 100644 --- a/apps/remix-ide-e2e/src/tests/runAndDeploy.ts +++ b/apps/remix-ide-e2e/src/tests/runAndDeploy.ts @@ -44,12 +44,12 @@ module.exports = { .addFile('Greet.sol', sources[0]['browser/Greet.sol']) .clickLaunchIcon('udapp') .selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c') - .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]') + .waitForElementPresent('*[data-id="Deploy - transact (not payable)"]', 45000) .click('*[data-id="Deploy - transact (not payable)"]') .pause(5000) - .testFunction('0xc39ee005c1e1368c84f02e458de4b41dbb966631a8714d15ef8362dada249ede', { + .testFunction('0x82f6c88a909b49d6cc003fb302a6e0184c3f08e942b62e1c95dec326d4c6020b', { status: 'true Transaction mined and execution succeed', - 'transaction hash': '0xc39ee005c1e1368c84f02e458de4b41dbb966631a8714d15ef8362dada249ede' + 'transaction hash': '0x82f6c88a909b49d6cc003fb302a6e0184c3f08e942b62e1c95dec326d4c6020b' }) }, @@ -191,7 +191,7 @@ const sources = [ 'browser/Greet.sol': { content: ` - pragma solidity ^0.6.0; + pragma solidity ^0.7.0; contract helloWorld { string public message; diff --git a/apps/remix-ide-e2e/src/tests/signingMessage.test.ts b/apps/remix-ide-e2e/src/tests/signingMessage.test.ts index 67c405d30a..28a88d3d22 100644 --- a/apps/remix-ide-e2e/src/tests/signingMessage.test.ts +++ b/apps/remix-ide-e2e/src/tests/signingMessage.test.ts @@ -55,7 +55,7 @@ module.exports = { const sources = [ { 'browser/signMassage.sol': {content: ` - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.4.22 <0.8.0; contract SignMassageTest { function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) public pure returns (address) { return ecrecover(h, v, r, s); diff --git a/apps/remix-ide-e2e/src/tests/solidityImport.test.ts b/apps/remix-ide-e2e/src/tests/solidityImport.test.ts index fa4c9ff375..587faa82b1 100644 --- a/apps/remix-ide-e2e/src/tests/solidityImport.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityImport.test.ts @@ -57,8 +57,22 @@ module.exports = { browser .addFile('Untitled7.sol', sources[6]['browser/Untitled7.sol']) .clickLaunchIcon('fileExplorers') - .verifyContracts(['test11', 'ERC20', 'SafeMath'], {wait: 10000}) - .end() + .verifyContracts(['test11', 'ERC20', 'SafeMath'], {wait: 10000}) + }, + + 'Test switch to a github import from a solidity warning': function (browser: NightwatchBrowser) { + browser + .setSolidityCompilerVersion('soljson-v0.7.4+commit.3f05b770.js') + .addFile('Untitled8.sol', sources[7]['browser/Untitled8.sol']) + .clickLaunchIcon('fileExplorers') + .clickLaunchIcon('solidity') + .waitForElementPresent('[data-id="compiledErrors"] div:nth-child(3)') + .click('[data-id="compiledErrors"] div:nth-child(3)') // select the second warning which point to ERC20 code + .getEditorValue((content) => { + browser.assert.ok(content.indexOf(`contract ERC20 is Context, IERC20`) != -1, + 'current displayed content should be from the ERC20 source code') + }) + .end() }, tearDown: sauce } @@ -85,5 +99,8 @@ const sources = [ }, { 'browser/Untitled7.sol': {content: 'import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"; contract test11 {}'} + }, + { + 'browser/Untitled8.sol': {content: 'import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"; contract test12 {}'} } ] diff --git a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts index 80757def5d..512c00a1bd 100644 --- a/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts @@ -90,7 +90,7 @@ module.exports = { .scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]') .pause(5000) .click('*[data-id="testTabRunTestsTabStopAction"]') - .pause(1000) + // .pause(1000) .assert.containsText('*[data-id="testTabRunTestsTabStopAction"]', 'Stopping') .waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000) .assert.containsText('*[data-id="testTabSolidityUnitTestsOutput"]', 'browser/tests/ks2b_test.sol') @@ -172,7 +172,7 @@ function runTests (browser: NightwatchBrowser) { .pause(500) .scrollAndClick('#runTestsTabRunAction') .waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 40000) - .waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 7000) + .waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 10000) .assert.containsText('#solidityUnittestsOutput', 'browser/tests/4_Ballot_test.sol') .assert.containsText('#solidityUnittestsOutput', '✓ Check winning proposal') .assert.containsText('#solidityUnittestsOutput', '✓ Check winnin proposal with return value') @@ -183,12 +183,12 @@ const sources = [ { 'browser/simple_storage.sol': { content: ` - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.4.22 <0.8.0; contract SimpleStorage { uint public storedData; - constructor() public { + constructor() { storedData = 100; } @@ -204,7 +204,7 @@ const sources = [ }, 'browser/tests/simple_storage_test.sol': { content: ` - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.4.22 <0.8.0; import "remix_tests.sol"; import "../simple_storage.sol"; @@ -233,7 +233,7 @@ const sources = [ }, 'browser/ks2a.sol': { content: ` - pragma solidity >=0.4.22 <0.6.0; + pragma solidity >=0.4.22 <0.8.0; contract Kickstarter { enum State { Started, Completed } @@ -246,14 +246,14 @@ const sources = [ State state; mapping(address => uint) funders; // added } - + uint numProjects; Project[] public projects; - constructor() public { + constructor() { } function createProject(string memory name, uint goal) public { - projects.length++; // new line + projects.push(); // new line Project storage project = projects[projects.length - 1]; project.name = name; project.goal = goal; @@ -287,7 +287,7 @@ const sources = [ }, 'browser/tests/ks2b_test.sol': { content: ` - pragma solidity >=0.4.22 <0.6.0; + pragma solidity >=0.4.22 <0.8.0; pragma experimental ABIEncoderV2; import "remix_tests.sol"; // this import is automatically injected by Remix. @@ -336,7 +336,7 @@ const sources = [ } function checkProjectIsFundable () public { - kickstarter.fundProject.value(120000)(0); + kickstarter.fundProject{value:120000}(0); (address owner, string memory name, uint goal, uint fundsAvailable, uint amountContributed, Kickstarter.State state) = kickstarter.projects(0); Assert.equal(amountContributed, 120000, "contributed amount is incorrect"); } @@ -346,7 +346,7 @@ const sources = [ }, 'browser/compilationError_test.sol': { content: ` - pragma solidity ^0.6.1; + pragma solidity ^0.7.0; contract failOnCompilation { fallback() { @@ -357,10 +357,10 @@ const sources = [ }, 'browser/tests/deployError_test.sol': { content: ` - pragma solidity ^0.6.0; + pragma solidity ^0.7.0; contract failingDeploy { - constructor() public { + constructor() { revert('Deploy Failed'); } } @@ -368,7 +368,7 @@ const sources = [ }, 'browser/tests/methodFailure_test.sol': { content: ` - pragma solidity ^0.6.0; + pragma solidity ^0.7.0; contract methodfailure { function add(uint a, uint b) public { diff --git a/apps/remix-ide-e2e/src/tests/staticAnalysis.test.ts b/apps/remix-ide-e2e/src/tests/staticAnalysis.test.ts index c7c37043d2..cf8750f159 100644 --- a/apps/remix-ide-e2e/src/tests/staticAnalysis.test.ts +++ b/apps/remix-ide-e2e/src/tests/staticAnalysis.test.ts @@ -6,12 +6,12 @@ import sauce from './sauce' const sources = [ { 'browser/Untitled.sol': {content: ` -pragma solidity >=0.4.22 <0.6.0; +pragma solidity >=0.6.0 <0.8.0; contract test1 { address test = tx.origin; } contract test2 {} contract TooMuchGas { uint x; - function() external { + fallback() external { x++; uint test; uint test1; @@ -36,6 +36,7 @@ function runTests (browser: NightwatchBrowser) { browser .waitForElementVisible('#icon-panel', 10000) .clickLaunchIcon('solidity') + .pause(10000) .testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['TooMuchGas', 'test1', 'test2']) .clickLaunchIcon('solidityStaticAnalysis') .click('#staticanalysisView button') diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts index b141204f68..a95f08aa6e 100644 --- a/apps/remix-ide-e2e/src/tests/terminal.test.ts +++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts @@ -80,7 +80,7 @@ const asyncAwait = ` resolve("Promise Resolved") }, 5000) }) - } + } var run = async () => { console.log('Waiting Promise') diff --git a/apps/remix-ide/ci/browser_tests_chrome.sh b/apps/remix-ide/ci/browser_tests_chrome.sh index 6a15f1207c..297aca6e84 100755 --- a/apps/remix-ide/ci/browser_tests_chrome.sh +++ b/apps/remix-ide/ci/browser_tests_chrome.sh @@ -2,22 +2,14 @@ set -e -setupRemixd () { - mkdir remixdSharedfolder - cd apps/remix-ide/contracts - echo 'sharing folder: ' - echo $PWD - ../../../node_modules/.bin/remixd -s $PWD --remix-ide http://127.0.0.1:8080 & - cd ../../.. -} - BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} echo "$BUILD_ID" TEST_EXITCODE=0 npm run ganache-cli & npm run serve & -setupRemixd +echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' & +npm run remixd & sleep 5 diff --git a/apps/remix-ide/ci/browser_tests_firefox.sh b/apps/remix-ide/ci/browser_tests_firefox.sh index d2058c1afb..8663a0763f 100755 --- a/apps/remix-ide/ci/browser_tests_firefox.sh +++ b/apps/remix-ide/ci/browser_tests_firefox.sh @@ -2,22 +2,14 @@ set -e -setupRemixd () { - mkdir remixdSharedfolder - cd apps/remix-ide/contracts - echo 'sharing folder: ' - echo $PWD - ../../../node_modules/.bin/remixd -s $PWD --remix-ide http://127.0.0.1:8080 & - cd ../../.. -} - BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} echo "$BUILD_ID" TEST_EXITCODE=0 npm run ganache-cli & npm run serve & -setupRemixd +echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' & +npm run remixd & sleep 5 diff --git a/apps/remix-ide/ci/browser_tests_run_deploy.sh b/apps/remix-ide/ci/browser_tests_run_deploy.sh index 38ec439873..aa3ff4885f 100755 --- a/apps/remix-ide/ci/browser_tests_run_deploy.sh +++ b/apps/remix-ide/ci/browser_tests_run_deploy.sh @@ -2,22 +2,12 @@ set -e -setupRemixd () { - mkdir remixdSharedfolder - cd apps/remix-ide/contracts - echo 'sharing folder: ' - echo $PWD - ../../../node_modules/.bin/remixd -s $PWD --remix-ide http://127.0.0.1:8080 & - cd ../../.. -} - BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}} echo "$BUILD_ID" TEST_EXITCODE=0 npm run ganache-cli & npm run serve & -setupRemixd sleep 5 diff --git a/apps/remix-ide/ci/build_and_publish_docker_images.sh b/apps/remix-ide/ci/build_and_publish_docker_images.sh index 249e259952..b51324fd77 100755 --- a/apps/remix-ide/ci/build_and_publish_docker_images.sh +++ b/apps/remix-ide/ci/build_and_publish_docker_images.sh @@ -7,11 +7,6 @@ if [ "$CIRCLE_BRANCH" == "master" ]; then export TAG="latest"; fi -rm -rf temp_publish_docker -mkdir temp_publish_docker -cp -r $FILES_TO_PACKAGE temp_publish_docker -ls - docker login --username $DOCKER_USER --password $DOCKER_PASS docker-compose -f docker-compose.yaml -f build.yaml build docker push remixproject/remix-ide:$TAG diff --git a/apps/remix-ide/ci/copy_resources.sh b/apps/remix-ide/ci/copy_resources.sh new file mode 100755 index 0000000000..ab0bf9ccb2 --- /dev/null +++ b/apps/remix-ide/ci/copy_resources.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +rm -rf temp_publish_docker +mkdir temp_publish_docker +cp -r $FILES_TO_PACKAGE temp_publish_docker +ls diff --git a/apps/remix-ide/ci/deploy_from_travis_remix-beta.sh b/apps/remix-ide/ci/deploy_from_travis_remix-beta.sh new file mode 100755 index 0000000000..764066c346 --- /dev/null +++ b/apps/remix-ide/ci/deploy_from_travis_remix-beta.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e + +SHA=`git rev-parse --short --verify HEAD` + +git config user.name "$COMMIT_AUTHOR" +git config user.email "$COMMIT_AUTHOR_EMAIL" +git checkout --orphan gh-pages +git rm --cached -r -f . +echo "# Automatic build" > README.md +echo "Built website from \`$SHA\`. See https://github.com/ethereum/remix-ide/ for details." >> README.md +echo "To use an offline copy, download \`remix-$SHA.zip\`." >> README.md +cp -r $FILES_TO_PACKAGE "./" +rm -rf dist +ls +FILES_TO_DEPLOY="assets index.html main.js polyfills.js runtime.js vendor.js favicon.ico" +# ZIP the whole directory +zip -r remix-$SHA.zip $FILES_TO_DEPLOY +# -f is needed because "build" is part of .gitignore +git add -f $FILES_TO_DEPLOY remix-$SHA.zip +git commit -m "Built website from {$SHA}." + +git push -f git@github.com:ethereum/remix-live-beta.git gh-pages diff --git a/apps/remix-ide/ci/makeMockCompiler.js b/apps/remix-ide/ci/makeMockCompiler.js index 5ad561ede7..155331d4cf 100644 --- a/apps/remix-ide/ci/makeMockCompiler.js +++ b/apps/remix-ide/ci/makeMockCompiler.js @@ -3,7 +3,7 @@ var fs = require('fs') var compiler = require('solc') var compilerInput = require('@remix-project/remix-solidity').CompilerInput -var defaultVersion = 'v0.6.6+commit.6c089d02' +var defaultVersion = 'v0.7.4+commit.3f05b770' const path = require('path') compiler.loadRemoteVersion(defaultVersion, (error, solcSnapshot) => { diff --git a/apps/remix-ide/src/app/editor/example-contracts.js b/apps/remix-ide/src/app/editor/example-contracts.js index a825255904..bbf637f8e4 100644 --- a/apps/remix-ide/src/app/editor/example-contracts.js +++ b/apps/remix-ide/src/app/editor/example-contracts.js @@ -1,6 +1,6 @@ 'use strict' -const storage = `pragma solidity >=0.4.22 <0.7.0; +const storage = `pragma solidity >=0.7.0 <0.8.0; /** * @title Storage @@ -27,7 +27,7 @@ contract Storage { } }` -const owner = `pragma solidity >=0.4.22 <0.7.0; +const owner = `pragma solidity >=0.7.0 <0.8.0; /** * @title Owner @@ -54,7 +54,7 @@ contract Owner { /** * @dev Set contract deployer as owner */ - constructor() public { + constructor() { owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor emit OwnerSet(address(0), owner); } @@ -77,7 +77,7 @@ contract Owner { } }` -const ballot = `pragma solidity >=0.4.22 <0.7.0; +const ballot = `pragma solidity >=0.7.0 <0.8.0; /** * @title Ballot @@ -109,7 +109,7 @@ contract Ballot { * @dev Create a new ballot to choose one of 'proposalNames'. * @param proposalNames names of proposals */ - constructor(bytes32[] memory proposalNames) public { + constructor(bytes32[] memory proposalNames) { chairperson = msg.sender; voters[chairperson].weight = 1; @@ -215,7 +215,7 @@ contract Ballot { } ` -var ballotTest = `pragma solidity >=0.4.22 <0.7.0; +var ballotTest = `pragma solidity >=0.7.0 <0.8.0; import "remix_tests.sol"; // this import is automatically injected by Remix. import "../3_Ballot.sol"; diff --git a/apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js b/apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js index d5b6449d35..a874880d61 100644 --- a/apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js +++ b/apps/remix-ide/src/app/tabs/compileTab/compilerContainer.js @@ -23,7 +23,7 @@ class CompilerContainer { timeout: 300, allversions: null, selectedVersion: null, - defaultVersion: 'soljson-v0.6.6+commit.6c089d02.js' // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler + defaultVersion: 'soljson-v0.7.4+commit.3f05b770.js' // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler } } diff --git a/apps/remix-ide/src/app/tabs/debugger/debuggerUI.js b/apps/remix-ide/src/app/tabs/debugger/debuggerUI.js index f37bda0d0c..dc3ce73c57 100644 --- a/apps/remix-ide/src/app/tabs/debugger/debuggerUI.js +++ b/apps/remix-ide/src/app/tabs/debugger/debuggerUI.js @@ -22,6 +22,26 @@ var css = csjs` .statusMessage { margin-left: 15px; } + + .debuggerConfig { + display: flex; + align-items: center; + } + + .debuggerConfig label { + margin: 0; + } + + .debuggerSection { + padding: 12px 24px 16px; + } + + .debuggerLabel { + margin-bottom: 2px; + font-size: 11px; + line-height: 12px; + text-transform: uppercase; + } ` class DebuggerUI { @@ -32,7 +52,9 @@ class DebuggerUI { this.event = new EventManager() this.isActive = false - + this.opt = { + debugWithGeneratedSources: false + } this.sourceHighlighter = new SourceHighlighter() this.startTxBrowser() @@ -72,12 +94,31 @@ class DebuggerUI { this.isActive = isActive }) - this.debugger.event.register('newSourceLocation', async (lineColumnPos, rawLocation) => { + this.debugger.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources) => { + if (!lineColumnPos) return const contracts = await this.fetchContractAndCompile( this.currentReceipt.contractAddress || this.currentReceipt.to, this.currentReceipt) if (contracts) { - const path = contracts.getSourceName(rawLocation.file) + let path = contracts.getSourceName(rawLocation.file) + if (!path) { + // check in generated sources + for (const source of generatedSources) { + if (source.id === rawLocation.file) { + path = `browser/.debugger/generated-sources/${source.name}` + let content + try { + content = await this.debuggerModule.call('fileManager', 'getFile', path, source.contents) + } catch (e) { + console.log('unable to fetch generated sources, the file probably doesn\'t exist yet', e) + } + if (content !== source.contents) { + await this.debuggerModule.call('fileManager', 'setFile', path, source.contents) + } + break + } + } + } if (path) { await this.debuggerModule.call('editor', 'discardHighlight') await this.debuggerModule.call('editor', 'highlight', lineColumnPos, path) @@ -137,7 +178,8 @@ class DebuggerUI { console.error(e) } return null - } + }, + debugWithGeneratedSources: this.opt.debugWithGeneratedSources }) this.listenToEvents() @@ -167,7 +209,8 @@ class DebuggerUI { console.error(e) } return null - } + }, + debugWithGeneratedSources: false }) debug.debugger.traceManager.traceRetriever.getTrace(hash, (error, trace) => { if (error) return reject(error) @@ -188,10 +231,16 @@ class DebuggerUI { var view = yo`
+
+

Debugger Configuration

+
+ { this.opt.debugWithGeneratedSources = event.target.checked }} type="checkbox" title="Debug with generated sources"> + +
+
${this.txBrowser.render()} ${this.stepManagerView} - ${this.debuggerHeadPanelsView} -
+ ${this.debuggerHeadPanelsView}
${this.statusMessage}
${this.debuggerPanelsView}
diff --git a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js index 4088021ba6..5df574bab0 100644 --- a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js +++ b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/VmDebugger.js @@ -83,9 +83,13 @@ function VmDebugger (vmDebuggerLogic) { this.vmDebuggerLogic.event.register('solidityStateUpdating', this.solidityState.setUpdating.bind(this.solidityState)) this.solidityLocals = new SolidityLocals() + this.solidityLocals.event.register('solidityLocalsLoadMore', (cursor) => { + this.vmDebuggerLogic.event.trigger('solidityLocalsLoadMore', [cursor]) + }) this.vmDebuggerLogic.event.register('solidityLocals', this.solidityLocals.update.bind(this.solidityLocals)) this.vmDebuggerLogic.event.register('solidityLocalsMessage', this.solidityLocals.setMessage.bind(this.solidityLocals)) this.vmDebuggerLogic.event.register('solidityLocalsUpdating', this.solidityLocals.setUpdating.bind(this.solidityLocals)) + this.vmDebuggerLogic.event.register('solidityLocalsLoadMoreCompleted', this.solidityLocals.loadMore.bind(this.solidityLocals)) this.returnValuesPanel = new DropdownPanel('Return Value', {json: true}) this.returnValuesPanel.data = {} diff --git a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js index 592b2ef060..513dc962ea 100644 --- a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js +++ b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/SolidityLocals.js @@ -6,18 +6,28 @@ var yo = require('yo-yo') class SolidityLocals { - constructor (_parent, _traceManager, _internalTreeCall) { + constructor () { this.event = new EventManager() this.basicPanel = new DropdownPanel('Solidity Locals', { json: true, formatSelf: solidityTypeFormatter.formatSelf, - extractData: solidityTypeFormatter.extractData + extractData: solidityTypeFormatter.extractData, + loadMore: (cursor) => { + this.event.trigger('solidityLocalsLoadMore', [cursor]) + } }) this.view + this._data = null } update (data) { - this.basicPanel.update(data) + this._data = data + this.basicPanel.update(this._data) + } + + loadMore (data) { + this._data = this.mergeLocals(data, this._data) + this.basicPanel.update(this._data) } setMessage (message) { @@ -28,6 +38,18 @@ class SolidityLocals { this.basicPanel.setUpdating() } + mergeLocals (locals1, locals2) { + Object.keys(locals2).map(item => { + if (locals2[item].cursor && (parseInt(locals2[item].cursor) < parseInt(locals1[item].cursor))) { + locals2[item] = { + ...locals1[item], + value: [...locals2[item].value, ...locals1[item].value] + } + } + }) + return locals2 + } + render () { this.view = yo`
${this.basicPanel.render()}
` return this.view diff --git a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js index 025f78bb18..2ddeef72ca 100644 --- a/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js +++ b/apps/remix-ide/src/app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter.js @@ -34,6 +34,8 @@ function extractData (item, parent, key) { }) ret.isArray = true ret.self = parent.isArray ? '' : item.type + ret.cursor = item.cursor + ret.hasNext = item.hasNext } else if (item.type.indexOf('struct') === 0) { ret.children = Object.keys((item.value || {})).map(function (key) { return {key: key, value: item.value[key]} diff --git a/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js b/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js index 76ee2343cf..b7d3ad14fa 100644 --- a/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js +++ b/apps/remix-ide/src/app/tabs/runTab/contractDropdown.js @@ -31,7 +31,7 @@ class ContractDropdownUI { if (success) { this.selectContractNames.removeAttribute('disabled') this.dropdownLogic.getCompiledContracts(compiler, compilerFullName).forEach((contract) => { - contractNames.appendChild(yo``) + contractNames.appendChild(yo``) }) } else { this.selectContractNames.setAttribute('disabled', true) diff --git a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js index ea2333bc9c..26ae3f6085 100644 --- a/apps/remix-ide/src/app/tabs/runTab/model/recorder.js +++ b/apps/remix-ide/src/app/tabs/runTab/model/recorder.js @@ -4,7 +4,6 @@ var remixLib = require('@remix-project/remix-lib') var EventManager = remixLib.EventManager var format = remixLib.execution.txFormat var txHelper = remixLib.execution.txHelper -var helper = require('../../../../lib/helper.js') /** * Record transaction as long as the user create them. @@ -12,14 +11,12 @@ var helper = require('../../../../lib/helper.js') * */ class Recorder { - constructor (blockchain, fileManager, config) { + constructor (blockchain) { var self = this self.event = new EventManager() self.blockchain = blockchain self.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} } - this.fileManager = fileManager - this.config = config - + this.blockchain.event.register('initiatingTransaction', (timestamp, tx, payLoad) => { if (tx.useCall) return var { from, to, value } = tx @@ -29,7 +26,7 @@ class Recorder { var record = { value, parameters: payLoad.funArgs } if (!to) { var abi = payLoad.contractABI - var keccak = ethutil.bufferToHex(ethutil.keccak(abi)) + var keccak = ethutil.bufferToHex(ethutil.keccak(JSON.stringify(abi))) record.abi = keccak record.contractName = payLoad.contractName record.bytecode = payLoad.contractBytecode @@ -279,51 +276,37 @@ class Recorder { return address } - runScenario (continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) { - var currentFile = this.config.get('currentFile') - this.fileManager.fileProviderOf(currentFile).get(currentFile, (error, json) => { - if (error) { - return cb('Invalid Scenario File ' + error) - } - if (!currentFile.match('.json$')) { - return cb('A scenario file is required. Please make sure a scenario file is currently displayed in the editor. The file must be of type JSON. Use the "Save Transactions" Button to generate a new Scenario File.') - } + runScenario (json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) { + if (!json) { + return cb('a json content must be provided') + } + if (typeof json === 'string') { try { - var obj = JSON.parse(json) - var txArray = obj.transactions || [] - var accounts = obj.accounts || [] - var options = obj.options || {} - var abis = obj.abis || {} - var linkReferences = obj.linkReferences || {} + json = JSON.parse(json) } catch (e) { - return cb('Invalid Scenario File, please try again') - } + return cb('A scenario file is required. It must be json formatted') + } + } - if (!txArray.length) { - return - } + try { + var txArray = json.transactions || [] + var accounts = json.accounts || [] + var options = json.options || {} + var abis = json.abis || {} + var linkReferences = json.linkReferences || {} + } catch (e) { + return cb('Invalid Scenario File. Please try again') + } - this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, (abi, address, contractName) => { - cb(null, abi, address, contractName) - }) - }) - } + if (!txArray.length) { + return + } - saveScenario (promptCb, cb) { - var txJSON = JSON.stringify(this.getAll(), null, 2) - var path = this.fileManager.currentPath() - promptCb(path, input => { - var fileProvider = this.fileManager.fileProviderOf(path) - if (!fileProvider) return - var newFile = path + '/' + input - helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { - if (error) return cb('Failed to create file. ' + newFile + ' ' + error) - if (!fileProvider.set(newFile, txJSON)) return cb('Failed to create file ' + newFile) - this.fileManager.open(newFile) - }) - }) + this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, (abi, address, contractName) => { + cb(null, abi, address, contractName) + }) + } - } module.exports = Recorder diff --git a/apps/remix-ide/src/app/tabs/runTab/recorder.js b/apps/remix-ide/src/app/tabs/runTab/recorder.js index 7fa784da6b..48aa809eed 100644 --- a/apps/remix-ide/src/app/tabs/runTab/recorder.js +++ b/apps/remix-ide/src/app/tabs/runTab/recorder.js @@ -1,16 +1,29 @@ var yo = require('yo-yo') var remixLib = require('@remix-project/remix-lib') var EventManager = remixLib.EventManager +import { Plugin } from '@remixproject/engine' var csjs = require('csjs-inject') var css = require('../styles/run-tab-styles') +import * as packageJson from '../../../../package.json' + var modalDialogCustom = require('../../ui/modal-dialog-custom') var modalDialog = require('../../ui/modaldialog') var confirmDialog = require('../../ui/confirmDialog') -class RecorderUI { +var helper = require('../../../lib/helper.js') + +const profile = { + name: 'recorder', + methods: ['runScenario'], + version: packageJson.version +} + +class RecorderUI extends Plugin { - constructor (blockchain, recorder, logCallBack, config) { + constructor (blockchain, fileManager, recorder, logCallBack, config) { + super(profile) + this.fileManager = fileManager this.blockchain = blockchain this.recorder = recorder this.logCallBack = logCallBack @@ -31,10 +44,16 @@ class RecorderUI { onclick=${this.triggerRecordButton.bind(this)} title="Save Transactions" aria-hidden="true"> ` - this.runButton.onclick = this.runScenario.bind(this) + this.runButton.onclick = () => { + const file = this.config.get('currentFile') + if (!file) return modalDialogCustom.alert('A scenario file has to be selected') + this.runScenario(file) + } } - runScenario () { + + runScenario (file) { + if (!file) return modalDialogCustom.alert('Unable to run scenerio, no specified scenario file') var continueCb = (error, continueTxExecution, cancelCb) => { if (error) { var msg = typeof error !== 'string' ? error.message : error @@ -67,14 +86,16 @@ class RecorderUI { const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog) - // TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily - this.recorder.runScenario(continueCb, promptCb, alertCb, confirmationCb, this.logCallBack, (error, abi, address, contractName) => { - if (error) { - return modalDialogCustom.alert(error) - } + this.fileManager.readFile(file).then((json) => { + // TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily + this.recorder.runScenario(json, continueCb, promptCb, alertCb, confirmationCb, this.logCallBack, (error, abi, address, contractName) => { + if (error) { + return modalDialogCustom.alert(error) + } - this.event.trigger('newScenario', [abi, address, contractName]) - }) + this.event.trigger('newScenario', [abi, address, contractName]) + }) + }).catch((error) => modalDialogCustom.alert(error)) } getConfirmationCb (modalDialog, confirmDialog) { @@ -110,7 +131,7 @@ class RecorderUI { } triggerRecordButton () { - this.recorder.saveScenario( + this.saveScenario( (path, cb) => { modalDialogCustom.prompt('Save transactions as scenario', 'Transactions will be saved in a file under ' + path, 'scenario.json', cb) }, @@ -120,6 +141,21 @@ class RecorderUI { ) } + saveScenario (promptCb, cb) { + var txJSON = JSON.stringify(this.recorder.getAll(), null, 2) + var path = this.fileManager.currentPath() + promptCb(path, input => { + var fileProvider = this.fileManager.fileProviderOf(path) + if (!fileProvider) return + var newFile = path + '/' + input + helper.createNonClashingName(newFile, fileProvider, (error, newFile) => { + if (error) return cb('Failed to create file. ' + newFile + ' ' + error) + if (!fileProvider.set(newFile, txJSON)) return cb('Failed to create file ' + newFile) + this.fileManager.open(newFile) + }) + }) + } + } module.exports = RecorderUI diff --git a/apps/remix-ide/src/app/tabs/testTab/testTab.js b/apps/remix-ide/src/app/tabs/testTab/testTab.js index 964b431337..f0a7399811 100644 --- a/apps/remix-ide/src/app/tabs/testTab/testTab.js +++ b/apps/remix-ide/src/app/tabs/testTab/testTab.js @@ -63,7 +63,7 @@ class TestTabLogic { let relative = remixPath.relative(this.currentPath, remixPath.dirname(fileToImport)) if (relative === '') relative = '.' const comment = hasCurrent ? `import "${relative}/${remixPath.basename(fileToImport)}";` : '// Import here the file to test.' - return `pragma solidity >=0.4.22 <0.7.0; + return `pragma solidity >=0.4.22 <0.8.0; import "remix_tests.sol"; // this import is automatically injected by Remix. ${comment} diff --git a/apps/remix-ide/src/app/udapp/make-udapp.js b/apps/remix-ide/src/app/udapp/make-udapp.js index b7f8dff5b2..f410d33577 100644 --- a/apps/remix-ide/src/app/udapp/make-udapp.js +++ b/apps/remix-ide/src/app/udapp/make-udapp.js @@ -2,7 +2,6 @@ var registry = require('../../global/registry') var remixLib = require('@remix-project/remix-lib') var yo = require('yo-yo') var EventsDecoder = remixLib.execution.EventsDecoder -var TransactionReceiptResolver = require('../../lib/transactionReceiptResolver') const transactionDetailsLinks = { 'Main': 'https://www.etherscan.io/tx/', @@ -27,7 +26,19 @@ export function makeUdapp (blockchain, compilersArtefacts, logHtmlCallback) { }) // ----------------- Tx listener ----------------- - const transactionReceiptResolver = new TransactionReceiptResolver(blockchain) + let _transactionReceipts = {} + const transactionReceiptResolver = (tx, cb) => { + if (_transactionReceipts[tx.hash]) { + return cb(null, _transactionReceipts[tx.hash]) + } + blockchain.web3().eth.getTransactionReceipt(tx.hash, (error, receipt) => { + if (error) { + return cb(error) + } + _transactionReceipts[tx.hash] = receipt + cb(null, receipt) + }) + } const txlistener = blockchain.getTxListener({ api: { @@ -35,9 +46,7 @@ export function makeUdapp (blockchain, compilersArtefacts, logHtmlCallback) { if (compilersArtefacts['__last']) return compilersArtefacts.getAllContractDatas() return null }, - resolveReceipt: function (tx, cb) { - transactionReceiptResolver.resolve(tx, cb) - } + resolveReceipt: transactionReceiptResolver } }) @@ -45,11 +54,7 @@ export function makeUdapp (blockchain, compilersArtefacts, logHtmlCallback) { blockchain.startListening(txlistener) const eventsDecoder = new EventsDecoder({ - api: { - resolveReceipt: function (tx, cb) { - transactionReceiptResolver.resolve(tx, cb) - } - } + resolveReceipt: transactionReceiptResolver }) txlistener.startListening() registry.put({api: eventsDecoder, name: 'eventsDecoder'}) diff --git a/apps/remix-ide/src/app/udapp/run-tab.js b/apps/remix-ide/src/app/udapp/run-tab.js index a12adbb1d8..21ec6562fc 100644 --- a/apps/remix-ide/src/app/udapp/run-tab.js +++ b/apps/remix-ide/src/app/udapp/run-tab.js @@ -118,13 +118,13 @@ export class RunTab extends LibraryPlugin { renderRecorder (udappUI, fileManager, config, logCallback) { this.recorderCount = yo`0` - const recorder = new Recorder(this.blockchain, fileManager, config) + const recorder = new Recorder(this.blockchain) recorder.event.register('recorderCountChange', (count) => { this.recorderCount.innerText = count }) this.event.register('clearInstance', recorder.clearAll.bind(recorder)) - this.recorderInterface = new RecorderUI(this.blockchain, recorder, logCallback, config) + this.recorderInterface = new RecorderUI(this.blockchain, fileManager, recorder, logCallback, config) this.recorderInterface.event.register('newScenario', (abi, address, contractName) => { var noInstancesText = this.noInstancesText diff --git a/apps/remix-ide/src/app/ui/TreeView.js b/apps/remix-ide/src/app/ui/TreeView.js index 5fc62d9552..59866a43c5 100644 --- a/apps/remix-ide/src/app/ui/TreeView.js +++ b/apps/remix-ide/src/app/ui/TreeView.js @@ -34,6 +34,9 @@ var css = csjs` .label_value { min-width: 10%; } + .cursor_pointer { + cursor: pointer; + } ` var EventManager = require('../../lib/events') @@ -49,6 +52,7 @@ class TreeView { this.event = new EventManager() this.extractData = opts.extractData || this.extractDataDefault this.formatSelf = opts.formatSelf || this.formatSelfDefault + this.loadMore = opts.loadMore this.view = null this.expandPath = [] } @@ -111,6 +115,9 @@ class TreeView { self.event.trigger('nodeRightClick', [keyPath, data, label, event]) } li.appendChild(list) + if (data.hasNext) { + list.appendChild(yo`
  • Load more
  • `) + } } else { caret.style.visibility = 'hidden' label.oncontextmenu = function (event) { diff --git a/apps/remix-ide/src/app/ui/landing-page/landing-page.js b/apps/remix-ide/src/app/ui/landing-page/landing-page.js index f6c6bff749..67a22a0515 100644 --- a/apps/remix-ide/src/app/ui/landing-page/landing-page.js +++ b/apps/remix-ide/src/app/ui/landing-page/landing-page.js @@ -164,7 +164,6 @@ export class LandingPage extends ViewPlugin { ` this.adjustMediaPanel() globalRegistry.get('themeModule').api.events.on('themeChanged', (theme) => { - console.log("theme is ", theme.quality) this.onThemeChanged(theme.quality) }) } @@ -177,6 +176,7 @@ export class LandingPage extends ViewPlugin { hideMediaPanel (e) { const mediaPanelsTitle = document.getElementById('remixIDEMediaPanelsTitle') const mediaPanels = document.getElementById('remixIDEMediaPanels') + if (!mediaPanelsTitle || !mediaPanels) return if (!mediaPanelsTitle.contains(e.target) && !mediaPanels.contains(e.target)) { this.mediumPanel.classList.remove('d-block') this.mediumPanel.classList.add('d-none') @@ -186,7 +186,6 @@ export class LandingPage extends ViewPlugin { } onThemeChanged (themeQuality) { - console.log("themes in listener is", themeQuality) let twitterFrame = yo`