Merge branch 'master' into loadingImprove

pull/5370/head
David Disu 3 years ago committed by GitHub
commit 6fcb8a3492
  1. 15
      apps/remix-ide-e2e/src/commands/currentSelectedFileIs.ts
  2. 211
      apps/remix-ide-e2e/src/tests/editor.test.ts
  3. 12
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  4. 107
      apps/remix-ide-e2e/src/tests/transactionExecution.test.ts
  5. 3
      apps/remix-ide-e2e/src/tests/url.spec.ts
  6. 1
      apps/remix-ide-e2e/src/types/index.d.ts
  7. 2
      apps/remix-ide/src/app/editor/editor.js
  8. 2
      apps/remix-ide/src/app/files/fileManager.ts
  9. 3
      apps/remix-ide/src/app/panels/layout.ts
  10. 6
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  11. BIN
      apps/remix-ide/src/assets/img/cairoLogo.webp
  12. BIN
      apps/remix-ide/src/assets/img/starkNetLogo.webp
  13. 2
      apps/remix-ide/src/blockchain/blockchain.js
  14. 22
      libs/remix-core-plugin/src/lib/editor-context-listener.ts
  15. 2
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  16. 24
      libs/remix-solidity/jest.config.js
  17. 1
      libs/remix-solidity/package.json
  18. 12
      libs/remix-solidity/src/compiler/compiler-input.ts
  19. 2
      libs/remix-solidity/src/index.ts
  20. 25
      libs/remix-solidity/tests/compiler-input.spec.ts
  21. 2
      libs/remix-solidity/tsconfig.json
  22. 17
      libs/remix-tests/src/compiler.ts
  23. 98
      libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx
  24. 3
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  25. 84
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  26. 1
      libs/remix-ui/panel/src/lib/dragbar/dragbar.tsx
  27. 12
      libs/remix-ui/panel/src/lib/main/main-panel.tsx
  28. 2
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  29. 1
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  30. 2
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  31. 9
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  32. 9
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  33. 2
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  34. 4
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  35. 10
      libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
  36. 7
      libs/remix-ui/terminal/src/lib/components/Context.tsx
  37. 4
      libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx
  38. 4
      libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx
  39. 20
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  40. 12
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  41. 2
      libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx
  42. 2
      libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx
  43. 15
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css
  44. 28
      libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.tsx
  45. 2
      libs/remix-ui/workspace/src/lib/actions/index.ts
  46. 6
      workspace.json

@ -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

@ -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;
}
}
`

@ -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) {

@ -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;
}
}`
}
}
]

@ -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')

@ -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 {

@ -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

@ -470,7 +470,7 @@ class FileManager extends Plugin {
}
currentFile () {
return this._deps.config.get('currentFile')
return this.editor.current()
}
async closeAllFiles () {

@ -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')

@ -142,11 +142,11 @@ function remixdDialog () {
</div>
<div className='mb-2 text-break'>
If you are just looking for the remixd command, here it is:
<br></br><br></br><b>${commandText}</b>
<br></br><br></br><b>{commandText}</b>
<CopyToClipboard data-id='remixdCopyCommand' content={commandText}></CopyToClipboard>
</div>
<div className='mb-2 text-break'>
When connected, a session will be started between <em>${window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>.
When connected, a session will be started between <em>{window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>.
The shared folder will be in the "File Explorers" workspace named "localhost".
<br/>Read more about other <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a>
</div>
@ -155,7 +155,7 @@ function remixdDialog () {
</div>
<div className='mb-2 text-break'>
<h6 className="text-danger">
Before using, make sure remixd version is latest i.e. <b>${remixdVersion}</b>
Before using, make sure remixd version is latest i.e. <b>v{remixdVersion}</b>
<br></br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a>
</h6>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -353,7 +353,7 @@ export class Blockchain extends Plugin {
if (network.name === 'VM') return
this.call('terminal', 'logHtml',
(<a href={etherScanLink(network.name, txhash)} target="_blank">
open in etherscan
view on etherscan
</a>))
})
})

@ -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
}
}
}
}

@ -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)
}

@ -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'
};

@ -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": {

@ -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
}

@ -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'

@ -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)
})
})

@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"types": ["node"]
"types": ["jest", "node"]
},
"include": ["**/*.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)

@ -8,15 +8,26 @@ import './remix-ui-editor-context-view.css'
export type astNode = {
name: string,
id: number,
children: Array<any>,
children?: Array<any>,
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<astNode>) => 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<astNode>
getActiveHighlights: () => Array<astNode>
getActiveHighlights: () => Array<astNodeLight>
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<nullableAstNode>(null)
const [state, setState] = useState<{
nodes: Array<astNode>,
references: Array<astNode>,
activeHighlights: Array<any>
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<astNode>) => {
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<astNodeLight> = 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 (<div></div>)
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<astNode> = state.activeHighlights
const nodes: Array<astNodeLight> = 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 && <div className="container-context-view contextviewcontainer bg-light text-dark border-0 py-1">
{_render(state.currentNode)}
{_render()}
</div>
)
}

@ -398,11 +398,12 @@ export const EditorUI = (props: EditorUIProps) => {
<RemixUiEditorContextView
hide={false}
gotoLine={(line, column) => 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) }}

@ -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<string>) => {
setState(prevState => {
@ -229,18 +266,27 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
</ModalDialog>
<Toaster message={state.toasterMsg} />
<div className="d-flex flex-column ml-4" id="remixUiRightPanel">
<div className="border-bottom d-flex justify-content-between mr-4 pb-3 mb-3">
<div className="mx-4 my-4 d-flex">
<label style={ { fontSize: 'xxx-large', height: 'auto', alignSelf: 'flex-end' } }>Remix IDE</label>
<div className="border-bottom d-flex flex-column mr-4 pb-3 mb-3">
<div className="d-flex justify-content-between ">
<div className="mx-4 my-4 d-flex">
<label style={ { fontSize: 'xxx-large', height: 'auto', alignSelf: 'flex-end' } }>Remix IDE</label>
</div>
<div className="mr-4 d-flex">
<img className="mt-4 mb-2 remixui_home_logoImg" src="assets/img/guitarRemiCroped.webp" onClick={ () => playRemi() } alt=""></img>
<audio
id="remiAudio"
muted={false}
src="assets/audio/remiGuitar-single-power-chord-A-minor.wav"
ref={remiAudioEl}
></audio>
</div>
</div>
<div className="mr-4 d-flex">
<img className="mt-4 mb-2 remixui_home_logoImg" src="assets/img/guitarRemiCroped.webp" onClick={ () => playRemi() } alt=""></img>
<audio
id="remiAudio"
muted={false}
src="assets/audio/remiGuitar-single-power-chord-A-minor.wav"
ref={remiAudioEl}
></audio>
<div>
<i className="pl-4 text-danger fas fa-exclamation-triangle"></i>
<span className="px-2 remixui_home_text text-danger mt-4 pt-4">
Scam Alert: Beware of Youtube videos promoting "liquidity front runner bots" asking to paste contract code into Remix IDE.
</span>
<a className="remixui_home_text" target="__blank" href="https://medium.com/remix-ide/remix-in-youtube-crypto-scams-71c338da32d">Learn more</a>
</div>
</div>
<div className="row mx-2 mr-4" data-id="landingPageHpSections">
@ -250,7 +296,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<div className="d-flex flex-row pt-2">
<ThemeContext.Provider value={ state.themeQuality }>
<PluginButton imgPath="assets/img/solidityLogo.webp" envID="solidityLogo" envText="Solidity" callback={() => startSolidity()} />
<PluginButton imgPath="assets/img/cairoLogo.webp" envID="CairoLogo" envText="Cairo compiler" l2={true} callback={() => startCairo()} />
<PluginButton imgPath="assets/img/starkNetLogo.webp" envID="starkNetLogo" envText="StarkNet" l2={true} callback={() => startStarkNet()} />
<PluginButton imgPath="assets/img/solhintLogo.webp" envID="solhintLogo" envText="Solhint linter" callback={() => startSolhint()} />
<PluginButton imgPath="assets/img/learnEthLogo.webp" envID="learnEthLogo" envText="LearnEth" callback={() => startLearnEth()} />
<PluginButton imgPath="assets/img/sourcifyLogo.webp" envID="sourcifyLogo" envText="Sourcify" callback={() => startSourceVerify()} />
@ -280,6 +326,10 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<i className="mr-1 far fa-hdd"></i>
<label className="ml-1 remixui_home_text" onClick={() => connectToLocalhost()}>Connect to Localhost</label>
</p>
<p className="mb-1">
<i className="mr-1 far fa-download"></i>
<label className="ml-1 remixui_home_text" onClick={() => downloadFiles()}>Download Backup</label>
</p>
<p className="mt-3 mb-0"><label>LOAD FROM:</label></p>
<div className="btn-group">
<button className="btn mr-1 btn-secondary" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button>

@ -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)

@ -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 (
<div className="mainview">
{Object.values(plugins).map((pluginRecord, i) => {
return (
<React.Fragment key={`mainView${i}`}>
{(pluginRecord.profile.name === 'terminal') ? <DragBar key='dragbar-terminal' hidden={pluginRecord.minimized || false} setHideStatus={() => {}} refObject={terminalRef}></DragBar> : null}
{(pluginRecord.profile.name === 'terminal') ? <DragBar key='dragbar-terminal' hidden={pluginRecord.minimized || false} setHideStatus={showTerminal} refObject={terminalRef}></DragBar> : null}
<RemixUIPanelPlugin
ref={refs[i]}
key={pluginRecord.profile.name}

@ -237,7 +237,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
</div>
<div className="udapp_orLabel mt-2" style={{ display: loadType === 'abi' ? 'none' : 'block' }}>or</div>
<div className="udapp_button udapp_atAddressSect">
<button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} onClick={loadFromAddress}>At Address</button>
<button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} title={atAddressOptions.title} onClick={loadFromAddress}>At Address</button>
<input
className="udapp_input udapp_ataddressinput ataddressinput form-control"
placeholder="Load contract from Address"

@ -25,6 +25,7 @@ export function ContractGUI (props: ContractGUIProps) {
} else {
setTitle(props.funcABI.type === 'receive' ? '(receive)' : '(fallback)')
}
setBasicInput('')
}, [props.title, props.funcABI])
useEffect(() => {

@ -205,7 +205,7 @@ export function RunTabUI (props: RunTabProps) {
{from}
<span className="font-weight-bold text-warning">
is changing your environment to
</span> {env}
</span> {env && env.context}
</span>
</div>
)

@ -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) => {
<div className="mb-2">
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label>
<select onChange={(e) => handleLanguageChange(e.target.value)} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7">
<option value='Solidity'>Solidity</option>
<option value='Yul'>Yul</option>
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select>
</div>
<div className="mb-2">

@ -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)
}

@ -723,7 +723,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
<label className="text-warning h6" data-id="testTabTestsExecutionStopped" hidden={testsExecutionStoppedHidden}>The test execution has been stopped</label>
<label className="text-danger h6" data-id="testTabTestsExecutionStoppedError" hidden={testsExecutionStoppedErrorHidden}>The test execution has been stopped because of error(s) in your test file</label>
</div>
<div id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput">{testsOutput}</div>
<div className="mx-3 mb-2 pb-4 border-primary" id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput">{testsOutput}</div>
</div>
</div>
)

@ -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 (
<div ref={el => { tabsRef.current[index] = el }} className={classNameTab} title={tab.tooltip}>
<div ref={el => { tabsRef.current[index] = el }} className={classNameTab} data-id={index === currentIndexRef.current ? 'tab-active' : ''} title={tab.tooltip}>
{tab.icon ? (<img className="my-1 mr-1 iconImage" src={tab.icon} />) : (<i className={classNameImg}></i>)}
<span className="title-tabs">{tab.title}</span>
<span className="close-tabs" onClick={(event) => { props.onClose(index); event.stopPropagation() }}>
@ -74,7 +74,7 @@ export const TabsUI = (props: TabsUIProps) => {
}, [])
return (
<div className="remix-ui-tabs d-flex justify-content-between border-0 header nav-tabs">
<div className="remix-ui-tabs d-flex justify-content-between border-0 header nav-tabs" data-id="tabs-component">
<div className="d-flex flex-row" style={ { maxWidth: 'fit-content', width: '97%' } }>
<div className="d-flex flex-row justify-content-center align-items-center m-1 mt-2">
<span data-id="tabProxyZoomOut" className="btn btn-sm px-2 fas fa-search-minus text-dark" title="Zoom out" onClick={() => props.onZoomOut()}></span>

@ -111,9 +111,11 @@ export const listenOnNetworkAction = async (plugins, isListening) => {
}
export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch<any>) => {
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<any>) =
const log = async (plugins, tx, receipt, dispatch: React.Dispatch<any>) => {
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<any>) =
}
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 } })
}
}

@ -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 (
<div>
<span>
@ -29,7 +30,7 @@ const Context = ({ opts, blockchain }) => {
<div className='remix_ui_terminal_txItem'><span className='remix_ui_terminal_txItemTitle'>hash:</span> {hash}</div>
</span>
</div>)
} else if (blockchain.getProvider() !== 'vm' && data.resolvedData) {
} else if (provider !== 'vm' && data.resolvedData) {
return (
<div>
<span>

@ -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
<span id={`tx${tx.hash}`} key={index}>
<div className="remix_ui_terminal_log" onClick={(event) => txDetails(event, tx)}>
<CheckTxStatus tx={receipt} type={txType} />
<Context opts = { options } blockchain={plugin.blockchain} />
<Context opts = { options } provider={provider} />
<div className='remix_ui_terminal_buttons'>
<div
className='remix_ui_terminal_debug btn btn-primary btn-sm'

@ -3,7 +3,7 @@ import CheckTxStatus from './ChechTxStatus' // eslint-disable-line
import Context from './Context' // eslint-disable-line
import showTable from './Table'
const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash, txDetails, modal }) => {
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,
<span id={`tx${tx.hash}`} key={index}>
<div className="remix_ui_terminal_log" onClick={(event) => txDetails(event, tx)}>
<CheckTxStatus tx={receipt || tx} type={txType} />
<Context opts = { options } blockchain={plugin.blockchain} />
<Context opts = { options } provider={provider} />
<div className='remix_ui_terminal_buttons'>
<div className='remix_ui_terminal_debug btn btn-primary btn-sm'
data-shared='txLoggerDebugButton'

@ -144,52 +144,52 @@ export const registerScriptRunnerReducer = (state, action) => {
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 })
}
}
}

@ -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}
/>}
</div>
)
@ -517,6 +528,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
showTableHash = { showTableHash }
txDetails = { txDetails }
modal={modal}
provider={x.provider}
/>) }
</div>
)

@ -7,7 +7,7 @@ interface HomeProps {
function Home ({ verticalIconPlugin }: HomeProps) {
return (
<div
className="mt-3 my-1 remixui_homeIcon"
className="mt-2 my-1 remixui_homeIcon"
onClick={async () => await verticalIconPlugin.activateHome()}
{...{ plugin: 'home'}}
title="Home"

@ -85,7 +85,7 @@ const Icon = ({
return (
<>
<div
className={`remixui_icon m-2 pl-1`}
className={`remixui_icon m-2 pt-1`}
onClick={() => {
(verticalIconPlugin as any).toggle(name)
}}

@ -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;
}

@ -90,7 +90,7 @@ const RemixUiVerticalIconsPanel = ({
<Chevron
direction='up'
divElementRef={scrollableRef}
cssRule={'fa fa-chevron-up remixui_icon-chevron mt-0 mb-0 ml-1 pl-3'}
cssRule={'fa fa-chevron-up remixui_icon-chevron my-0'}
/>
) : null
}
@ -109,19 +109,19 @@ const RemixUiVerticalIconsPanel = ({
itemContextAction={itemContextAction}
/>
</div>
<div>
{ scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? (<Chevron
divElementRef={scrollableRef}
direction='down'
cssRule={'fa fa-chevron-down remixui_icon-chevron mt-0 mb-0 ml-1 pl-3'}
/>) : null }
<IconList
theme={theme}
icons={icons.filter((p) => p.profile.name === 'settings' || p.profile.name === 'pluginManager')}
verticalIconsPlugin={verticalIconsPlugin}
itemContextAction={itemContextAction}
/>
</div>
<div className="remixui_default-icons-container border-0">
{ scrollableRef.current && scrollableRef.current.scrollHeight > scrollableRef.current.clientHeight ? (<Chevron
divElementRef={scrollableRef}
direction='down'
cssRule={'fa fa-chevron-down remixui_icon-chevron my-0'}
/>) : null }
<IconList
theme={theme}
icons={icons.filter((p) => p.profile.name === 'settings' || p.profile.name === 'pluginManager')}
verticalIconsPlugin={verticalIconsPlugin}
itemContextAction={itemContextAction}
/>
</div>
</div>
</div>
)

@ -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

@ -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": {

Loading…
Cancel
Save