Merge branch 'master' of https://github.com/ethereum/remix-project into filedecoratorplugin

pull/2668/head
bunsenstraat 2 years ago
commit d0febd2d35
  1. 2
      apps/remix-ide-e2e/src/tests/pluginManager.test.ts
  2. 40
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  3. 2
      apps/remix-ide/src/blockchain/execution-context.js
  4. 3
      apps/solidity-compiler/src/app/compiler-api.ts
  5. 2
      apps/vyper/src/app/app.tsx
  6. 2
      libs/remix-analyzer/test/analysis/staticAnalysisIntegration-test-0.4.24.ts
  7. 12
      libs/remix-tests/src/run.ts
  8. 3
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx
  9. 133
      libs/remix-ui/editor/src/lib/cairoSyntax.ts
  10. 20
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  11. 132
      libs/remix-ui/editor/src/lib/syntaxes/cairo.ts
  12. 4
      libs/remix-ui/editor/src/lib/syntaxes/solidity.ts
  13. 144
      libs/remix-ui/editor/src/lib/syntaxes/zokrates.ts
  14. 2
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  15. 13
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  16. 6
      libs/remix-ui/settings/src/lib/github-settings.tsx
  17. 14
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  18. 18
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  19. 10
      libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx
  20. 70
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  21. 12
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  22. 3
      package.json
  23. 682
      yarn.lock

@ -109,7 +109,7 @@ module.exports = {
'Should load back installed plugins after reload': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.waitForElementVisible('*[data-id="remixIdeSidePanel"]',3000)
.waitForElementVisible('*[data-id="pluginManagerComponentPluginManager"]')
.getInstalledPlugins((plugins) => {
browser.refresh()

@ -200,7 +200,31 @@ module.exports = {
.sendKeys('body', [browser.Keys.CONTROL, browser.Keys.SHIFT, 's'])
.pause(15000)
.journalLastChildIncludes('147')
}
},
'Should run a script which log transaction and block using web3.js and ethers #group7': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('udapp')
.switchEnvironment('External Http Provider')
.waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(() => {
(document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus()
}, [], () => {})
.setValue('[data-id="modalDialogCustomPromp"]', 'https://remix-goerli.ethdevops.io')
.modalFooterOKClick('basic-http-provider')
.clickLaunchIcon('filePanel')
.openFile('README.txt')
.addFile('scripts/log_tx_block.js', { content: scriptBlockAndTransaction } )
.pause(1000)
.executeScriptInTerminal('remix.execute(\'scripts/log_tx_block.js\')')
.pause(10000)
// check if the input of the transaction is being logged (web3 call)
.journalChildIncludes('0x775526410000000000000000000000000000000000000000000000000000000000000060464c0335b2f1609abd9de25141c0a3b49db516fc7375970dc737c32b986e88e3000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000000602926b30b10e7a514d92bc71e085f5bff2687fac2856ae43ef7621bf1756fa370516d310bec5727543089be9a4d5f68471174ee528e95a2520b0ca36c2b6c6eb0000000000000000000000000000000000000000000000000000000000046f49036f5e4ea4dd042801c8841e3db8e654124305da0f11824fc1db60c405dbb39f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
// check if the logsBloom is being logged (web3 call)
.journalChildIncludes('0x00000000000000000000000000100000000000000000020000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000040000000060000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000100000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001')
// check if the logsBloom is being logged (ethers.js call)
.journalChildIncludes('"hex":"0x025cd8"')
}
}
const asyncAwait = `
@ -605,3 +629,17 @@ const scriptAutoExec = {
})()
`
}
const scriptBlockAndTransaction = `
// Right click on the script name and hit "Run" to execute
(async () => {
try {
web3.eth.getTransaction('0x022ccd55747677ac50f8d9dfd1bf5b843fa2f36438a28c1d0a0958e057bb3e2a').then(console.log)
web3.eth.getBlock('7367447').then(console.log);
let ethersProvider = new ethers.providers.Web3Provider(web3Provider)
ethersProvider.getBlock(7367447).then(console.log)
} catch (e) {
console.log(e.message)
}
})()
`

@ -156,7 +156,7 @@ export class ExecutionContext {
return cb()
} else {
if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) {
if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
if (!await injectedProvider._metamask.isUnlocked()) infoCb('Please make sure the injected provider is unlocked (e.g Metamask).')
}
this.askPermission()
this.executionContext = context

@ -222,9 +222,10 @@ export const CompilerApiMixin = (Base) => class extends Base {
}
this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler)
this.data.eventHandlers.onCompilerLoaded = () => {
this.data.eventHandlers.onCompilerLoaded = (version) => {
this.data.loading = false
this.statusChanged({ key: 'none' })
this.emit('compilerLoaded', version)
}
this.compiler.event.register('compilerLoaded', this.data.eventHandlers.onCompilerLoaded)

@ -59,7 +59,7 @@ const App: React.FC = () => {
function compilerUrl() {
return state.environment === 'remote'
? 'https://vyper.live/compile'
? 'https://vyper.remixproject.org/compile'
: state.localUrl
}

@ -38,7 +38,7 @@ const testFiles: string[] = [
'forLoopIteratesOverDynamicArray.sol'
]
let compilationResults: Record<string, CompilationResult> = {}
const compilationResults: Record<string, CompilationResult> = {}
test('setup', function (t: test.Test) {
solcOrg.loadRemoteVersion('v0.4.24+commit.e67f0147', (error, compiler) => {

@ -64,7 +64,7 @@ commander
// If path is for a file, file name must have `_test.sol` suffix
if (!isDirectory && !testsPath.endsWith('_test.sol')) {
log.error('Test filename should end with "_test.sol"')
process.exit()
process.exit(1)
}
// Console message
@ -85,7 +85,7 @@ commander
const compString = releases ? releases[compVersion] : null
if (!compString) {
log.error(`No compiler found in releases with version ${compVersion}`)
process.exit()
process.exit(1)
} else {
compilerConfig.currentCompilerUrl = compString.replace('soljson-', '').replace('.js', '')
log.info(`Compiler version set to ${compVersion}. Latest version is ${latestRelease}`)
@ -105,7 +105,7 @@ commander
if (commander.runs) {
if (!commander.optimize) {
log.error('Optimization should be enabled for runs')
process.exit()
process.exit(1)
}
compilerConfig.runs = commander.runs
log.info(`Runs set to ${compilerConfig.runs}`)
@ -116,12 +116,14 @@ commander
await provider.init()
web3.setProvider(provider)
extend(web3)
runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig)
runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig, (error) => {
if (error) process.exit(1)
})
})
if (!process.argv.slice(2).length) {
log.error('Please specify a file or directory path')
process.exit()
process.exit(1)
}
commander.parse(process.argv)

@ -1,6 +1,7 @@
import React from 'react' // eslint-disable-line
import DropdownPanel from './dropdown-panel' // eslint-disable-line
import { BN } from 'ethereumjs-util'
import Web3 from 'web3'
export const GlobalVariables = ({ block, receipt, tx }) => {
// see https://docs.soliditylang.org/en/latest/units-and-global-variables.html#block-and-transaction-properties
@ -17,7 +18,7 @@ export const GlobalVariables = ({ block, receipt, tx }) => {
'tx.origin': tx.from
}
if (block.baseFeePerGas) {
globals['block.basefee'] = (new BN(block.baseFeePerGas.replace('0x', ''), 'hex')).toString(10) + ` Wei (${block.baseFeePerGas})`
globals['block.basefee'] = Web3.utils.toBN(block.baseFeePerGas).toString(10) + ` Wei (${block.baseFeePerGas})`
}
return (

@ -1,133 +0,0 @@
/* eslint-disable */
export const cairoConf = {
comments: {
lineComment: '#'
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
['%{', '%}']
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '%{', close: '%}' },
{ open: "'", close: "'", notIn: ['string', 'comment'] }
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '%{', close: '%}' },
{ open: "'", close: "'" }
]
}
export const cairoLang = {
defaultToken: '',
tokenPostfix: '.cairo',
brackets: [
{ token: 'delimiter.curly', open: '{', close: '}' },
{ token: 'delimiter.parenthesis', open: '(', close: ')' },
{ token: 'delimiter.square', open: '[', close: ']' },
{ token: 'delimiter.curly', open: '%{', close: '%}' }
],
keywords: [
// control
'if',
'else',
'end',
// meta
'alloc_locals',
'as',
'assert',
'cast',
'const',
'dw',
'felt',
'from',
'func',
'import',
'let',
'local',
'member',
'nondet',
'return',
'static_assert',
'struct',
'tempvar',
'with_attr',
'with',
// register
'ap',
'fp',
// opcode
'call',
'jmp',
'ret',
'abs',
'rel'
],
operators: ['=', ':', '==', '++', '+', '-', '*', '**', '/', '&', '%', '_'],
// we include these common regular expressions
symbols: /[=><!~?:&|+\-*\/\^%]+/,
numberDecimal: /[+-]?[0-9]+/,
numberHex: /[+-]?0x[0-9a-fA-F]+/,
// The main tokenizer for our languages
tokenizer: {
root: [
// identifiers and keywords
[
/[a-zA-Z_]\w*/,
{
cases: {
'@keywords': { token: 'keyword.$0' },
'@default': 'identifier'
}
}
],
// whitespace
{ include: '@whitespace' },
// directives
[/^%[a-zA-Z]\w*/, 'tag'],
// delimiters and operators
[/[{}()\[\]]/, '@brackets'],
[/[<>](?!@symbols)/, '@brackets'],
[
/@symbols/,
{
cases: {
'@operators': 'delimiter',
'@default': ''
}
}
],
// numbers
[/(@numberHex)/, 'number.hex'],
[/(@numberDecimal)/, 'number'],
// strings
[/'[^']*'/, 'string']
],
whitespace: [
[/\s+/, 'white'],
[/(^#.*$)/, 'comment']
]
}
}

@ -2,8 +2,9 @@ import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint
import { RemixUiEditorContextView, astNode } from '@remix-ui/editor-context-view'
import Editor, { loader } from '@monaco-editor/react'
import { reducerActions, reducerListener, initialState } from './actions/editor'
import { language, conf } from './syntax'
import { cairoLang, cairoConf } from './cairoSyntax'
import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity'
import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo'
import { zokratesTokensProvider, zokratesLanguageConfig } from './syntaxes/zokrates'
import './remix-ui-editor.css'
import { loadTypes } from './web-types'
@ -255,6 +256,8 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity')
} else if (file.language === 'cairo') {
monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo')
} else if (file.language === 'zokrates') {
monacoRef.current.editor.setModelLanguage(file.model, 'remix-zokrates')
}
}, [props.currentFile])
@ -464,12 +467,17 @@ export const EditorUI = (props: EditorUIProps) => {
// Register a new language
monacoRef.current.languages.register({ id: 'remix-solidity' })
monacoRef.current.languages.register({ id: 'remix-cairo' })
monacoRef.current.languages.register({ id: 'remix-zokrates' })
// Register a tokens provider for the language
monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', language)
monacoRef.current.languages.setLanguageConfiguration('remix-solidity', conf)
monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider)
monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig)
monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoTokensProvider)
monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoLanguageConfig)
monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoLang)
monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoConf)
monacoRef.current.languages.setMonarchTokensProvider('remix-zokrates', zokratesTokensProvider)
monacoRef.current.languages.setLanguageConfiguration('remix-zokrates', zokratesLanguageConfig)
loadTypes(monacoRef.current)
}

@ -0,0 +1,132 @@
/* eslint-disable */
export const cairoLanguageConfig = {
comments: {
lineComment: "#",
},
brackets: [
["{", "}"],
["[", "]"],
["(", ")"],
["%{", "%}"],
],
autoClosingPairs: [
{ open: "{", close: "}" },
{ open: "[", close: "]" },
{ open: "(", close: ")" },
{ open: "%{", close: "%}" },
{ open: "'", close: "'", notIn: ["string", "comment"] },
],
surroundingPairs: [
{ open: "{", close: "}" },
{ open: "[", close: "]" },
{ open: "(", close: ")" },
{ open: "%{", close: "%}" },
{ open: "'", close: "'" },
],
}
export const cairoTokensProvider = {
defaultToken: "",
tokenPostfix: ".cairo",
brackets: [
{ token: "delimiter.curly", open: "{", close: "}" },
{ token: "delimiter.parenthesis", open: "(", close: ")" },
{ token: "delimiter.square", open: "[", close: "]" },
{ token: "delimiter.curly", open: "%{", close: "%}" },
],
keywords: [
// control
"if",
"else",
"end",
// meta
"alloc_locals",
"as",
"assert",
"cast",
"const",
"dw",
"felt",
"from",
"func",
"import",
"let",
"local",
"member",
"nondet",
"return",
"static_assert",
"struct",
"tempvar",
"with_attr",
"with",
// register
"ap",
"fp",
// opcode
"call",
"jmp",
"ret",
"abs",
"rel",
],
operators: ["=", ":", "==", "++", "+", "-", "*", "**", "/", "&", "%", "_"],
// we include these common regular expressions
symbols: /[=><!~?:&|+\-*\/\^%]+/,
numberDecimal: /[+-]?[0-9]+/,
numberHex: /[+-]?0x[0-9a-fA-F]+/,
// The main tokenizer for our languages
tokenizer: {
root: [
// identifiers and keywords
[
/[a-zA-Z_]\w*/,
{
cases: {
"@keywords": { token: "keyword.$0" },
"@default": "identifier",
},
},
],
// whitespace
{ include: "@whitespace" },
// directives
[/^%[a-zA-Z]\w*/, "tag"],
// delimiters and operators
[/[{}()\[\]]/, "@brackets"],
[/[<>](?!@symbols)/, "@brackets"],
[
/@symbols/,
{
cases: {
"@operators": "delimiter",
"@default": "",
},
},
],
// numbers
[/(@numberHex)/, "number.hex"],
[/(@numberDecimal)/, "number"],
// strings
[/'[^']*'/, "string"],
],
whitespace: [
[/\s+/, "white"],
[/(^#.*$)/, "comment"],
],
},
}

@ -1,5 +1,5 @@
/* eslint-disable */
export const conf = {
export const solidityLanguageConfig = {
comments: {
lineComment: '//',
blockComment: ['/*', '*/']
@ -19,7 +19,7 @@ export const conf = {
]
}
export const language = {
export const solidityTokensProvider = {
defaultToken: '',
tokenPostfix: '.sol',

@ -0,0 +1,144 @@
/* eslint-disable */
export const zokratesLanguageConfig = {
comments: {
lineComment: '//',
blockComment: ['/*', '*/']
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
['<', '>']
],
autoClosingPairs: [
{ open: '"', close: '"', notIn: ['string', 'comment'] },
{ open: '{', close: '}', notIn: ['string', 'comment'] },
{ open: '[', close: ']', notIn: ['string', 'comment'] },
{ open: '(', close: ')', notIn: ['string', 'comment'] },
{ open: '<', close: '>', notIn: ['string', 'comment'] }
]
}
export const zokratesTokensProvider = {
defaultToken: "",
tokenPostfix: ".zok",
keywords: [
"log",
"assert",
"as",
"bool",
"const",
"def",
"else",
"false",
"field",
"for",
"if",
"import",
"from",
"in",
"mut",
"private",
"public",
"return",
"struct",
"true",
"type",
"u8",
"u16",
"u32",
"u64",
],
typeKeywords: ["bool", "field", "u8", "u16", "u32", "u64"],
operators: [
"=",
">",
"<",
"!",
"?",
":",
"==",
"<=",
">=",
"!=",
"&&",
"||",
"+",
"-",
"*",
"**",
"/",
"&",
"|",
"^",
"%",
"<<",
">>",
],
decimalSuffix: /(u16|u32|u64|u8|f)?/,
// we include these common regular expressions
symbols: /[=><!~?:&|+\-*\/\^%]+/,
escapes:
/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
// The main tokenizer for our languages
tokenizer: {
root: [
// identifiers and keywords
[
/[a-zA-Z]\w*/,
{
cases: {
"@typeKeywords": "type.identifier",
"@keywords": "keyword",
"@default": "identifier",
},
},
],
// whitespace
{ include: "@whitespace" },
// delimiters and operators
[/[{}()\[\]]/, "@brackets"],
[/[<>](?!@symbols)/, "@brackets"],
[/@symbols/, { cases: { "@operators": "operator", "@default": "" } }],
// numbers
[/0[xX][0-9a-fA-F]+/, "number.hex"],
[/\d+(@decimalSuffix)/, "number"],
// delimiter
[/[;,.]/, "delimiter"],
// strings
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
[/"/, { token: "string.quote", bracket: "@open", next: "@string" }],
],
comment: [
[/[^\/*]+/, "comment"],
[/\/\*/, "comment", "@push"], // nested comment
["\\*/", "comment", "@pop"],
[/[\/*]/, "comment"],
],
string: [
[/[^\\"]+/, "string"],
[/@escapes/, "string.escape"],
[/\\./, "string.escape.invalid"],
[/"/, { token: "string.quote", bracket: "@close", next: "@pop" }],
],
whitespace: [
[/[ \t\r\n]+/, "white"],
[/\/\*/, "comment", "@comment"],
[/\/\/.*$/, "comment"],
],
},
}

@ -237,7 +237,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
<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 online videos promoting "liquidity front runner bots".
Scam Alert: There are video tutorials going around that provide urls other than remix.ethereum.org, and could be scams. Also, beware of online videos promoting "liquidity front runner bots".
</span>
<a className="remixui_home_text" target="__blank" href="https://medium.com/remix-ide/remix-in-youtube-crypto-scams-71c338da32d">Learn more</a>
</div>

@ -25,22 +25,23 @@ export function RecorderUI (props: RecorderProps) {
setToggleExpander(!toggleExpander)
}
return (
<div className="udapp_cardContainer list-group-item border border-bottom">
<div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}>
<div className="d-flex">
<div className="d-flex justify-content-center align-items-center">
<label className="mt-1 udapp_recorderSectionLabel">Transactions recorded</label>
<div className="ml-2 mb-2 badge badge-pill badge-primary" title="The number of recorded transactions">{props.count}</div>
<div className="ml-2 badge badge-pill badge-primary text-center" title="The number of recorded transactions">{props.count}</div>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>Save transactions (deployed contracts and function executions) and replay them in another environment. <br/> e.g Transactions created in Remix VM can be replayed in the Injected Provider.
</span>
</Tooltip>
}>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
</OverlayTrigger>
</div>
<div>
<div className="p-3">
<span data-id='udappRecorderTitleExpander' onClick={toggleClass}>
<i className={!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down'} aria-hidden="true"></i>
</span>
@ -56,7 +57,7 @@ export function RecorderUI (props: RecorderProps) {
}>
<label className="form-check-label custom-control-label" data-id="runtabLivemodeInput" htmlFor="livemode-recorder">Run transactions using the latest compilation result</label>
</OverlayTrigger>
</div>
</div>
<div className="mb-1 mt-1 udapp_transactionActions">
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="tooltip-save-recorder">
@ -72,7 +73,7 @@ export function RecorderUI (props: RecorderProps) {
}>
<button className="btn btn-sm btn-info runtransaction udapp_runTxs" data-id="runtransaction" title={enableRunButton ? 'No scenario file selected' : ''} disabled={enableRunButton} onClick={handleClickRunButton}>Run</button>
</OverlayTrigger>
</div>
</div>
</div>
</div>
)

@ -51,7 +51,7 @@ export function GithubSettings (props: GithubSettingsProps) {
<p className="">Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only \'create gist\' permission.</p>
<p className="mb-1"><a className="text-primary" target="_blank" href="https://github.com/settings/tokens">https://github.com/settings/tokens</a></p>
<div>
<label>TOKEN:</label>
<label className="mb-0 pb-0">TOKEN:</label>
<div className="input-group text-secondary mb-0 h6">
<input id="gistaccesstoken" data-id="settingsTabGistAccessToken" type="password" className="form-control" onChange={(e) => handleChangeTokenState(e)} value={ githubToken } />
<div className="input-group-append">
@ -60,13 +60,13 @@ export function GithubSettings (props: GithubSettingsProps) {
</div>
</div>
<div>
<label>USERNAME:</label>
<label className="pt-2 mb-0 pb-0">USERNAME:</label>
<div className="text-secondary mb-0 h6">
<input id="githubusername" data-id="settingsTabGithubUsername" type="text" className="form-control" onChange={(e) => handleChangeUserNameState(e)} value={ githubUserName } />
</div>
</div>
<div>
<label>EMAIL:</label>
<label className="pt-2 mb-0 pb-0">EMAIL:</label>
<div className="text-secondary mb-0 h6">
<input id="githubemail" data-id="settingsTabGithubEmail" type="text" className="form-control" onChange={(e) => handleChangeEmailState(e)} value={ githubEmail } />
<div className="d-flex justify-content-end pt-2">

@ -248,12 +248,12 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
<div className="border-top">
<div className="card-body pt-3 pb-2">
<h6 className="card-title">{ swarmSettingsTitle }</h6>
<div className="pt-2 mb-1"><label>PRIVATE BEE ADDRESS:</label>
<div className="pt-2 pt-2 mb-0 pb-0"><label>PRIVATE BEE ADDRESS:</label>
<div className="text-secondary mb-0 h6">
<input id="swarmprivatebeeaddress" data-id="settingsPrivateBeeAddress" className="form-control" onChange={handleSavePrivateBeeAddress} value={ privateBeeAddress } />
</div>
</div>
<div className=""><label>POSTAGE STAMP ID:</label>
<div className="pt-2 mb-0 pb-0"><label>POSTAGE STAMP ID:</label>
<div className="text-secondary mb-0 h6">
<input id="swarmpostagestamp" data-id="settingsPostageStampId" className="form-control" onChange={handleSavePostageStampId} value={ postageStampId } />
<div className="d-flex justify-content-end pt-2">
@ -312,27 +312,27 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
<div className="border-top">
<div className="card-body pt-3 pb-2">
<h6 className="card-title">{ ipfsSettingsText }</h6>
<div className="pt-2 mb-1"><label>IPFS HOST:</label>
<div className="pt-2 mb-0"><label>IPFS HOST:</label>
<div className="text-secondary mb-0 h6">
<input placeholder='e.g. ipfs.infura.io' id="settingsIpfsUrl" data-id="settingsIpfsUrl" className="form-control" onChange={handleSaveIpfsUrl} value={ ipfsUrl } />
</div>
</div>
<div className=""><label>IPFS PROTOCOL:</label>
<div className="pt-2 mb-0 pb-0"><label>IPFS PROTOCOL:</label>
<div className="text-secondary mb-0 h6">
<input placeholder='e.g. https' id="settingsIpfsProtocol" data-id="settingsIpfsProtocol" className="form-control" onChange={handleSaveIpfsProtocol} value={ ipfsProtocol } />
</div>
</div>
<div className=""><label>IPFS PORT:</label>
<div className="pt-2 mb-0 pb-0"><label>IPFS PORT:</label>
<div className="text-secondary mb-0 h6">
<input placeholder='e.g. 5001' id="settingsIpfsPort" data-id="settingsIpfsPort" className="form-control" onChange={handleSaveIpfsPort} value={ ipfsPort } />
</div>
</div>
<div className=""><label>IPFS PROJECT ID [ INFURA ]:</label>
<div className="pt-2 mb-0 pb-0"><label>IPFS PROJECT ID [ INFURA ]:</label>
<div className="text-secondary mb-0 h6">
<input id="settingsIpfsProjectId" data-id="settingsIpfsProjectId" className="form-control" onChange={handleSaveIpfsProjectId} value={ ipfsProjectId } />
</div>
</div>
<div className=""><label>IPFS PROJECT SECRET [ INFURA ]:</label>
<div className="pt-2 mb-0 pb-0"><label>IPFS PROJECT SECRET [ INFURA ]:</label>
<div className="text-secondary mb-0 h6">
<input id="settingsIpfsProjectSecret" data-id="settingsIpfsProjectSecret" className="form-control" type="password" onChange={handleSaveIpfsSecret} value={ ipfsProjectSecret } />
</div>

@ -1,4 +1,5 @@
import React, { useState, useRef, useEffect, ReactElement } from 'react' // eslint-disable-line
import * as semver from 'semver'
import { eachOfSeries } from 'async' // eslint-disable-line
import type Web3 from 'web3'
import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity'
@ -154,8 +155,23 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
await setCurrentPath(defaultPath)
})
const truncateVersion = (version: string) => {
const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version)
return tmp ? tmp[1] : version
}
testTab.fileManager.events.on('noFileSelected', async () => { await updateForNewCurrent() })
testTab.fileManager.events.on('currentFileChanged', async (file: string) => await updateForNewCurrent(file))
testTab.fileManager.events.on('currentFileChanged', async (file: string) => {
await updateForNewCurrent(file)
})
testTab.on('solidity', 'compilerLoaded', async (version: string) => {
const { currentVersion } = testTab.compileTab.getCurrentCompilerConfig()
if (!semver.gt(truncateVersion(currentVersion), '0.4.12')) {
setDisableRunButton(true)
setRunButtonTitle('Please select Solidity compiler version greater than 0.4.12.')
}
})
}, []) // eslint-disable-line

@ -3,16 +3,20 @@ import React from 'react' //eslint-disable-line
interface StaticAnalyserButtonProps {
onClick: (event) => void
buttonText: string,
disabled?: boolean
disabled?: boolean,
title?: string
}
const StaticAnalyserButton = ({
onClick,
buttonText,
disabled
disabled,
title
}: StaticAnalyserButtonProps) => {
let classList = "btn btn-sm w-25 btn-primary"
classList += disabled ? " disabled" : ""
return (
<button className="btn btn-sm w-25 btn-primary" onClick={onClick} disabled={disabled}>
<button className={classList} disabled={disabled} title={title} onClick={onClick}>
{buttonText}
</button>
)

@ -2,6 +2,7 @@ import React, { useEffect, useState, useReducer, useRef } from 'react' // eslint
import Button from './Button/StaticAnalyserButton' // eslint-disable-line
import { util } from '@remix-project/remix-lib'
import _ from 'lodash'
import * as semver from 'semver'
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-line
import { RemixUiCheckbox } from '@remix-ui/checkbox' // eslint-disable-line
import ErrorRenderer from './ErrorRenderer' // eslint-disable-line
@ -64,14 +65,30 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
const [autoRun, setAutoRun] = useState(true)
const [slitherEnabled, setSlitherEnabled] = useState(false)
const [showSlither, setShowSlither] = useState(false)
const [isSupportedVersion, setIsSupportedVersion] = useState(false)
let [showLibsWarning, setShowLibsWarning] = useState(false) // eslint-disable-line prefer-const
const [categoryIndex, setCategoryIndex] = useState(groupedModuleIndex(groupedModules))
const [warningState, setWarningState] = useState({})
const [runButtonTitle, setRunButtonTitle] = useState<string>('Run Static Analysis')
const warningContainer = useRef(null)
const allWarnings = useRef({})
const [state, dispatch] = useReducer(analysisReducer, initialState)
const setDisableForRun = (version: string) => {
const truncateVersion = (version: string) => {
const tmp: RegExpExecArray | null = /^(\d+.\d+.\d+)/.exec(version)
return tmp ? tmp[1] : version
}
if (version != '' && !semver.gt(truncateVersion(version), '0.4.12')) {
setIsSupportedVersion(false)
setRunButtonTitle('Sselect Solidity compiler version greater than 0.4.12.')
} else {
setIsSupportedVersion(true)
setRunButtonTitle('Run static analysis')
}
}
useEffect(() => {
compilation(props.analysisModule, dispatch)
}, [props])
@ -91,6 +108,10 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
return () => { }
}, [state])
useEffect(() => {
props.analysisModule.call('solidity', 'getCompilerState').then((compilerState) => setDisableForRun(compilerState.currentVersion))
}, [])
useEffect(() => {
props.analysisModule.on('filePanel', 'setWorkspace', (currentWorkspace) => {
// Reset warning state
@ -119,20 +140,24 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
setSlitherEnabled(false)
}
})
props.analysisModule.on('solidity', 'compilerLoaded', async (version: string) => {
setDisableForRun(version)
})
return () => { }
}, [props])
const message = (name, warning, more, fileName, locationString) : string => {
return (`
<span className='d-flex flex-column'>
<span className='h6 font-weight-bold'>${name}</span>
${warning}
${more
? (<span><a href={more} target='_blank'>more</a></span>)
: (<span> </span>)
}
<span className="" title={Position in ${fileName}}>Pos: ${locationString}</span>
</span>`
<span className='d-flex flex-column'>
<span className='h6 font-weight-bold'>${name}</span>
${warning}
${more
? (<span><a href={more} target='_blank'>more</a></span>)
: (<span> </span>)
}
<span className="" title={Position in ${fileName}}>Pos: ${locationString}</span>
</span>`
)
}
@ -183,6 +208,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
}
const run = async (lastCompilationResult, lastCompilationSource, currentFile) => {
if (!isSupportedVersion) return
if (state.data !== null) {
if (lastCompilationResult && (categoryIndex.length > 0 || slitherEnabled)) {
const warningMessage = []
@ -474,7 +500,12 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
label="Autorun"
onChange={() => {}}
/>
<Button buttonText="Run" onClick={async () => await run(state.data, state.source, state.file)} disabled={(state.data === null || categoryIndex.length === 0) && !slitherEnabled }/>
<Button
buttonText="Run"
title={runButtonTitle}
onClick={async () => await run(state.data, state.source, state.file)}
disabled={(state.data === null || categoryIndex.length === 0) && !slitherEnabled || !isSupportedVersion }
/>
</div>
{ showSlither &&
<div className="d-flex mt-2" id="enableSlitherAnalysis">
@ -520,15 +551,15 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
{Object.entries(warningState).length > 0 &&
<div id='staticanalysisresult' >
<RemixUiCheckbox
id="showLibWarnings"
name="showLibWarnings"
categoryId="showLibWarnings"
title="when checked, the results are also displayed for external contract libraries"
inputType="checkbox"
checked={showLibsWarning}
label="Show warnings for external libraries"
onClick={handleShowLibsWarning}
onChange={() => {}}
id="showLibWarnings"
name="showLibWarnings"
categoryId="showLibWarnings"
title="when checked, the results are also displayed for external contract libraries"
inputType="checkbox"
checked={showLibsWarning}
label="Show warnings for external libraries"
onClick={handleShowLibsWarning}
onChange={() => {}}
/>
<br/>
<div className="mb-4">
@ -541,7 +572,6 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
<div data-id={`staticAnalysisModule${x.warningModuleName}${i}`} id={`staticAnalysisModule${x.warningModuleName}${i}`} key={i}>
<ErrorRenderer name={`staticAnalysisModule${x.warningModuleName}${i}`} message={x.msg} opt={x.options} warningErrors={ x.warningErrors} editor={props.analysisModule}/>
</div>
) : null
))}
</div>

@ -543,17 +543,25 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
})
} else if (Array.isArray(x.message)) {
return x.message.map((msg, i) => {
if (!msg) msg = 'null'
if (React.isValidElement(msg)) {
return (
<div className="px-4 block" data-id="block" key={i}><span className={x.style}>{ msg }</span></div>
)
} else if (typeof msg === 'object') {
let stringified
try {
stringified = JSON.stringify(msg)
} catch (e) {
console.error(e)
stringified = '< value not displayable >'
}
return (
<div className={classNameBlock} data-id="block" key={i}><span className={x.style}>{ msg.value && typeof msg.value !== 'object' ? parse(msg.value) : JSON.stringify(msg) } </span></div>
<div className={classNameBlock} data-id="block" key={i}><span className={x.style}>{ stringified } </span></div>
)
} else {
return (
<div className={classNameBlock} data-id="block" key={i}><span className={x.style}>{msg? msg.toString() : null}</span></div>
<div className={classNameBlock} data-id="block" key={i}><span className={x.style}>{msg ? msg.toString() : null}</span></div>
)
}
})

@ -211,7 +211,7 @@
"time-stamp": "^2.2.0",
"ts-loader": "^9.2.6",
"tslib": "^2.3.0",
"web3": "^1.5.1",
"web3": "^1.7.5",
"winston": "^3.3.3",
"ws": "^7.3.0"
},
@ -252,6 +252,7 @@
"@types/react-dom": "^17.0.9",
"@types/react-router-dom": "^5.3.0",
"@types/request": "^2.48.7",
"@types/semver": "^7.3.10",
"@types/tape": "^4.13.0",
"@types/ws": "^7.2.4",
"@typescript-eslint/eslint-plugin": "^4.32.0",

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save