diff --git a/apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts b/apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts new file mode 100644 index 0000000000..81164cb97a --- /dev/null +++ b/apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts @@ -0,0 +1,15 @@ +import { NightwatchBrowser } from 'nightwatch' +import EventEmitter from 'events' + +class CurrentSelectedFileIs extends EventEmitter { + command (this: NightwatchBrowser, value: string): NightwatchBrowser { + this.api + .waitForElementContainsText('*[data-id="tabs-component"] *[data-id="tab-active"]', value) + .perform(() => { + this.emit('complete') + }) + return this + } +} + +module.exports = CurrentSelectedFileIs diff --git a/apps/remix-ide-e2e/src/tests/editor.test.ts b/apps/remix-ide-e2e/src/tests/editor.test.ts index 7b714a432b..59c3211e79 100644 --- a/apps/remix-ide-e2e/src/tests/editor.test.ts +++ b/apps/remix-ide-e2e/src/tests/editor.test.ts @@ -147,6 +147,7 @@ module.exports = { .waitForElementContainsText('.contextview .type', 'uint256') .waitForElementContainsText('.contextview .name', 'number') .click('.contextview [data-action="previous"]') // declaration + .pause(1000) .execute(() => { return (document.getElementById('editorView') as any).getCursorPosition() }, [], (result) => { @@ -154,6 +155,7 @@ module.exports = { browser.assert.equal(result.value, '180') }) .click('.contextview [data-action="next"]') // back to the initial state + .pause(1000) .execute(() => { return (document.getElementById('editorView') as any).getCursorPosition() }, [], (result) => { @@ -161,6 +163,7 @@ module.exports = { browser.assert.equal(result.value, '323') }) .click('.contextview [data-action="next"]') // next reference + .pause(1000) .execute(() => { return (document.getElementById('editorView') as any).getCursorPosition() }, [], (result) => { @@ -168,12 +171,74 @@ module.exports = { browser.assert.equal(result.value, '489') }) .click('.contextview [data-action="gotoref"]') // back to the declaration + .pause(1000) .execute(() => { return (document.getElementById('editorView') as any).getCursorPosition() }, [], (result) => { console.log('result', result) browser.assert.equal(result.value, '180') }) + }, + + 'Should display the context view, loop over "Owner" by switching file #group2': function (browser: NightwatchBrowser) { + browser + .clickLaunchIcon('solidity') + .click('[for="autoCompile"]') // disable auto compile + .openFile('contracts') + .openFile('contracts/3_Ballot.sol') + .waitForElementVisible('#editorView') + .setEditorValue(BallotWithARefToOwner) + .clickLaunchIcon('solidity') + .click('*[data-id="compilerContainerCompileBtn"]') // compile + .pause(2000) + .execute(() => { + (document.getElementById('editorView') as any).gotoLine(14, 6) + }, [], () => {}) + .waitForElementVisible('.contextview') + .waitForElementContainsText('.contextview .type', 'ContractDefinition') + .waitForElementContainsText('.contextview .name', 'Owner') + .click('.contextview [data-action="next"]') + .pause(1000) + .execute(() => { + return (document.getElementById('editorView') as any).getCursorPosition() + }, [], (result) => { + console.log('result', result) + browser.assert.equal(result.value, '1061') + }) + .click('.contextview [data-action="next"]') + .pause(1000) + .execute(() => { + return (document.getElementById('editorView') as any).getCursorPosition() + }, [], (result) => { + console.log('result', result) + browser.assert.equal(result.value, '122') + }) + .currentSelectedFileIs('2_Owner.sol') // make sure the current file has been properly changed + .click('.contextview [data-action="next"]') + .pause(1000) + .execute(() => { + return (document.getElementById('editorView') as any).getCursorPosition() + }, [], (result) => { + console.log('result', result) + browser.assert.equal(result.value, '211') + }) + .click('.contextview [data-action="next"]') + .currentSelectedFileIs('3_Ballot.sol') + .pause(1000) + .execute(() => { + return (document.getElementById('editorView') as any).getCursorPosition() + }, [], (result) => { + console.log('result', result) + browser.assert.equal(result.value, '1061') + }) + .click('.contextview [data-action="gotoref"]') // go to the declaration + .pause(1000) + .execute(() => { + return (document.getElementById('editorView') as any).getCursorPosition() + }, [], (result) => { + console.log('result', result) + browser.assert.equal(result.value, '122') + }) .end() } } @@ -281,3 +346,149 @@ contract Storage { return number; } }` + +const BallotWithARefToOwner = ` + + +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; + +import "./2_Owner.sol"; + +/** + * @title Ballot + * @dev Implements voting process along with vote delegation + */ +contract Ballot { + Owner c; + struct Voter { + uint weight; // weight is accumulated by delegation + bool voted; // if true, that person already voted + address delegate; // person delegated to + uint vote; // index of the voted 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; + + mapping(address => Voter) public voters; + + Proposal[] public proposals; + + /** + * @dev Create a new ballot to choose one of 'proposalNames'. + * @param proposalNames names of proposals + */ + constructor(bytes32[] memory proposalNames) { + c = new Owner(); + chairperson = msg.sender; + voters[chairperson].weight = 1; + + for (uint i = 0; i < proposalNames.length; i++) { + // 'Proposal({...})' creates a temporary + // Proposal object and 'proposals.push(...)' + // appends it to the end of 'proposals'. + proposals.push(Proposal({ + name: proposalNames[i], + voteCount: 0 + })); + } + } + + /** + * @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; + } + + /** + * @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. + require(to != msg.sender, "Found loop in delegation."); + } + sender.voted = true; + sender.delegate = to; + 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; + } else { + // If the delegate did not vote yet, + // add to her weight. + delegate_.weight += sender.weight; + } + } + + /** + * @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, + // this will throw automatically and revert all + // changes. + proposals[proposal].voteCount += sender.weight; + } + + /** + * @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; + } + } + } + + /** + * @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; + } +} +` diff --git a/apps/remix-ide-e2e/src/tests/plugin_api.ts b/apps/remix-ide-e2e/src/tests/plugin_api.ts index d3df343415..fc09a9c8f1 100644 --- a/apps/remix-ide-e2e/src/tests/plugin_api.ts +++ b/apps/remix-ide-e2e/src/tests/plugin_api.ts @@ -149,6 +149,18 @@ module.exports = { await clickAndCheckLog(browser, 'udapp:getAccounts', '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', null, null) }, + 'Should select another provider #group1': async function (browser: NightwatchBrowser) { + await clickAndCheckLog(browser, 'udapp:setEnvironmentMode', null, null, { context: 'vm', fork: 'berlin' }) + await browser + .frameParent() + .useCss() + .clickLaunchIcon('udapp') + .waitForElementContainsText('#selectExEnvOptions option:checked', 'JavaScript VM (Berlin)') + .clickLaunchIcon('localPlugin') + .useXpath() + // @ts-ignore + .frame(0) + }, // context menu item 'Should create context menu item #group1': async function (browser: NightwatchBrowser) { diff --git a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts index 1be9168abb..feb70d7d62 100644 --- a/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts +++ b/apps/remix-ide-e2e/src/tests/transactionExecution.test.ts @@ -195,6 +195,26 @@ module.exports = { .journalLastChildIncludes('"documentation": "param2 from library"') .journalLastChildIncludes('"documentation": "param3 from library"') .journalLastChildIncludes('Debug the transaction to get more information.') + }, + + 'Should compile and deploy 2 simple contracts, the contract creation component state should be correctly reset for the deployment of the second contract #group4': function (browser: NightwatchBrowser) { + browser + .addFile('Storage.sol', sources[6]['Storage.sol']) + .addFile('Owner.sol', sources[6]['Owner.sol']) + .clickLaunchIcon('udapp') + .createContract('42') + .openFile('Storage.sol') + .clickLaunchIcon('udapp') + .createContract('') // this creation will fail if the component hasn't been properly reset. + .clickInstance(1) + .clickFunction('store - transact (not payable)', { types: 'uint256 num', values: '24' }) + .testFunction('last', // we check if the contract is actually reachable. + { + status: 'true Transaction mined and execution succeed', + 'decoded input': { + 'uint256 num': '24' + } + }) .end() } } @@ -322,5 +342,92 @@ contract C { } }` } + }, + { + 'Owner.sol': { + content: ` + // SPDX-License-Identifier: GPL-3.0 + + pragma solidity >=0.7.0 <0.9.0; + + /** + * @title Owner + * @dev Set & change owner + */ + contract Owner { + + address private owner; + + // event for EVM logging + event OwnerSet(address indexed oldOwner, address indexed newOwner); + + // modifier to check if caller is owner + modifier isOwner() { + // If the first argument of 'require' evaluates to 'false', execution terminates and all + // changes to the state and to Ether balances are reverted. + // This used to consume all gas in old EVM versions, but not anymore. + // It is often a good idea to use 'require' to check if functions are called correctly. + // As a second argument, you can also provide an explanation about what went wrong. + require(msg.sender == owner, "Caller is not owner"); + _; + } + + /** + * @dev Set contract deployer as owner + */ + constructor(uint p) { + owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor + emit OwnerSet(address(0), owner); + } + + /** + * @dev Change owner + * @param newOwner address of new owner + */ + function changeOwner(address newOwner) public isOwner { + emit OwnerSet(owner, newOwner); + owner = newOwner; + } + + /** + * @dev Return owner address + * @return address of owner + */ + function getOwner() external view returns (address) { + return owner; + } + }` + }, + 'Storage.sol': { + content: ` + // SPDX-License-Identifier: GPL-3.0 + + pragma solidity >=0.7.0 <0.9.0; + + /** + * @title Storage + * @dev Store & retrieve value in a variable + */ + contract Storage { + + uint256 number; + + /** + * @dev Store value in variable + * @param num value to store + */ + function store(uint256 num) public { + number = num; + } + + /** + * @dev Return value + * @return value of 'number' + */ + function retrieve() public view returns (uint256){ + return number; + } + }` + } } ] diff --git a/apps/remix-ide-e2e/src/tests/url.spec.ts b/apps/remix-ide-e2e/src/tests/url.spec.ts index 9d7e8e323e..13227238f7 100644 --- a/apps/remix-ide-e2e/src/tests/url.spec.ts +++ b/apps/remix-ide-e2e/src/tests/url.spec.ts @@ -77,12 +77,13 @@ module.exports = { 'Should load using URL compiler params': function (browser: NightwatchBrowser) { browser .pause(5000) - .url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js') + .url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&language=Yul') .refresh() .pause(5000) .clickLaunchIcon('solidity') .assert.containsText('#versionSelector option[data-id="selected"]', '0.7.4+commit.3f05b770') .assert.containsText('#evmVersionSelector option[data-id="selected"]', 'istanbul') + .assert.containsText('#compilierLanguageSelector option[data-id="selected"]', 'Yul') .verify.elementPresent('#optimize:checked') .verify.elementPresent('#autoCompile:checked') .verify.attributeEquals('#runs', 'value', '300') diff --git a/apps/remix-ide-e2e/src/types/index.d.ts b/apps/remix-ide-e2e/src/types/index.d.ts index f9a7c35d63..9abe5c2174 100644 --- a/apps/remix-ide-e2e/src/types/index.d.ts +++ b/apps/remix-ide-e2e/src/types/index.d.ts @@ -61,6 +61,7 @@ declare module 'nightwatch' { acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser clearConsole (this: NightwatchBrowser): NightwatchBrowser clearTransactions (this: NightwatchBrowser): NightwatchBrowser + currentSelectedFileIs (name: string): NightwatchBrowser } export interface NightwatchBrowser { diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 013b1b9853..1bc0a00b82 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -438,7 +438,7 @@ class Editor extends Plugin { if (!filePath) return filePath = await this.call('fileManager', 'getPathFromUrl', filePath) filePath = filePath.file - if (!this.sessions[filePath]) throw new Error('file not found' + filePath) + if (!this.sessions[filePath]) return const path = filePath || this.currentFile const { from } = this.currentRequest diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 1c4a3e61bf..a3828d68b3 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -470,7 +470,7 @@ class FileManager extends Plugin { } currentFile () { - return this._deps.config.get('currentFile') + return this.editor.current() } async closeAllFiles () { diff --git a/apps/remix-ide/src/app/panels/layout.ts b/apps/remix-ide/src/app/panels/layout.ts index 451c2aefe7..663692db26 100644 --- a/apps/remix-ide/src/app/panels/layout.ts +++ b/apps/remix-ide/src/app/panels/layout.ts @@ -80,7 +80,8 @@ export class Layout extends Plugin { const params = queryParams.get() if (params.minimizeterminal || params.embed) { this.panels.terminal.minimized = true - this.event.emit('change', null) + this.event.emit('change', this.panels) + this.emit('change', this.panels) } if (params.minimizesidepanel || params.embed) { this.event.emit('minimizesidepanel') diff --git a/apps/remix-ide/src/app/plugins/remixd-handle.tsx b/apps/remix-ide/src/app/plugins/remixd-handle.tsx index 68367d1df3..9ff3d8339f 100644 --- a/apps/remix-ide/src/app/plugins/remixd-handle.tsx +++ b/apps/remix-ide/src/app/plugins/remixd-handle.tsx @@ -142,11 +142,11 @@ function remixdDialog () {
If you are just looking for the remixd command, here it is: -



${commandText} +



{commandText}
- When connected, a session will be started between ${window.location.origin} and your local file system at ws://127.0.0.1:65520. + When connected, a session will be started between {window.location.origin} and your local file system at ws://127.0.0.1:65520. The shared folder will be in the "File Explorers" workspace named "localhost".
Read more about other Remixd ports usage
@@ -155,7 +155,7 @@ function remixdDialog () {
- Before using, make sure remixd version is latest i.e. ${remixdVersion} + Before using, make sure remixd version is latest i.e. v{remixdVersion}

Read here how to update it
diff --git a/apps/remix-ide/src/assets/img/cairoLogo.webp b/apps/remix-ide/src/assets/img/cairoLogo.webp deleted file mode 100644 index b7210dfa36..0000000000 Binary files a/apps/remix-ide/src/assets/img/cairoLogo.webp and /dev/null differ diff --git a/apps/remix-ide/src/assets/img/starkNetLogo.webp b/apps/remix-ide/src/assets/img/starkNetLogo.webp new file mode 100644 index 0000000000..6ada716998 Binary files /dev/null and b/apps/remix-ide/src/assets/img/starkNetLogo.webp differ diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js index 88ea275779..a747ba0f31 100644 --- a/apps/remix-ide/src/blockchain/blockchain.js +++ b/apps/remix-ide/src/blockchain/blockchain.js @@ -353,7 +353,7 @@ export class Blockchain extends Plugin { if (network.name === 'VM') return this.call('terminal', 'logHtml', ( - open in etherscan + view on etherscan )) }) }) diff --git a/libs/remix-core-plugin/src/lib/editor-context-listener.ts b/libs/remix-core-plugin/src/lib/editor-context-listener.ts index 7bde42de11..9e73f6bc01 100644 --- a/libs/remix-core-plugin/src/lib/editor-context-listener.ts +++ b/libs/remix-core-plugin/src/lib/editor-context-listener.ts @@ -84,11 +84,6 @@ export class EditorContextListener extends Plugin { async _highlightItems (cursorPosition, compilationResult, file) { if (this.currentPosition === cursorPosition) return - if (this.currentFile !== file) { - this.currentFile = file - this.currentPosition = cursorPosition - return - } this._stopHighlighting() this.currentPosition = cursorPosition this.currentFile = file @@ -122,9 +117,13 @@ export class EditorContextListener extends Plugin { async _highlight (node, compilationResult) { if (!node) return const position = sourceMappingDecoder.decode(node.src) + const fileTarget = compilationResult.getSourceName(position.file) + const nodeFound = this._activeHighlights.find((el) => el.fileTarget === fileTarget && el.position.file === position.file && el.position.length === position.length && el.position.start === position.start) + if (nodeFound) return // if the content is already highlighted, do nothing. + await this._highlightInternal(position, node, compilationResult) if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) { - this._activeHighlights.push({ position, fileTarget: compilationResult.getSourceName(position.file), nodeId: node.id }) + this._activeHighlights.push({ position, fileTarget, nodeId: node.id }) } } @@ -204,13 +203,16 @@ export class EditorContextListener extends Plugin { } _loadContractInfos (node) { + const path = (this.nodes.length && this.nodes[0].absolutePath) || this.results.source.target for (const i in this.nodes) { if (this.nodes[i].id === node.scope) { const contract = this.nodes[i] - this.contract = this.results.data.contracts[this.results.source.target][contract.name] - this.estimationObj = this.contract.evm.gasEstimates - this.creationCost = this.estimationObj === null ? '-' : this.estimationObj.creation.totalCost - this.codeDepositCost = this.estimationObj === null ? '-' : this.estimationObj.creation.codeDepositCost + this.contract = this.results.data.contracts[path][contract.name] + if (contract) { + this.estimationObj = this.contract.evm.gasEstimates + this.creationCost = this.estimationObj === null ? '-' : this.estimationObj.creation.totalCost + this.codeDepositCost = this.estimationObj === null ? '-' : this.estimationObj.creation.codeDepositCost + } } } } diff --git a/libs/remix-lib/src/execution/txRunnerWeb3.ts b/libs/remix-lib/src/execution/txRunnerWeb3.ts index 9b41fe9e5f..c62b655fb8 100644 --- a/libs/remix-lib/src/execution/txRunnerWeb3.ts +++ b/libs/remix-lib/src/execution/txRunnerWeb3.ts @@ -153,7 +153,7 @@ async function tryTillReceiptAvailable (txhash, web3) { async function tryTillTxAvailable (txhash, web3) { try { const tx = await web3.eth.getTransaction(txhash) - if (tx) return tx + if (tx && tx.blockHash) return tx } catch (e) {} return await tryTillTxAvailable(txhash, web3) } diff --git a/libs/remix-solidity/jest.config.js b/libs/remix-solidity/jest.config.js new file mode 100644 index 0000000000..abfff6a986 --- /dev/null +++ b/libs/remix-solidity/jest.config.js @@ -0,0 +1,24 @@ +module.exports = { + name: 'remix-solidity', + preset: '../../jest.config.js', + verbose: true, + silent: false, // Silent console messages, specially the 'remix-simulator' ones + transform: { + '^.+\\.[tj]sx?$': 'ts-jest', + }, + transformIgnorePatterns: ["/node_modules/", "/dist/", "\\.pnp\\.[^\\\/]+$"], + rootDir: "./", + testTimeout: 40000, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html', 'json'], + // Coverage + collectCoverage: true, + coverageReporters: ['text', 'text-summary'], + collectCoverageFrom: [ + "**/*.ts", + "!**/sol/**", + "!src/types.ts", + "!src/logger.ts" + ], + coverageDirectory: '../../coverage/libs/remix-solidity' + }; + \ No newline at end of file diff --git a/libs/remix-solidity/package.json b/libs/remix-solidity/package.json index 7f90f6ee3c..fdfaca6c02 100644 --- a/libs/remix-solidity/package.json +++ b/libs/remix-solidity/package.json @@ -41,7 +41,6 @@ "@types/node": "^13.1.1", "babel-eslint": "^10.0.0", "babelify": "^10.0.0", - "tape": "^4.6.0", "typescript": "^3.7.4" }, "scripts": { diff --git a/libs/remix-solidity/src/compiler/compiler-input.ts b/libs/remix-solidity/src/compiler/compiler-input.ts index 2baabecc79..8b00c647ee 100644 --- a/libs/remix-solidity/src/compiler/compiler-input.ts +++ b/libs/remix-solidity/src/compiler/compiler-input.ts @@ -1,6 +1,6 @@ 'use strict' -import { CompilerInput, Source, CompilerInputOptions } from './types' +import { CompilerInput, Source, CompilerInputOptions, Language } from './types' export default (sources: Source, opts: CompilerInputOptions): string => { const o: CompilerInput = { @@ -32,3 +32,13 @@ export default (sources: Source, opts: CompilerInputOptions): string => { } return JSON.stringify(o) } + +export const Languages = ['Solidity', 'Yul'] + +export function getValidLanguage (val: string): Language { + if (val !== undefined && val !== null && val) { + const lang = val.slice(0, 1).toUpperCase() + val.slice(1).toLowerCase() + return Languages.indexOf(lang) > -1 ? lang as Language : null + } + return null +} diff --git a/libs/remix-solidity/src/index.ts b/libs/remix-solidity/src/index.ts index 7de163dc97..d5878a0f85 100644 --- a/libs/remix-solidity/src/index.ts +++ b/libs/remix-solidity/src/index.ts @@ -1,6 +1,6 @@ export { Compiler } from './compiler/compiler' export { compile } from './compiler/compiler-helpers' -export { default as CompilerInput } from './compiler/compiler-input' +export { default as CompilerInput, getValidLanguage } from './compiler/compiler-input' export { CompilerAbstract } from './compiler/compiler-abstract' export * from './compiler/types' export { promisedMiniXhr, pathToURL, baseURLBin, baseURLWasm, canUseWorker, urlFromVersion } from './compiler/compiler-utils' diff --git a/libs/remix-solidity/tests/compiler-input.spec.ts b/libs/remix-solidity/tests/compiler-input.spec.ts new file mode 100644 index 0000000000..3dc247624b --- /dev/null +++ b/libs/remix-solidity/tests/compiler-input.spec.ts @@ -0,0 +1,25 @@ +import { getValidLanguage } from '../src/compiler/compiler-input' +import { Language } from '../src/compiler/types' + +describe('compiler-input', () => { + test('getValidLanguage', () => { + const correctYul: Language = 'Yul' + const correctSolidity: Language = 'Solidity' + + const yulUpperCase = 'Yul' + const yulLowerCase = 'yul' + + const solidityUpperCase = 'Solidity' + const solidityLowerCase = 'solidity' + + expect(getValidLanguage(yulLowerCase)).toBe(correctYul) + expect(getValidLanguage(yulUpperCase)).toBe(correctYul) + expect(getValidLanguage(solidityUpperCase)).toBe(correctSolidity) + expect(getValidLanguage(solidityLowerCase)).toBe(correctSolidity) + expect(getValidLanguage(null)).toBe(null) + expect(getValidLanguage(undefined)).toBe(null) + expect(getValidLanguage('')).toBe(null) + expect(getValidLanguage('A')).toBe(null) + expect(getValidLanguage('Something')).toBe(null) + }) +}) diff --git a/libs/remix-solidity/tsconfig.json b/libs/remix-solidity/tsconfig.json index 5ed408bd51..ec3a5d0c25 100644 --- a/libs/remix-solidity/tsconfig.json +++ b/libs/remix-solidity/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "types": ["node"] + "types": ["jest", "node"] }, "include": ["**/*.ts"] } \ No newline at end of file diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts index 740a306931..b8fd320f00 100644 --- a/libs/remix-tests/src/compiler.ts +++ b/libs/remix-tests/src/compiler.ts @@ -47,7 +47,6 @@ function isRemixTestFile (path: string) { function processFile (filePath: string, sources: SrcIfc, isRoot = false) { const importRegEx = /import ['"](.+?)['"];/g - let group: RegExpExecArray| null = null const isFileAlreadyInSources: boolean = Object.keys(sources).includes(filePath) // Return if file is a remix test file or already processed @@ -62,14 +61,6 @@ function processFile (filePath: string, sources: SrcIfc, isRoot = false) { content = includeTestLibs.concat(content) } sources[filePath] = { content } - importRegEx.exec('') // Resetting state of RegEx - - // Process each 'import' in file content - while ((group = importRegEx.exec(content))) { - const importedFile: string = group[1] - const importedFilePath: string = path.join(path.dirname(filePath), importedFile) - processFile(importedFilePath, sources) - } } const userAgent = (typeof (navigator) !== 'undefined') && navigator.userAgent ? navigator.userAgent.toLowerCase() : '-' @@ -123,7 +114,13 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts } finally { async.waterfall([ function loadCompiler (next) { - compiler = new RemixCompiler() + compiler = new RemixCompiler((url, cb) => { + try { + cb(null, fs.readFileSync(url, 'utf-8')) + } catch (e) { + cb(e.message) + } + }) if (compilerConfig) { const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig if (evmVersion) compiler.set('evmVersion', evmVersion) diff --git a/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx index d2668d57d3..7e25e32592 100644 --- a/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx +++ b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx @@ -8,15 +8,26 @@ import './remix-ui-editor-context-view.css' export type astNode = { name: string, id: number, - children: Array, + children?: Array, typeDescriptions: any, nodeType: String, - src: any, - nodeId: any, - position: any + src: string // e.g "142:1361:0" +} + +export type nodePositionLight = { + file: number, + length: number, + start: number +} + +export type astNodeLight = { + fileTarget: String, + nodeId: number, + position: nodePositionLight } export type onContextListenerChangedListener = (nodes: Array) => void +export type ononCurrentFileChangedListener = (name: string) => void export type gasEstimationType = { executionCost: string, @@ -30,8 +41,9 @@ export interface RemixUiEditorContextViewProps { offsetToLineColumn: (position: any, file: any, sources: any, asts: any) => any, getCurrentFileName: () => String onContextListenerChanged: (listener: onContextListenerChangedListener) => void + onCurrentFileChanged: (listener: ononCurrentFileChangedListener) => void referencesOf: (nodes: astNode) => Array - getActiveHighlights: () => Array + getActiveHighlights: () => Array gasEstimation: (node: astNode) => gasEstimationType declarationOf: (node: astNode) => astNode } @@ -48,49 +60,56 @@ function isDefinition (node: any) { type nullableAstNode = astNode | null export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) { - /* - gotoLineDisableRef is used to temporarily disable the update of the view. - e.g when the user ask the component to "gotoLine" we don't want to rerender the component (but just to put the mouse on the desired line) - */ - const gotoLineDisableRef = useRef(false) + const loopOverReferences = useRef(0) + const currentNodeDeclaration = useRef(null) const [state, setState] = useState<{ nodes: Array, - references: Array, activeHighlights: Array - currentNode: nullableAstNode, gasEstimation: gasEstimationType }>({ nodes: [], - references: [], activeHighlights: [], - currentNode: null, gasEstimation: { executionCost: '', codeDepositCost: '' } }) useEffect(() => { + props.onCurrentFileChanged(() => { + currentNodeDeclaration.current = null + setState(prevState => { + return { ...prevState, nodes: [], activeHighlights: [] } + }) + }) + props.onContextListenerChanged(async (nodes: Array) => { - if (gotoLineDisableRef.current) { - gotoLineDisableRef.current = false - return - } - let currentNode + let nextNodeDeclaration + let nextNode if (!props.hide && nodes && nodes.length) { - currentNode = nodes[nodes.length - 1] - if (!isDefinition(currentNode)) { - currentNode = await props.declarationOf(currentNode) + nextNode = nodes[nodes.length - 1] + if (!isDefinition(nextNode)) { + nextNodeDeclaration = await props.declarationOf(nextNode) + } else { + nextNodeDeclaration = nextNode } } - let references + if (nextNodeDeclaration && currentNodeDeclaration.current && nextNodeDeclaration.id === currentNodeDeclaration.current.id) return + + currentNodeDeclaration.current = nextNodeDeclaration + let gasEstimation - if (currentNode) { - references = await props.referencesOf(currentNode) - if (currentNode.nodeType === 'FunctionDefinition') { - gasEstimation = await props.gasEstimation(currentNode) + if (currentNodeDeclaration.current) { + if (currentNodeDeclaration.current.nodeType === 'FunctionDefinition') { + gasEstimation = await props.gasEstimation(currentNodeDeclaration.current) } } - const activeHighlights = await props.getActiveHighlights() + const activeHighlights: Array = await props.getActiveHighlights() + if (nextNode && activeHighlights && activeHighlights.length) { + loopOverReferences.current = activeHighlights.findIndex((el: astNodeLight) => `${el.position.start}:${el.position.length}:${el.position.file}` === nextNode.src) + loopOverReferences.current = loopOverReferences.current === -1 ? 0 : loopOverReferences.current + } else { + loopOverReferences.current = 0 + } setState(prevState => { - return { ...prevState, nodes, references, activeHighlights, currentNode, gasEstimation } + return { ...prevState, nodes, activeHighlights, gasEstimation } }) }) }, []) @@ -123,8 +142,7 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) if (fileName !== await props.getCurrentFileName()) { await props.openFile(fileName) } - if (lineColumn.start && lineColumn.start.line && lineColumn.start.column) { - gotoLineDisableRef.current = true + if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) { props.gotoLine(lineColumn.start.line, lineColumn.end.column + 1) } } @@ -141,14 +159,14 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) } } - const _render = (node: nullableAstNode) => { + const _render = () => { + const node = currentNodeDeclaration.current if (!node) return (
) - const references = state.references + const references = state.activeHighlights const type = node.typeDescriptions && node.typeDescriptions.typeString ? node.typeDescriptions.typeString : node.nodeType const referencesCount = `${references ? references.length : '0'} reference(s)` - let ref = 0 - const nodes: Array = state.activeHighlights + const nodes: Array = state.activeHighlights const jumpTo = () => { if (node && node.src) { @@ -161,10 +179,10 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) // JUMP BETWEEN REFERENCES const jump = (e: any) => { - e.target.dataset.action === 'next' ? ref++ : ref-- - if (ref < 0) ref = nodes.length - 1 - if (ref >= nodes.length) ref = 0 - _jumpToInternal(nodes[ref].position) + e.target.dataset.action === 'next' ? loopOverReferences.current++ : loopOverReferences.current-- + if (loopOverReferences.current < 0) loopOverReferences.current = nodes.length - 1 + if (loopOverReferences.current >= nodes.length) loopOverReferences.current = 0 + _jumpToInternal(nodes[loopOverReferences.current].position) } return ( @@ -181,7 +199,7 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) return ( !props.hide &&
- {_render(state.currentNode)} + {_render()}
) } 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 67df7cd443..82a18ef075 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -398,11 +398,12 @@ export const EditorUI = (props: EditorUIProps) => { props.plugin.call('editor', 'gotoLine', line, column)} - openFile={(file) => props.plugin.call('editor', 'openFile', file)} + openFile={(file) => props.plugin.call('fileManager', 'switchFile', file)} getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') } } offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) } } getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') } } onContextListenerChanged={(listener) => { props.plugin.on('contextualListener', 'contextChanged', listener) }} + onCurrentFileChanged={(listener) => { props.plugin.on('fileManager', 'currentFileChanged', listener) }} referencesOf={(node: astNode) => { return props.plugin.call('contextualListener', 'referencesOf', node) }} getActiveHighlights={() => { return props.plugin.call('contextualListener', 'getActiveHighlights') }} gasEstimation={(node: astNode) => { return props.plugin.call('contextualListener', 'gasEstimation', node) }} diff --git a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx index 73bd4f0357..1e0b366b3f 100644 --- a/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx +++ b/libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx @@ -1,6 +1,7 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import './remix-ui-home-tab.css' +import JSZip from 'jszip' import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import PluginButton from './components/pluginButton' // eslint-disable-line @@ -152,10 +153,10 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => { plugin.verticalIcons.select('solidity') _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'solidity']) } - const startCairo = async () => { - await plugin.appManager.activatePlugin('cairo_compiler') - plugin.verticalIcons.select('cairo_compiler') - _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'cairo_compiler']) + const startStarkNet = async () => { + await plugin.appManager.activatePlugin('starkNet_compiler') + plugin.verticalIcons.select('starkNet_compiler') + _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'starkNet_compiler']) } const startSolhint = async () => { await plugin.appManager.activatePlugin(['solidity', 'solhint']) @@ -173,9 +174,45 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => { _paq.push(['trackEvent', 'pluginManager', 'userActivate', 'sourcify']) } const startPluginManager = async () => { - await plugin.appManager.activatePlugin('pluginManager') plugin.verticalIcons.select('pluginManager') } + const saveAs = (blob, name) => { + const node = document.createElement('a') + node.download = name + node.rel = 'noopener' + node.href = URL.createObjectURL(blob) + setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s + setTimeout(function () { + try { + node.dispatchEvent(new MouseEvent('click')) + } catch (e) { + var evt = document.createEvent('MouseEvents') + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, + 20, false, false, false, false, 0, null) + node.dispatchEvent(evt) + } + }, 0) // 40s + } + const downloadFiles = async () => { + try { + plugin.call('notification', 'toast', 'preparing files for download, please wait..') + const zip = new JSZip() + const browserProvider = fileManager.getProvider('browser') + await browserProvider.copyFolderToJson('/', ({ path, content }) => { + zip.file(path, content) + }) + zip.generateAsync({ type: 'blob' }).then(function (blob) { + var today = new Date() + var date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() + var time = today.getHours() + 'h' + today.getMinutes() + 'min' + saveAs(blob, `remix-backup-at-${time}-${date}.zip`) + }).catch((e) => { + plugin.call('notification', 'toast', e.message) + }) + } catch (e) { + plugin.call('notification', 'toast', e.message) + } + } const showFullMessage = (title: string, loadItem: string, examples: Array) => { setState(prevState => { @@ -229,18 +266,27 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
-
-
- +
+
+
+ +
+
+ playRemi() } alt=""> + +
-
- playRemi() } alt=""> - +
+ + + Scam Alert: Beware of Youtube videos promoting "liquidity front runner bots" asking to paste contract code into Remix IDE. + + Learn more
@@ -250,7 +296,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
startSolidity()} /> - startCairo()} /> + startStarkNet()} /> startSolhint()} /> startLearnEth()} /> startSourceVerify()} /> @@ -280,6 +326,10 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {

+

+ + +

diff --git a/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx b/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx index 232f23ff10..116630c64c 100644 --- a/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx +++ b/libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx @@ -20,6 +20,7 @@ const DragBar = (props: IRemixDragBarUi) => { props.refObject.current.setAttribute('style', `height: ${h}px;`) setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) setDragState(false) + props.setHideStatus(false) } const handleResize = () => { setDragBarPosY(window.innerHeight - props.refObject.current.offsetHeight) diff --git a/libs/remix-ui/panel/src/lib/main/main-panel.tsx b/libs/remix-ui/panel/src/lib/main/main-panel.tsx index 4fb00ddf65..4ea7dc20f2 100644 --- a/libs/remix-ui/panel/src/lib/main/main-panel.tsx +++ b/libs/remix-ui/panel/src/lib/main/main-panel.tsx @@ -37,14 +37,24 @@ const RemixUIMainPanel = () => { appContext.layout.event.on('change', () => { renderPanels() }) + + return () => { + appContext.layout.event.off('change') + } }, []) + const showTerminal = (hide: boolean) => { + appContext.layout.panels.terminal.minimized = hide + appContext.layout.event.emit('change', appContext.layout.panels) + appContext.layout.emit('change', appContext.layout.panels) + } + return (
{Object.values(plugins).map((pluginRecord, i) => { return ( - {(pluginRecord.profile.name === 'terminal') ? : null} + {(pluginRecord.profile.name === 'terminal') ? : null}
or
- + { diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index 3dc10d1e52..8cf8615b09 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -205,7 +205,7 @@ export function RunTabUI (props: RunTabProps) { {from} is changing your environment to - {env} + {env && env.context}
) diff --git a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx index eabccbd8a8..398250a3dd 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx @@ -7,6 +7,7 @@ import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promi import { compilerReducer, compilerInitialState } from './reducers/compiler' import { resetEditorMode, listenToEvents } from './actions/compiler' import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line +import { getValidLanguage } from '@remix-project/remix-solidity' import './css/style.css' @@ -74,6 +75,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => { const optimize = params.optimize const runs = params.runs as string const evmVersion = params.evmVersion + const language = getValidLanguage(params.language) return { ...prevState, @@ -82,7 +84,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => { includeNightlies: includeNightlies, optimize: optimize, runs: runs, - evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default' + evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default', + language: (language !== null) ? language : 'Solidity' } }) } @@ -537,8 +540,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts index c938cdb633..bb301dc402 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts @@ -1,4 +1,5 @@ import { ICompilerApi } from '@remix-project/remix-lib-ts' +import { getValidLanguage } from '@remix-project/remix-solidity' const Compiler = require('@remix-project/remix-solidity').Compiler const EventEmitter = require('events') @@ -15,6 +16,7 @@ export class CompileTabLogic { public optimize public runs public evmVersion: string + public language: string public compilerImport public event @@ -39,6 +41,11 @@ export class CompileTabLogic { } this.api.setCompilerParameters({ evmVersion: this.evmVersion }) this.compiler.set('evmVersion', this.evmVersion) + + this.language = getValidLanguage(this.api.getCompilerParameters().language) + if (this.language != null) { + this.compiler.set('language', this.language) + } } setOptimize (newOptimizeValue) { @@ -68,6 +75,8 @@ export class CompileTabLogic { * @params lang {'Solidity' | 'Yul'} ... */ setLanguage (lang) { + this.language = lang + this.api.setCompilerParameters({ language: lang }) this.compiler.set('language', lang) } diff --git a/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx index 9f9da608f9..15e19bd358 100644 --- a/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx +++ b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx @@ -723,7 +723,7 @@ export const SolidityUnitTesting = (props: Record) => { // eslint-d
-
{testsOutput}
+
{testsOutput}
) diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx index 3c791d1c1b..45632fc4de 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx @@ -36,7 +36,7 @@ export const TabsUI = (props: TabsUIProps) => { const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass const classNameTab = 'nav-item nav-link d-flex justify-content-center align-items-center px-2 py-1 tab' + (index === currentIndexRef.current ? ' active' : '') return ( -
{ tabsRef.current[index] = el }} className={classNameTab} title={tab.tooltip}> +
{ tabsRef.current[index] = el }} className={classNameTab} data-id={index === currentIndexRef.current ? 'tab-active' : ''} title={tab.tooltip}> {tab.icon ? () : ()} {tab.title} { props.onClose(index); event.stopPropagation() }}> @@ -74,7 +74,7 @@ export const TabsUI = (props: TabsUIProps) => { }, []) return ( -
+
props.onZoomOut()}> diff --git a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts index d0efd289b5..5ee522018b 100644 --- a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts +++ b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts @@ -111,9 +111,11 @@ export const listenOnNetworkAction = async (plugins, isListening) => { } export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch) => { + const provider = plugins.blockchain.getProvider() + plugins.txListener.event.register(NEW_BLOCK, (block) => { if (!block.transactions || (block.transactions && !block.transactions.length)) { - dispatch({ type: EMPTY_BLOCK, payload: { message: 0 } }) + dispatch({ type: EMPTY_BLOCK, payload: { message: 0, provider } }) } }) plugins.txListener.event.register(KNOWN_TRANSACTION, () => { @@ -128,6 +130,8 @@ export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch) = const log = async (plugins, tx, receipt, dispatch: React.Dispatch) => { const resolvedTransaction = await plugins.txListener.resolvedTransaction(tx.hash) + const provider = plugins.blockchain.getProvider() + if (resolvedTransaction) { let compiledContracts = null if (plugins._deps.compilersArtefacts.__last) { @@ -135,11 +139,11 @@ export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch) = } await plugins.eventsDecoder.parseLogs(tx, resolvedTransaction.contractName, compiledContracts, async (error, logs) => { if (!error) { - await dispatch({ type: KNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs }] } }) + await dispatch({ type: KNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs }], provider } }) } }) } else { - await dispatch({ type: UNKNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt }] } }) + await dispatch({ type: UNKNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt }], provider } }) } } diff --git a/libs/remix-ui/terminal/src/lib/components/Context.tsx b/libs/remix-ui/terminal/src/lib/components/Context.tsx index 9e5d09d93c..d5234f9526 100644 --- a/libs/remix-ui/terminal/src/lib/components/Context.tsx +++ b/libs/remix-ui/terminal/src/lib/components/Context.tsx @@ -4,7 +4,7 @@ import helper from 'apps/remix-ide/src/lib/helper' const remixLib = require('@remix-project/remix-lib') const typeConversion = remixLib.execution.typeConversion -const Context = ({ opts, blockchain }) => { +const Context = ({ opts, provider }: { opts, provider: string }) => { const data = opts.tx || '' const from = opts.from ? helper.shortenHexData(opts.from) : '' let to = opts.to @@ -16,7 +16,8 @@ const Context = ({ opts, blockchain }) => { const block = data.receipt ? data.receipt.blockNumber : data.blockNumber || '' const i = data.receipt ? data.transactionIndex : data.transactionIndex const value = val ? typeConversion.toInt(val) : 0 - if (blockchain.getProvider() === 'vm') { + + if (provider === 'vm') { return (
@@ -29,7 +30,7 @@ const Context = ({ opts, blockchain }) => {
hash: {hash}
) - } else if (blockchain.getProvider() !== 'vm' && data.resolvedData) { + } else if (provider !== 'vm' && data.resolvedData) { return (
diff --git a/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx b/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx index 3682219e06..64d58248bf 100644 --- a/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx +++ b/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx @@ -8,7 +8,7 @@ import showTable from './Table' const remixLib = require('@remix-project/remix-lib') const typeConversion = remixLib.execution.typeConversion -const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugin, showTableHash, txDetails, modal }) => { +const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugin, showTableHash, txDetails, modal, provider }) => { const debug = (event, tx) => { event.stopPropagation() if (tx.isCall && tx.envMode !== 'vm') { @@ -26,7 +26,7 @@ const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugi
txDetails(event, tx)}> - +
{ +const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash, txDetails, modal, provider }) => { const debug = (event, tx) => { event.stopPropagation() if (tx.isCall && tx.envMode !== 'vm') { @@ -21,7 +21,7 @@ const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash,
txDetails(event, tx)}> - +
{ case HTML: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider }) } case LOG: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info', provider: action.payload.provider }) } case INFO: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info', provider: action.payload.provider }) } case WARN: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-warning' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-warning', provider: action.payload.provider }) } case ERROR: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-danger' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-danger', provider: action.payload.provider }) } case SCRIPT: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider }) } case KNOWN_TRANSACTION: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction', provider: action.payload.provider }) } case UNKNOWN_TRANSACTION: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'unknownTransaction' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'unknownTransaction', provider: action.payload.provider }) } case EMPTY_BLOCK: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'emptyBlock' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'emptyBlock', provider: action.payload.provider }) } case NEW_TRANSACTION: return { ...state, - journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '' }) + journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', provider: action.payload.provider }) } } } diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx index 1de6922dce..95f3bf6d71 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx @@ -419,6 +419,16 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { props.plugin.call('layout', 'minimize', props.plugin.profile.name, isOpen) } + useEffect(() => { + props.plugin.on('layout', 'change', (panels) => { + setIsOpen(!panels.terminal.minimized) + }) + + return () => { + props.plugin.off('layout', 'change') + } + }, []) + const classNameBlock = 'remix_ui_terminal_block px-4 py-1 text-break' return ( @@ -490,6 +500,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { showTableHash={showTableHash} txDetails={txDetails} modal={modal} + provider={x.provider} />}
) @@ -517,6 +528,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { showTableHash = { showTableHash } txDetails = { txDetails } modal={modal} + provider={x.provider} />) }
) diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx b/libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx index efeceed420..94a56bed15 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx +++ b/libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx @@ -7,7 +7,7 @@ interface HomeProps { function Home ({ verticalIconPlugin }: HomeProps) { return (
await verticalIconPlugin.activateHome()} {...{ plugin: 'home'}} title="Home" diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx b/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx index cb5cc56931..a0aa750e73 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx +++ b/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx @@ -85,7 +85,7 @@ const Icon = ({ return ( <>
{ (verticalIconPlugin as any).toggle(name) }} diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css index c14e3bb47c..5fd6e085ee 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css +++ b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css @@ -29,6 +29,7 @@ width: 36px; height: 36px; border-radius: 8px; + align-items: center; } .remixui_icon img { width: 28px; @@ -39,15 +40,12 @@ .remixui_icon .selected-dark { filter: invert(1) grayscale(1); - } .remixui_icon .selected-light { filter: invert(0) grayscale(1); } - .remixui_image { - } .remixui_icon svg { width: 28px; height: 28px; @@ -106,9 +104,13 @@ scrollbar-width: none; /* Firefox hide scrollbar */ -ms-overflow-style: none; } + .remixui_requiredSection { + text-align: center; + } .remixui_scrollable-container { flex-basis: 510px; flex-grow: 2; + text-align: center; /* border-bottom: 3px solid #3f4455; */ } .remixui_scrollbar::-webkit-scrollbar { /* Chrome, Safari and other Webkit browsers*/ @@ -119,9 +121,12 @@ } .remixui_default-icons-container { border-bottom: 2px solid #3f4455; + text-align: center; } .remixui_icon-chevron { z-index: 1000; + cursor: pointer; + align-items: center; } .remixui_settings { @@ -132,7 +137,3 @@ list-style: none; margin: 0px; } - - .remixui_icon-chevron { - cursor: pointer; - } \ No newline at end of file diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx index 6d606d3149..3d4413eb4d 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx +++ b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx @@ -90,7 +90,7 @@ const RemixUiVerticalIconsPanel = ({ ) : null } @@ -109,19 +109,19 @@ const RemixUiVerticalIconsPanel = ({ itemContextAction={itemContextAction} />
-
- { scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? () : null } - p.profile.name === 'settings' || p.profile.name === 'pluginManager')} - verticalIconsPlugin={verticalIconsPlugin} - itemContextAction={itemContextAction} - /> -
+
+ { scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? () : null } + p.profile.name === 'settings' || p.profile.name === 'pluginManager')} + verticalIconsPlugin={verticalIconsPlugin} + itemContextAction={itemContextAction} + /> +
) diff --git a/libs/remix-ui/workspace/src/lib/actions/index.ts b/libs/remix-ui/workspace/src/lib/actions/index.ts index 390a1ff44d..e9bc5d1a62 100644 --- a/libs/remix-ui/workspace/src/lib/actions/index.ts +++ b/libs/remix-ui/workspace/src/lib/actions/index.ts @@ -179,7 +179,7 @@ export const createNewFolder = async (path: string, rootDir: string) => { const exists = await fileManager.exists(dirName) if (exists) { - return dispatch(displayNotification('Rename File Failed', `A file or folder ${extractNameFromKey(path)} already exists at this location. Please choose a different name.`, 'Close', null, () => {})) + return dispatch(displayNotification('Failed to create folder', `A folder ${extractNameFromKey(path)} already exists at this location. Please choose a different name.`, 'Close', null, () => {})) } await fileManager.mkdir(dirName) path = path.indexOf(rootDir + '/') === 0 ? path.replace(rootDir + '/', '') : path diff --git a/workspace.json b/workspace.json index 5ff3f1ca9f..8e78b40751 100644 --- a/workspace.json +++ b/workspace.json @@ -309,10 +309,10 @@ } }, "test": { - "builder": "@nrwl/workspace:run-commands", + "builder": "@nrwl/jest:jest", "options": { - "commands": ["./../../node_modules/.bin/npm-run-all test"], - "cwd": "libs/remix-solidity" + "jestConfig": "libs/remix-solidity/jest.config.js", + "tsConfig": "libs/remix-solidity/tsconfig.spec.json" } }, "build": {