this.highlightLocation(result.location, runningTests, result.filename)}
>
-
@@ -499,7 +499,7 @@ module.exports = class TestTab extends ViewPlugin {
usingWorker: canUseWorker(currentVersion),
runs
}
- this.testRunner.runTestSources(runningTest, compilerConfig, () => {}, () => {}, (error, result) => {
+ this.testRunner.runTestSources(runningTest, compilerConfig, () => {}, () => {}, null, (error, result) => {
if (error) return reject(error)
resolve(result)
}, (url, cb) => {
@@ -527,17 +527,22 @@ module.exports = class TestTab extends ViewPlugin {
usingWorker: canUseWorker(currentVersion),
runs
}
+ const deployCb = async (file, contractAddress) => {
+ const compilerData = await this.call('compilerArtefacts', 'getCompilerAbstract', file)
+ await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilerData)
+ }
this.testRunner.runTestSources(
runningTests,
compilerConfig,
(result) => this.testCallback(result, runningTests),
(_err, result, cb) => this.resultsCallback(_err, result, cb),
+ deployCb,
(error, result) => {
this.updateFinalResult(error, result, testFilePath)
callback(error)
}, (url, cb) => {
return this.contentImport.resolveAndSave(url).then((result) => cb(null, result)).catch((error) => cb(error.message))
- }
+ }, { testFilePath }
)
}).catch((error) => {
if (error) return // eslint-disable-line
diff --git a/apps/remix-ide/src/lib/cmdInterpreterAPI.js b/apps/remix-ide/src/lib/cmdInterpreterAPI.js
index 9c79cae25b..1bd834054b 100644
--- a/apps/remix-ide/src/lib/cmdInterpreterAPI.js
+++ b/apps/remix-ide/src/lib/cmdInterpreterAPI.js
@@ -93,7 +93,6 @@ class CmdInterpreterAPI {
if (cb) cb()
return
}
-
self._components.terminal.commands.script(content)
}
diff --git a/apps/solidity-compiler/src/app/app.tsx b/apps/solidity-compiler/src/app/app.tsx
index 00ce99f3d7..85e67924b8 100644
--- a/apps/solidity-compiler/src/app/app.tsx
+++ b/apps/solidity-compiler/src/app/app.tsx
@@ -6,7 +6,7 @@ import { CompilerClientApi } from './compiler'
const remix = new CompilerClientApi()
-export const App = () => {
+export const App = () => {
return (
diff --git a/apps/solidity-compiler/src/app/compiler.ts b/apps/solidity-compiler/src/app/compiler.ts
index 5a6e8eafaa..4f90fa4bd3 100644
--- a/apps/solidity-compiler/src/app/compiler.ts
+++ b/apps/solidity-compiler/src/app/compiler.ts
@@ -30,7 +30,6 @@ const defaultCompilerParameters = {
evmVersion: null, // compiler default
language: 'Solidity'
}
-
export class CompilerClientApi extends CompilerApiMixin(PluginClient) implements ICompilerApi {
constructor () {
super()
diff --git a/libs/remix-analyzer/package.json b/libs/remix-analyzer/package.json
index c5cd096a67..e9d8c22e53 100644
--- a/libs/remix-analyzer/package.json
+++ b/libs/remix-analyzer/package.json
@@ -19,9 +19,9 @@
}
],
"dependencies": {
- "@ethereumjs/block": "^3.4.0",
- "@ethereumjs/tx": "^3.3.0",
- "@ethereumjs/vm": "^5.5.0",
+ "@ethereumjs/block": "^3.5.1",
+ "@ethereumjs/tx": "^3.3.2",
+ "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-astwalker": "^0.0.37",
"@remix-project/remix-lib": "^0.5.7",
"async": "^2.6.2",
diff --git a/libs/remix-astwalker/package.json b/libs/remix-astwalker/package.json
index fe3baecb9d..789a23dea9 100644
--- a/libs/remix-astwalker/package.json
+++ b/libs/remix-astwalker/package.json
@@ -34,9 +34,9 @@
]
},
"dependencies": {
- "@ethereumjs/block": "^3.4.0",
- "@ethereumjs/tx": "^3.3.0",
- "@ethereumjs/vm": "^5.5.0",
+ "@ethereumjs/block": "^3.5.1",
+ "@ethereumjs/tx": "^3.3.2",
+ "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.7",
"@types/tape": "^4.2.33",
"async": "^2.6.2",
diff --git a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
index da22ffb39e..52e1c7730b 100644
--- a/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
+++ b/libs/remix-core-plugin/src/lib/compiler-artefacts.ts
@@ -4,7 +4,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = {
name: 'compilerArtefacts',
- methods: ['get', 'addResolvedContract'],
+ methods: ['get', 'addResolvedContract', 'getCompilerAbstract'],
events: [],
version: '0.0.1'
}
diff --git a/libs/remix-debug/package.json b/libs/remix-debug/package.json
index 5be0192f86..bbc9b1042d 100644
--- a/libs/remix-debug/package.json
+++ b/libs/remix-debug/package.json
@@ -18,10 +18,10 @@
],
"main": "src/index.js",
"dependencies": {
- "@ethereumjs/block": "^3.4.0",
- "@ethereumjs/common": "^2.2.0",
- "@ethereumjs/tx": "^3.3.0",
- "@ethereumjs/vm": "^5.5.0",
+ "@ethereumjs/block": "^3.5.1",
+ "@ethereumjs/common": "^2.5.0",
+ "@ethereumjs/tx": "^3.3.2",
+ "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-astwalker": "^0.0.37",
"@remix-project/remix-lib": "^0.5.7",
"@remix-project/remix-simulator": "^0.2.7",
diff --git a/libs/remix-lib/package.json b/libs/remix-lib/package.json
index 5b25ecd191..f8cfe6ca60 100644
--- a/libs/remix-lib/package.json
+++ b/libs/remix-lib/package.json
@@ -14,9 +14,9 @@
],
"main": "src/index.js",
"dependencies": {
- "@ethereumjs/block": "^3.4.0",
- "@ethereumjs/tx": "^3.3.0",
- "@ethereumjs/vm": "^5.5.0",
+ "@ethereumjs/block": "^3.5.1",
+ "@ethereumjs/tx": "^3.3.2",
+ "@ethereumjs/vm": "^5.5.3",
"async": "^2.1.2",
"ethereumjs-util": "^7.0.10",
"ethers": "^4.0.40",
diff --git a/libs/remix-simulator/package.json b/libs/remix-simulator/package.json
index 891cc3bad6..ad508bcc3a 100644
--- a/libs/remix-simulator/package.json
+++ b/libs/remix-simulator/package.json
@@ -14,10 +14,10 @@
],
"main": "src/index.js",
"dependencies": {
- "@ethereumjs/block": "^3.4.0",
- "@ethereumjs/common": "^2.2.0",
- "@ethereumjs/tx": "^3.3.0",
- "@ethereumjs/vm": "^5.5.0",
+ "@ethereumjs/block": "^3.5.1",
+ "@ethereumjs/common": "^2.5.0",
+ "@ethereumjs/tx": "^3.3.2",
+ "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.7",
"ansi-gray": "^0.1.1",
"async": "^3.1.0",
diff --git a/libs/remix-solidity/package.json b/libs/remix-solidity/package.json
index 50ee1074a5..9fed725a41 100644
--- a/libs/remix-solidity/package.json
+++ b/libs/remix-solidity/package.json
@@ -15,9 +15,9 @@
}
],
"dependencies": {
- "@ethereumjs/block": "^3.4.0",
- "@ethereumjs/tx": "^3.3.0",
- "@ethereumjs/vm": "^5.5.0",
+ "@ethereumjs/block": "^3.5.1",
+ "@ethereumjs/tx": "^3.3.2",
+ "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.7",
"async": "^2.6.2",
"eslint-scope": "^5.0.0",
diff --git a/libs/remix-tests/package.json b/libs/remix-tests/package.json
index 990ad5a6a1..914bfebcc2 100644
--- a/libs/remix-tests/package.json
+++ b/libs/remix-tests/package.json
@@ -35,10 +35,10 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme",
"dependencies": {
- "@ethereumjs/block": "^3.4.0",
- "@ethereumjs/common": "^2.2.0",
- "@ethereumjs/tx": "^3.3.0",
- "@ethereumjs/vm": "^5.5.0",
+ "@ethereumjs/block": "^3.5.1",
+ "@ethereumjs/common": "^2.5.0",
+ "@ethereumjs/tx": "^3.3.2",
+ "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.7",
"@remix-project/remix-simulator": "^0.2.7",
"@remix-project/remix-solidity": "^0.4.7",
diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts
index e77fdcdcfc..e15bb656e4 100644
--- a/libs/remix-tests/src/compiler.ts
+++ b/libs/remix-tests/src/compiler.ts
@@ -171,8 +171,9 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
* @param cb Callback
*/
export function compileContractSources (sources: SrcIfc, compilerConfig: CompilerConfiguration, importFileCb: any, opts: any, cb): void {
- let compiler, filepath: string
+ let compiler
const accounts: string[] = opts.accounts || []
+ const filepath = opts.testFilePath || ''
// Iterate over sources keys. Inject test libraries. Inject test library import statements.
if (!('remix_tests.sol' in sources) && !('tests.sol' in sources)) {
sources['tests.sol'] = { content: require('../sol/tests.sol.js') }
diff --git a/libs/remix-tests/src/deployer.ts b/libs/remix-tests/src/deployer.ts
index aeefac3faa..d700b08de5 100644
--- a/libs/remix-tests/src/deployer.ts
+++ b/libs/remix-tests/src/deployer.ts
@@ -11,7 +11,7 @@ import { compilationInterface } from './types'
* @param callback Callback
*/
-export function deployAll (compileResult: compilationInterface, web3: Web3, withDoubleGas: boolean, callback) {
+export function deployAll (compileResult: compilationInterface, web3: Web3, withDoubleGas: boolean, deployCb, callback) {
const compiledObject = {}
const contracts = {}
let accounts: string[] = []
@@ -70,7 +70,7 @@ export function deployAll (compileResult: compilationInterface, web3: Web3, with
deployObject.send({
from: accounts[0],
gas: gas
- }).on('receipt', function (receipt) {
+ }).on('receipt', async function (receipt) {
contractObject.options.address = receipt.contractAddress
contractObject.options.from = accounts[0]
contractObject.options.gas = 5000 * 1000
@@ -79,6 +79,7 @@ export function deployAll (compileResult: compilationInterface, web3: Web3, with
contracts[contractName] = contractObject
contracts[contractName].filename = filename
+ if (deployCb) await deployCb(filename, receipt.contractAddress)
callback(null, { receipt: { contractAddress: receipt.contractAddress } }) // TODO this will only work with JavaScriptV VM
}).on('error', function (err) {
console.error(err)
diff --git a/libs/remix-tests/src/runTestFiles.ts b/libs/remix-tests/src/runTestFiles.ts
index b447452f22..d9ed0c2b6e 100644
--- a/libs/remix-tests/src/runTestFiles.ts
+++ b/libs/remix-tests/src/runTestFiles.ts
@@ -61,13 +61,13 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
for (const filename in asts) {
if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast }
}
- deployAll(compilationResult, web3, false, (err, contracts) => {
+ deployAll(compilationResult, web3, false, null, (err, contracts) => {
if (err) {
// If contract deployment fails because of 'Out of Gas' error, try again with double gas
// This is temporary, should be removed when remix-tests will have a dedicated UI to
// accept deployment params from UI
if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
- deployAll(compilationResult, web3, true, (error, contracts) => {
+ deployAll(compilationResult, web3, true, null, (error, contracts) => {
if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array
else next(null, compilationResult, contracts)
})
diff --git a/libs/remix-tests/src/runTestSources.ts b/libs/remix-tests/src/runTestSources.ts
index 4894b713eb..ecc35aa3f2 100644
--- a/libs/remix-tests/src/runTestSources.ts
+++ b/libs/remix-tests/src/runTestSources.ts
@@ -39,7 +39,7 @@ export class UnitTestRunner {
* @param importFileCb Import file callback
* @param opts Options
*/
- async runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, finalCallback: any, importFileCb, opts: Options) {
+ async runTestSources (contractSources: SrcIfc, compilerConfig: CompilerConfiguration, testCallback, resultCallback, deployCb:any, finalCallback: any, importFileCb, opts: Options) {
opts = opts || {}
const sourceASTs: any = {}
const web3 = opts.web3 || await this.createWeb3Provider()
@@ -53,19 +53,19 @@ export class UnitTestRunner {
})
},
(next) => {
- compileContractSources(contractSources, compilerConfig, importFileCb, { accounts, event: this.event }, next)
+ compileContractSources(contractSources, compilerConfig, importFileCb, { accounts, testFilePath: opts.testFilePath, event: this.event }, next)
},
function deployAllContracts (compilationResult: compilationInterface, asts: ASTInterface, next) {
for (const filename in asts) {
if (filename.endsWith('_test.sol')) { sourceASTs[filename] = asts[filename].ast }
}
- deployAll(compilationResult, web3, false, (err, contracts) => {
+ deployAll(compilationResult, web3, false, deployCb, (err, contracts) => {
if (err) {
// If contract deployment fails because of 'Out of Gas' error, try again with double gas
// This is temporary, should be removed when remix-tests will have a dedicated UI to
// accept deployment params from UI
if (err.message.includes('The contract code couldn\'t be stored, please check your gas limit')) {
- deployAll(compilationResult, web3, true, (error, contracts) => {
+ deployAll(compilationResult, web3, true, deployCb, (error, contracts) => {
if (error) next([{ message: 'contract deployment failed after trying twice: ' + error.message, severity: 'error' }]) // IDE expects errors in array
else next(null, compilationResult, contracts)
})
diff --git a/libs/remix-tests/src/testRunner.ts b/libs/remix-tests/src/testRunner.ts
index 102dae0bf5..fe5014d2f9 100644
--- a/libs/remix-tests/src/testRunner.ts
+++ b/libs/remix-tests/src/testRunner.ts
@@ -244,6 +244,7 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
if (func.inputs && func.inputs.length > 0) { return resultsCallback(new Error(`Method '${func.name}' can not have parameters inside a test contract`), { passingNum, failureNum, timePassed }) }
const method = testObject.methods[func.name].apply(testObject.methods[func.name], [])
const startTime = Date.now()
+ let debugTxHash:string
if (func.constant) {
sendParams = {}
const tagTimestamp = 'remix_tests_tag' + Date.now()
@@ -253,13 +254,16 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
let tagTxHash
if (web3.eth && web3.eth.getHashFromTagBySimulator) tagTxHash = await web3.eth.getHashFromTagBySimulator(tagTimestamp)
if (web3.eth && web3.eth.getHHLogsForTx) hhLogs = await web3.eth.getHHLogsForTx(tagTxHash)
+ debugTxHash = tagTxHash
if (result) {
const resp: TestResultInterface = {
type: 'testPass',
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
- context: testName
+ context: testName,
+ web3,
+ debugTxHash
}
if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs
testCallback(undefined, resp)
@@ -272,7 +276,9 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
filename: testObject.filename,
time: time,
errMsg: 'function returned false',
- context: testName
+ context: testName,
+ web3,
+ debugTxHash
}
if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs
testCallback(undefined, resp)
@@ -293,6 +299,7 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
sendParams.gas = 10000000 * 8
method.send(sendParams).on('receipt', async (receipt) => {
try {
+ debugTxHash = receipt.transactionHash
if (web3.eth && web3.eth.getHHLogsForTx) hhLogs = await web3.eth.getHHLogsForTx(receipt.transactionHash)
const time: number = (Date.now() - startTime) / 1000.0
const assertionEventHashes = assertionEvents.map(e => Web3.utils.sha3(e.name + '(' + e.params.join() + ')'))
@@ -322,7 +329,8 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
returned: testEvent[3],
expected: testEvent[4],
location,
- web3
+ web3,
+ debugTxHash
}
if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs
testCallback(undefined, resp)
@@ -341,7 +349,9 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
value: changeCase.sentenceCase(func.name),
filename: testObject.filename,
time: time,
- context: testName
+ context: testName,
+ web3,
+ debugTxHash
}
if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs
testCallback(undefined, resp)
@@ -380,6 +390,7 @@ export function runTest (testName: string, testObject: any, contractDetails: Com
const txHash = JSON.parse(err.message.replace('Transaction has been reverted by the EVM:', '')).transactionHash
if (web3.eth && web3.eth.getHHLogsForTx) hhLogs = await web3.eth.getHHLogsForTx(txHash)
if (hhLogs && hhLogs.length) resp.hhLogs = hhLogs
+ resp.debugTxHash = txHash
}
testCallback(undefined, resp)
failureNum += 1
diff --git a/libs/remix-tests/src/types.ts b/libs/remix-tests/src/types.ts
index fb52c56bae..fb03e94887 100644
--- a/libs/remix-tests/src/types.ts
+++ b/libs/remix-tests/src/types.ts
@@ -38,6 +38,7 @@ export interface TestResultInterface {
location?: string
hhLogs?: []
web3?: any
+ debugTxHash?: string
}
export interface TestCbInterface {
(error: Error | null | undefined, result: TestResultInterface) : void;
@@ -48,6 +49,7 @@ export interface ResultCbInterface {
export interface Options {
accounts?: string[] | null,
+ testFilePath?: string
web3?: any
}
diff --git a/libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol b/libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol
new file mode 100644
index 0000000000..030552b16a
--- /dev/null
+++ b/libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol
@@ -0,0 +1,12 @@
+import "remix_tests.sol"; // this import is automatically injected by Remix.
+
+contract AssertOkTest {
+
+ function okPassTest() public {
+ Assert.ok(true, "okPassTest passes");
+ }
+
+ function okFailTest() public {
+ Assert.ok(false, "okFailTest fails");
+ }
+}
\ No newline at end of file
diff --git a/libs/remix-tests/tests/testRunner.cli.spec_disabled.ts b/libs/remix-tests/tests/testRunner.cli.spec.ts
similarity index 91%
rename from libs/remix-tests/tests/testRunner.cli.spec_disabled.ts
rename to libs/remix-tests/tests/testRunner.cli.spec.ts
index 91174355af..496f6f4936 100644
--- a/libs/remix-tests/tests/testRunner.cli.spec_disabled.ts
+++ b/libs/remix-tests/tests/testRunner.cli.spec.ts
@@ -10,7 +10,9 @@ describe('testRunner: remix-tests CLI', () => {
if(result) {
const dirContent = result.stdout.toString()
// Install dependencies if 'node_modules' is not already present
- if(!dirContent.includes('node_modules')) execSync('npm install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') })
+ if(!dirContent.includes('node_modules')) {
+ execSync('npm install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') })
+ }
}
@@ -40,7 +42,7 @@ Commands:
})
test('remix-tests running a test file', () => {
- const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
@@ -55,7 +57,7 @@ Commands:
})
test('remix-tests running a test file with custom compiler version', () => {
- const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.4. Latest version is')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.4+commit.3f05b770 ...')).toBeTruthy()
@@ -69,13 +71,13 @@ Commands:
})
test('remix-tests running a test file with unavailable custom compiler version (should fail)', () => {
- const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('No compiler found in releases with version 1.10.4')).toBeTruthy()
})
test('remix-tests running a test file with custom EVM', () => {
- const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('EVM set to petersburg')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
@@ -88,7 +90,7 @@ Commands:
})
test('remix-tests running a test file by enabling optimization', () => {
- const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/)
@@ -101,7 +103,7 @@ Commands:
})
test('remix-tests running a test file by enabling optimization and setting runs', () => {
- const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Runs set to 300')).toBeTruthy()
@@ -115,13 +117,13 @@ Commands:
})
test('remix-tests running a test file without enabling optimization and setting runs (should fail)', () => {
- const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Optimization should be enabled for runs')).toBeTruthy()
})
test('remix-tests running a test file with all options', () => {
- const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
+ const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')])
// match initial lines
expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.5. Latest version is')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.5+commit.eb77ed08 ...')).toBeTruthy()
diff --git a/libs/remix-tests/tests/testRunner.spec.ts b/libs/remix-tests/tests/testRunner.spec.ts
index 351daf23f4..928491faa5 100644
--- a/libs/remix-tests/tests/testRunner.spec.ts
+++ b/libs/remix-tests/tests/testRunner.spec.ts
@@ -67,7 +67,7 @@ async function compileAndDeploy(filename: string, callback: Function) {
}
try {
compilationData = compilationResult
- deployAll(compilationResult, web3, false, next)
+ deployAll(compilationResult, web3, false, null, next)
} catch (e) {
throw e
}
@@ -128,8 +128,8 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' },
- { type: 'testPass', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 },
- { type: 'testFailure', value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '370:36:0', expected: 'true', returned: 'false'},
+ { type: 'testPass', debugTxHash: '0x5b665752a4faf83229259b9b2811d3295be0af633b0051d4b90042283ef55707', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 },
+ { type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2',value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '370:36:0', expected: 'true', returned: 'false'},
], ['time', 'web3'])
})
@@ -158,18 +158,18 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'AssertEqualTest', filename: __dirname + '/examples_0/assert_equal_test.sol' },
- { type: 'testPass', value: 'Equal uint pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
- { type: 'testFailure', value: 'Equal uint fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalUintFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '273:57:0', expected: '2', returned: '1'},
- { type: 'testPass', value: 'Equal int pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
- { type: 'testFailure', value: 'Equal int fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalIntFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '493:45:0', expected: '2', returned: '-1'},
- { type: 'testPass', value: 'Equal bool pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
- { type: 'testFailure', value: 'Equal bool fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBoolFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '708:52:0', expected: false, returned: true},
- { type: 'testPass', value: 'Equal address pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
- { type: 'testFailure', value: 'Equal address fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalAddressFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1015:130:0', expected: '0x1c6637567229159d1eFD45f95A6675e77727E013', returned: '0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9'},
- { type: 'testPass', value: 'Equal bytes32 pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
- { type: 'testFailure', value: 'Equal bytes32 fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBytes32FailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1670:48:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6979000000000000000000000000000000000000000000000000000000'},
- { type: 'testPass', value: 'Equal string pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
- { type: 'testFailure', value: 'Equal string fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalStringFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1916:81:0', expected: 'remix-tests', returned: 'remix'}
+ { type: 'testPass', debugTxHash: '0xbe77baee10f8a044a68fbb83abf57ce1ca63b8739f3b2dcd122dab0ee44fd0e9', value: 'Equal uint pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
+ { type: 'testFailure', debugTxHash: '0xfc9691b0cd0a49dbefed5cd3fad32158dd229e5bb7b0eb11da3c72054eafae8b', value: 'Equal uint fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalUintFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '273:57:0', expected: '2', returned: '1'},
+ { type: 'testPass', debugTxHash: '0xbc142789e5a51841781536a9291c9022896b0c7453140fdc98996638c0d76045', value: 'Equal int pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
+ { type: 'testFailure', debugTxHash: '0xcc28211a4ab3149b1122fb47f45529a4edddbafa076d2338cc3754ab0629eaa1', value: 'Equal int fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalIntFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '493:45:0', expected: '2', returned: '-1'},
+ { type: 'testPass', debugTxHash: '0x4f5570fc7da86f09aafb436ff3b4c46aa885f71680a233234433d0ef0346206b', value: 'Equal bool pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x28dc2d146dee77a2d6446efb088e5f9d008a3c7a14116e798401b68470da017f', value: 'Equal bool fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBoolFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '708:52:0', expected: false, returned: true},
+ { type: 'testPass', debugTxHash: '0x0abc8fa8831efa3a8c82c758d045c1382f71b6a7f7e9135ffbe9e40059f84617', value: 'Equal address pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x5ec200fb053539b8165a6b6ab36e9229a870c4752b0d6ff2786c4d5a66d5b35d', value: 'Equal address fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalAddressFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1015:130:0', expected: '0x1c6637567229159d1eFD45f95A6675e77727E013', returned: '0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9'},
+ { type: 'testPass', debugTxHash: '0xb6c34f5baa6916569d122bcb1210fcd07fb126f4b859fea58db5328e5f1dab85', value: 'Equal bytes32 pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
+ { type: 'testFailure', debugTxHash: '0xb3af74a384b8b6ddacbc03a480ae48e233415b1570717ca7023530023a871be6', value: 'Equal bytes32 fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalBytes32FailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1670:48:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6979000000000000000000000000000000000000000000000000000000'},
+ { type: 'testPass', debugTxHash: '0x8537e74941b511b5c745b398e55435748adcdf637659247e0d573fb681ee4833', value: 'Equal string pass test', filename: __dirname + '/examples_0/assert_equal_test.sol', context: 'AssertEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x30d44498e63ac51f1412062b849144c103e19a4dc9daf81c5e84bd984ef738a6', value: 'Equal string fail test', filename: __dirname + '/examples_0/assert_equal_test.sol', errMsg: 'equalStringFailTest fails', context: 'AssertEqualTest', assertMethod: 'equal', location: '1916:81:0', expected: 'remix-tests', returned: 'remix'}
], ['time', 'web3'])
})
})
@@ -197,18 +197,18 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'AssertNotEqualTest', filename: __dirname + '/examples_0/assert_notEqual_test.sol' },
- { type: 'testPass', value: 'Not equal uint pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
- { type: 'testFailure', value: 'Not equal uint fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualUintFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '288:63:0', expected: '1', returned: '1'},
- { type: 'testPass', value: 'Not equal int pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
- { type: 'testFailure', value: 'Not equal int fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualIntFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '525:52:0', expected: '-2', returned: '-2'},
- { type: 'testPass', value: 'Not equal bool pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
- { type: 'testFailure', value: 'Not equal bool fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBoolFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '760:57:0', expected: true, returned: true},
- { type: 'testPass', value: 'Not equal address pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
- { type: 'testFailure', value: 'Not equal address fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualAddressFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1084:136:0', expected: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9, returned: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9},
- { type: 'testPass', value: 'Not equal bytes32 pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
- { type: 'testFailure', value: 'Not equal bytes32 fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBytes32FailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1756:54:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6978000000000000000000000000000000000000000000000000000000'},
- { type: 'testPass', value: 'Not equal string pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
- { type: 'testFailure', value: 'Not equal string fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualStringFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '2026:81:0', expected: 'remix', returned: 'remix'},
+ { type: 'testPass', debugTxHash: '0xb0ac5cde13a5005dc1b4efbb66fb3a5d6f0697467aedd6165ed1c8ee552939bc', value: 'Not equal uint pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x69d25ed9b9a010e97e0f7282313d4018c77a8873fd5f2e0b12483701febdd6b4', value: 'Not equal uint fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualUintFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '288:63:0', expected: '1', returned: '1'},
+ { type: 'testPass', debugTxHash: '0x19a743cfb44b273c78b3271d603412d31087974309a70595bc5d15097a52c5c5', value: 'Not equal int pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x8dfce61b71a9ea65fb00c471370413779626f290b71d41f8be8408674e64f4d2', value: 'Not equal int fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualIntFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '525:52:0', expected: '-2', returned: '-2'},
+ { type: 'testPass', debugTxHash: '0x12bc9eb3a653ebe4c7ab954c144dab4848295c88d71d17cb86a41e8a004419ba', value: 'Not equal bool pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x901b9cd631f8f29841243a257d1914060b9c36d88ee7d8b624de76dad4a34c85', value: 'Not equal bool fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBoolFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '760:57:0', expected: true, returned: true},
+ { type: 'testPass', debugTxHash: '0xf9e48bac26d3a2871ceb92974b897cae61e60bbc4db115b7e8eff4ac0390e4a5', value: 'Not equal address pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x4e83a17426bc79b147ddd30a5434a2430a8302b3ce78b6979088ac5eceac5d3c', value: 'Not equal address fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualAddressFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1084:136:0', expected: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9, returned: 0x7994f14563F39875a2F934Ce42cAbF48a93FdDA9},
+ { type: 'testPass', debugTxHash: '0xfb4b30bb8373eeb6b09e38ad07880b86654f72780b41d7cf7554a112a04a0f53', value: 'Not equal bytes32 pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x43edf8bc68229415ee63f63f5303351a0dfecf9f3eb2ec35ffd2c10c60cf7005', value: 'Not equal bytes32 fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualBytes32FailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '1756:54:0', expected: '0x72656d6978000000000000000000000000000000000000000000000000000000', returned: '0x72656d6978000000000000000000000000000000000000000000000000000000'},
+ { type: 'testPass', debugTxHash: '0x712932edc040596e2b02ddc06a48b773f5fccc7346d55cefc5d1c52528ce3168', value: 'Not equal string pass test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', context: 'AssertNotEqualTest' },
+ { type: 'testFailure', debugTxHash: '0x961ce7425fdd4049aeb678ea20a0441eb837c1fe26b0d010593fc07d668321e6', value: 'Not equal string fail test', filename: __dirname + '/examples_0/assert_notEqual_test.sol', errMsg: 'notEqualStringFailTest fails', context: 'AssertNotEqualTest', assertMethod: 'notEqual', location: '2026:81:0', expected: 'remix', returned: 'remix'},
], ['time', 'web3'])
})
})
@@ -235,14 +235,14 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'AssertGreaterThanTest', filename: __dirname + '/examples_0/assert_greaterThan_test.sol' },
- { type: 'testPass', value: 'Greater than uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
- { type: 'testFailure', value: 'Greater than uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '303:69:0', expected: '4', returned: '1'},
- { type: 'testPass', value: 'Greater than int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
- { type: 'testFailure', value: 'Greater than int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '569:67:0', expected: '1', returned: '-1'},
- { type: 'testPass', value: 'Greater than uint int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
- { type: 'testFailure', value: 'Greater than uint int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '845:71:0', expected: '2', returned: '1'},
- { type: 'testPass', value: 'Greater than int uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
- { type: 'testFailure', value: 'Greater than int uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '1125:76:0', expected: '115792089237316195423570985008687907853269984665640564039457584007913129639836', returned: '100'}
+ { type: 'testPass', debugTxHash: '0x81cf46560b522280ac60bd5c8efedad4598957d33435a898c23eefb13ca6104f', value: 'Greater than uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
+ { type: 'testFailure', debugTxHash: '0x7090dc27ac06e1afd66963992bdd9188200d0404d43b95cfa5d925bbe6eba3ed', value: 'Greater than uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '303:69:0', expected: '4', returned: '1'},
+ { type: 'testPass', debugTxHash: '0xd57b40ed464763baf128f8a72efcd198e825e0b8f498cef90aed23045d0196db', value: 'Greater than int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
+ { type: 'testFailure', debugTxHash: '0x6c7b84bd8fc452b7839e11129d3818fa69dfd9b914e55556b39fdc68b70fc1b5', value: 'Greater than int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '569:67:0', expected: '1', returned: '-1'},
+ { type: 'testPass', debugTxHash: '0xc5883db70b83a1d3afff24a9f0555de3edd7776e5ec157cc2110e426e5be2594', value: 'Greater than uint int pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
+ { type: 'testFailure', debugTxHash: '0xa534085a6bbdcf73a68bdef6a1421218c11ac0ec1af398f9445defeea31cea6e', value: 'Greater than uint int fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanUintIntFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '845:71:0', expected: '2', returned: '1'},
+ { type: 'testPass', debugTxHash: '0x36a7139445d76f6072fab4cc0717461068276748622c0dfc3f092d548b197de8', value: 'Greater than int uint pass test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', context: 'AssertGreaterThanTest' },
+ { type: 'testFailure', debugTxHash: '0x7890f7b8f2eabae581b6f70d55d2d3bfa64ddd7753d5f892dbdf86ced96945fe', value: 'Greater than int uint fail test', filename: __dirname + '/examples_0/assert_greaterThan_test.sol', errMsg: 'greaterThanIntUintFailTest fails', context: 'AssertGreaterThanTest', assertMethod: 'greaterThan', location: '1125:76:0', expected: '115792089237316195423570985008687907853269984665640564039457584007913129639836', returned: '100'}
], ['time', 'web3'])
})
})
@@ -270,14 +270,14 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'AssertLesserThanTest', filename: __dirname + '/examples_0/assert_lesserThan_test.sol' },
- { type: 'testPass', value: 'Lesser than uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
- { type: 'testFailure', value: 'Lesser than uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '298:67:0', expected: '2', returned: '4'},
- { type: 'testPass', value: 'Lesser than int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
- { type: 'testFailure', value: 'Lesser than int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '557:65:0', expected: '-1', returned: '1'},
- { type: 'testPass', value: 'Lesser than uint int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
- { type: 'testFailure', value: 'Lesser than uint int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '826:71:0', expected: '-1', returned: '115792089237316195423570985008687907853269984665640564039457584007913129639935'},
- { type: 'testPass', value: 'Lesser than int uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
- { type: 'testFailure', value: 'Lesser than int uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '1105:69:0', expected: '1', returned: '1'},
+ { type: 'testPass', debugTxHash: '0x47875047c1fff8a7b1cc1603418960cd2a3afe8a9c59337b19fb463a85d6e47e', value: 'Lesser than uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
+ { type: 'testFailure', debugTxHash: '0xf6fd459d0b28d0d85c56dd69d953331291e1c234c8a263150252e35da0ed6671', value: 'Lesser than uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '298:67:0', expected: '2', returned: '4'},
+ { type: 'testPass', debugTxHash: '0x761d95111764af396634474899ff1db218d5e514d6de6bc3260af15b1f5b929f', value: 'Lesser than int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
+ { type: 'testFailure', debugTxHash: '0xc17697ef2df92c22707639caa9355bdf0d98dbb18157e72b8b257bb0eb2beb4e', value: 'Lesser than int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '557:65:0', expected: '-1', returned: '1'},
+ { type: 'testPass', debugTxHash: '0xf0721b28c547c1c64948661d677cf6afc10d139315726280162a984f2f7e5d9c', value: 'Lesser than uint int pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
+ { type: 'testFailure', debugTxHash: '0x0757289229b58043c101cb311df8db16d1b30944747493e1491daa9aca6aa30e', value: 'Lesser than uint int fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanUintIntFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '826:71:0', expected: '-1', returned: '115792089237316195423570985008687907853269984665640564039457584007913129639935'},
+ { type: 'testPass', debugTxHash: '0x316feb8f80c04b12194262dd80b6b004232eab00d7f7c3846badf6e92684e177', value: 'Lesser than int uint pass test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', context: 'AssertLesserThanTest' },
+ { type: 'testFailure', debugTxHash: '0x65c5ab3cb85f163eefe3321cc4444defa99154d3cbe415b9384bbd2627449b6a', value: 'Lesser than int uint fail test', filename: __dirname + '/examples_0/assert_lesserThan_test.sol', errMsg: 'lesserThanIntUintFailTest fails', context: 'AssertLesserThanTest', assertMethod: 'lesserThan', location: '1105:69:0', expected: '1', returned: '1'},
], ['time', 'web3'])
})
})
@@ -305,10 +305,10 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'MyTest', filename: __dirname + '/examples_1/simple_storage_test.sol' },
- { type: 'testPass', value: 'Initial value should be100', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' },
- { type: 'testPass', value: 'Initial value should not be200', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' },
- { type: 'testFailure', value: 'Should trigger one fail', filename: __dirname + '/examples_1/simple_storage_test.sol', errMsg: 'uint test 1 fails', context: 'MyTest', assertMethod: 'equal', location: '532:51:1', expected: '2', returned: '1'},
- { type: 'testPass', value: 'Should trigger one pass', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }
+ { type: 'testPass', debugTxHash: '0x5a805403a12f0431c5dd190d31a87eb62758f09dddc0c6ee7ee6899c5e7eba71', value: 'Initial value should be100', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' },
+ { type: 'testPass', debugTxHash: '0xd0ae7cb5a3a0f5e8f7bf90129e3daba36f649a5c1176ad54609f7b7615bef7dd', value: 'Initial value should not be200', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' },
+ { type: 'testFailure', debugTxHash: '0xb0d613434f2fd7060f97d4ca8bbfd8f2deeebed83062a25044f0237bd38b3229', value: 'Should trigger one fail', filename: __dirname + '/examples_1/simple_storage_test.sol', errMsg: 'uint test 1 fails', context: 'MyTest', assertMethod: 'equal', location: '532:51:1', expected: '2', returned: '1'},
+ { type: 'testPass', debugTxHash: '0x5ee675ec81b550386b2fdd359ae3530e49dd3e02145e877a4a5f68753ac4e341', value: 'Should trigger one pass', filename: __dirname + '/examples_1/simple_storage_test.sol', context: 'MyTest' }
], ['time', 'web3'])
})
})
@@ -336,8 +336,8 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'MyTest', filename: __dirname + '/examples_2/simple_storage_test.sol' },
- { type: 'testPass', value: 'Initial value should be100', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' },
- { type: 'testPass', value: 'Value is set200', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' }
+ { type: 'testPass', debugTxHash: '0xa700d29204f1ddb40ef66f151c44387f905d405b6da10380111a751451af2fe1', value: 'Initial value should be100', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' },
+ { type: 'testPass', debugTxHash: '0x2c037b78a435e5964615f838ea65f077f3b15d8552d514b3551d0fb87419e444', value: 'Value is set200', filename: __dirname + '/examples_2/simple_storage_test.sol', context: 'MyTest' }
], ['time', 'web3'])
})
})
@@ -362,8 +362,8 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'StringTest', filename: __dirname + '/examples_3/simple_string_test.sol' },
- { type: 'testPass', value: 'Initial value should be hello world', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' },
- { type: 'testPass', value: 'Value should not be hello wordl', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' }
+ { type: 'testPass', debugTxHash: '0x4e160dbc81f88d3d87b39d81651c42b0ea8e3aaa10c1a57394467e073bbcb2a4', value: 'Initial value should be hello world', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' },
+ { type: 'testPass', debugTxHash: '0x47030578c5bcb990d837356430697d061a02813e3322fa3323f6b5f78176eea6', value: 'Value should not be hello wordl', filename: __dirname + '/examples_3/simple_string_test.sol', context: 'StringTest' }
], ['time', 'web3'])
})
})
@@ -388,9 +388,9 @@ describe('testRunner', () => {
deepEqualExcluding(tests, [
{ type: 'accountList', value: accounts },
{ type: 'contract', value: 'StorageResolveTest', filename: __dirname + '/examples_5/test/simple_storage_test.sol' },
- { type: 'testPass', value: 'Initial value should be100', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' },
- { type: 'testPass', value: 'Check if even', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' },
- { type: 'testPass', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }
+ { type: 'testPass', debugTxHash: '0x85e901e9160c4a17725d020f030c7cbb020d36da1fda8422d990391df3cbfcbb', value: 'Initial value should be100', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' },
+ { type: 'testPass', debugTxHash: '0x1abb2456746b416cddcaf2f3fe960103e740e9772c47a0f1d65d48394facb21a', value: 'Check if even', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' },
+ { type: 'testPass', debugTxHash: '0xfcc2332a24d2780390e85a06343fab81c4dc20c12cf5455d746641a9c3e8db03', value: 'Check if odd', filename: __dirname + '/examples_5/test/simple_storage_test.sol', context: 'StorageResolveTest' }
], ['time', 'web3'])
})
})
diff --git a/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts b/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
index 17e58a0052..bc43ed73b1 100644
--- a/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
+++ b/libs/remix-ui/solidity-compiler/src/lib/actions/compiler.ts
@@ -42,7 +42,6 @@ export const listenToEvents = (compileTabLogic: CompileTabLogic, api) => (dispat
api.onContentChanged = () => {
dispatch(setEditorMode('contentChanged'))
}
-
compileTabLogic.compiler.event.register('loadingCompiler', () => {
dispatch(setCompilerMode('loadingCompiler'))
})
diff --git a/libs/remix-ui/terminal/.babelrc b/libs/remix-ui/terminal/.babelrc
new file mode 100644
index 0000000000..09d67939cc
--- /dev/null
+++ b/libs/remix-ui/terminal/.babelrc
@@ -0,0 +1,4 @@
+{
+ "presets": ["@nrwl/react/babel"],
+ "plugins": []
+}
diff --git a/libs/remix-ui/terminal/.eslintrc b/libs/remix-ui/terminal/.eslintrc
new file mode 100644
index 0000000000..dae5c6feeb
--- /dev/null
+++ b/libs/remix-ui/terminal/.eslintrc
@@ -0,0 +1,19 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true
+ },
+ "extends": "../../../.eslintrc",
+ "globals": {
+ "Atomics": "readonly",
+ "SharedArrayBuffer": "readonly"
+ },
+ "parserOptions": {
+ "ecmaVersion": 11,
+ "sourceType": "module"
+ },
+ "rules": {
+ "no-unused-vars": "off",
+ "@typescript-eslint/no-unused-vars": "error"
+ }
+}
diff --git a/libs/remix-ui/terminal/README.md b/libs/remix-ui/terminal/README.md
new file mode 100644
index 0000000000..4638096417
--- /dev/null
+++ b/libs/remix-ui/terminal/README.md
@@ -0,0 +1,7 @@
+# remix-ui-terminal
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test remix-ui-terminal` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/remix-ui/terminal/package.json b/libs/remix-ui/terminal/package.json
new file mode 100644
index 0000000000..623cf89c25
--- /dev/null
+++ b/libs/remix-ui/terminal/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "remix-ui-terminal",
+ "version": "0.0.1"
+}
diff --git a/libs/remix-ui/terminal/src/index.ts b/libs/remix-ui/terminal/src/index.ts
new file mode 100644
index 0000000000..5b8eeca5b5
--- /dev/null
+++ b/libs/remix-ui/terminal/src/index.ts
@@ -0,0 +1 @@
+export * from './lib/remix-ui-terminal'
diff --git a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
new file mode 100644
index 0000000000..dafe6f78cf
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
@@ -0,0 +1,152 @@
+import React from 'react'
+import { EMPTY_BLOCK, KNOWN_TRANSACTION, NEW_BLOCK, NEW_CALL, NEW_TRANSACTION, UNKNOWN_TRANSACTION } from '../types/terminalTypes'
+
+export const registerCommandAction = (name: string, command, activate, dispatch: React.Dispatch
) => {
+ const commands: any = {}
+ const _commands: any = {}
+ _commands[name] = command
+ const data: any = {
+ session: [],
+ activeFilters: { commands: {}, input: '' },
+ filterFns: {}
+ }
+ const _INDEX = {
+ all: [],
+ allMain: [],
+ commands: {},
+ commandsMain: {}
+ }
+
+ const registerFilter = (commandName, filterFn) => {
+ data.filterFns[commandName] = filterFn
+ }
+
+ commands[name] = function () {
+ const args = [...arguments]
+ const steps = []
+ const root = { steps, cmd: name, gidx: 0, idx: 0 }
+ const ITEM = { root, cmd: name }
+ root.gidx = _INDEX.allMain.push(ITEM) - 1
+ let item
+ function append (cmd, params, el) {
+ if (cmd) { // subcommand
+ item = { el, cmd, root }
+ } else { // command
+ item = ITEM
+ item.el = el
+ cmd = name
+ }
+ item.gidx = _INDEX.all.push(item) - 1
+ item.idx = _INDEX.commands[cmd].push(item) - 1
+ item.step = steps.push(item) - 1
+ item.args = params
+ }
+ const scopedCommands = _scopeCommands(append)
+ command(args, scopedCommands, el => append(null, args, el))
+ }
+ const help = typeof command.help === 'string' ? command.help : [
+ '// no help available for:', `terminal.command.${name}`
+ ].join('\n')
+ commands[name].toString = () => { return help }
+ commands[name].help = help
+ data.activeFilters.commands[name] = activate && activate.activate
+ if (activate.filterFn) {
+ registerFilter(name, activate.filterFn)
+ }
+ if (name !== (KNOWN_TRANSACTION || UNKNOWN_TRANSACTION || EMPTY_BLOCK)) {
+ dispatch({ type: name, payload: { commands: commands, _commands: _commands, data: data } })
+ }
+
+ const _scopeCommands = (append) => {
+ const scopedCommands = {}
+ Object.keys(commands).forEach(function makeScopedCommand (cmd) {
+ const command = _commands[cmd]
+ scopedCommands[cmd] = function _command () {
+ const args = [...arguments]
+ command(args, scopedCommands, el => append(cmd, args, el))
+ }
+ })
+ return scopedCommands
+ }
+}
+
+export const filterFnAction = (name: string, filterFn, dispatch: React.Dispatch) => {
+ const data: any = {
+ filterFns: {}
+ }
+ data.filterFns[name] = filterFn
+ dispatch({ type: name, payload: { data: data } })
+}
+
+export const registerLogScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch) => {
+ on('scriptRunner', commandName, (msg) => {
+ commandFn.log.apply(commandFn, msg.data)
+ dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
+ })
+}
+
+export const registerInfoScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch) => {
+ on('scriptRunner', commandName, (msg) => {
+ commandFn.info.apply(commandFn, msg.data)
+ dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
+ })
+}
+
+export const registerWarnScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch) => {
+ on('scriptRunner', commandName, (msg) => {
+ commandFn.warn.apply(commandFn, msg.data)
+ dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
+ })
+}
+
+export const registerErrorScriptRunnerAction = (on, commandName, commandFn, dispatch: React.Dispatch) => {
+ on('scriptRunner', commandName, (msg) => {
+ commandFn.error.apply(commandFn, msg.data)
+ dispatch({ type: commandName, payload: { commandFn, message: msg.data } })
+ })
+}
+
+export const listenOnNetworkAction = async (event, isListening) => {
+ event.trigger('listenOnNetWork', [isListening])
+}
+
+export const initListeningOnNetwork = (plugins, dispatch: React.Dispatch) => {
+ plugins.txListener.event.register(NEW_BLOCK, (block) => {
+ if (!block.transactions || (block.transactions && !block.transactions.length)) {
+ dispatch({ type: EMPTY_BLOCK, payload: { message: 0 } })
+ }
+ })
+ plugins.txListener.event.register(KNOWN_TRANSACTION, () => {
+ })
+ plugins.txListener.event.register(NEW_CALL, (tx, receipt) => {
+ log(plugins, tx, receipt, dispatch)
+ // log(this, tx, null)
+ })
+ plugins.txListener.event.register(NEW_TRANSACTION, (tx, receipt) => {
+ log(plugins, tx, receipt, dispatch)
+ })
+
+ const log = async (plugins, tx, receipt, dispatch: React.Dispatch) => {
+ const resolvedTransaction = await plugins.txListener.resolvedTransaction(tx.hash)
+ if (resolvedTransaction) {
+ let compiledContracts = null
+ if (plugins._deps.compilersArtefacts.__last) {
+ compiledContracts = await plugins._deps.compilersArtefacts.__last.getContracts()
+ }
+ 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 }] } })
+ }
+ })
+ } else {
+ await dispatch({ type: UNKNOWN_TRANSACTION, payload: { message: [{ tx: tx, receipt: receipt }] } })
+ }
+ }
+
+ plugins.txListener.event.register('debuggingRequested', async (hash) => {
+ // TODO should probably be in the run module
+ if (!await plugins.options.appManager.isActive('debugger')) await plugins.options.appManager.activatePlugin('debugger')
+ plugins.call('menuicons', 'select', 'debugger')
+ plugins.call('debugger', 'debug', hash)
+ })
+}
diff --git a/libs/remix-ui/terminal/src/lib/commands.ts b/libs/remix-ui/terminal/src/lib/commands.ts
new file mode 100644
index 0000000000..cc3b5e6963
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/commands.ts
@@ -0,0 +1,53 @@
+export const allPrograms = [
+ { ethers: 'The ethers.js library is a compact and complete JavaScript library for Ethereum.' },
+ { remix: 'Ethereum IDE and tools for the web.' },
+ { web3: 'The web3.js library is a collection of modules which contain specific functionality for the ethereum ecosystem.' }
+ // { swarmgw: 'This library can be used to upload/download files to Swarm via https://swarm-gateways.net/.' }
+]
+
+export const allCommands = [
+ { 'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.' },
+ { 'remix.exeCurrent()': 'Run the script currently displayed in the editor.' },
+ // { 'remix.help()': 'Display this help message.' },
+ { 'remix.loadgist(id)': 'Load a gist in the file explorer.' },
+ // { 'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm or ipfs.' },
+
+ // { 'swarmgw.get(url, cb)': 'Download files from Swarm via https://swarm-gateways.net/' },
+ // { 'swarmgw.put(content, cb)': 'Upload files to Swarm via https://swarm-gateways.net/' },
+
+ { 'ethers.Contract': 'This API provides a graceful connection to a contract deployed on the blockchain, simplifying calling and querying its functions and handling all the binary protocol and conversion as necessarily.' },
+ // { 'ethers.HDNode': 'A Hierarchical Deterministic Wallet represents a large tree of private keys which can reliably be reproduced from an initial seed.' },
+ // { 'ethers.Interface': 'The Interface Object is a meta-class that accepts a Solidity (or compatible) Application Binary Interface (ABI) and populates functions to deal with encoding and decoding the parameters to pass in and results returned.' },
+ { 'ethers.providers': 'A Provider abstracts a connection to the Ethereum blockchain, for issuing queries and sending state changing transactions.' },
+ // { 'ethers.SigningKey': 'The SigningKey interface provides an abstraction around the secp256k1 elliptic curve cryptography library.' },
+ // { 'ethers.utils': 'The utility functions exposed in both the ethers umbrella package and the ethers-utils.' },
+ // { 'ethers.utils.AbiCoder': 'Create a new ABI Coder object' },
+ // { 'ethers.utils.RLP': 'This encoding method is used internally for several aspects of Ethereum, such as encoding transactions and determining contract addresses.' },
+ { 'ethers.Wallet': 'A wallet manages a private/public key pair which is used to cryptographically sign transactions and prove ownership on the Ethereum network.' },
+ { 'ethers.version': 'Contains the version of the ethers container object.' },
+
+ { 'web3.eth': 'Eth module for interacting with the Ethereum network.' },
+ { 'web3.eth.accounts': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.' },
+ // TODO: need to break down the object return from abi response
+ // { 'web3.eth.abi': 'The web3.eth.abi functions let you de- and encode parameters to ABI (Application Binary Interface) for function calls to the EVM (Ethereum Virtual Machine).' },
+ { 'web3.eth.ens': 'The web3.eth.ens functions let you interacting with ENS.' },
+ { 'web3.eth.Iban': 'The web3.eth.Iban function lets convert Ethereum addresses from and to IBAN and BBAN.' },
+ { 'web3.eth.net': 'Net module for interacting with network properties.' },
+ { 'web3.eth.personal': 'Personal module for interacting with the Ethereum accounts.' },
+ { 'web3.eth.subscribe': 'The web3.eth.subscribe function lets you subscribe to specific events in the blockchain.' },
+ { 'web3.givenProvider': 'When using web3.js in an Ethereum compatible browser, it will set with the current native provider by that browser. Will return the given provider by the (browser) environment, otherwise null.' },
+ // { 'web3.modules': 'Contains the version of the web3 container object.' },
+ { 'web3.providers': 'Contains the current available providers.' },
+ { 'web3.shh': 'Shh module for interacting with the whisper protocol' },
+ { 'web3.utils': 'This package provides utility functions for Ethereum dapps and other web3.js packages.' },
+ { 'web3.version': 'Contains the version of the web3 container object.' },
+
+ { 'web3.eth.clearSubscriptions();': 'Resets subscriptions.' }
+// { 'web3.eth.Contract(jsonInterface[, address][, options])': 'The web3.eth.Contract object makes it easy to interact with smart contracts on the ethereum blockchain.' },
+// { 'web3.eth.accounts.create([entropy]);': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.' },
+// { 'web3.eth.getAccounts();': 'Retrieve the list of accounts' },
+// { 'web3.eth.accounts.privateKeyToAccount(privateKey [, ignoreLength ]);': 'Get the account from the private key' },
+// { 'web3.eth.accounts.signTransaction(tx, privateKey [, callback]);': 'Sign Transaction' },
+// { 'web3.eth.accounts.recoverTransaction(rawTransaction);': 'Sign Transaction' },
+// { 'web3.eth.accounts.hashMessage(message);': 'Hash message' }
+]
diff --git a/libs/remix-ui/terminal/src/lib/components/ChechTxStatus.tsx b/libs/remix-ui/terminal/src/lib/components/ChechTxStatus.tsx
new file mode 100644
index 0000000000..005be81e02
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/components/ChechTxStatus.tsx
@@ -0,0 +1,16 @@
+import React from 'react' // eslint-disable-line
+
+const CheckTxStatus = ({ tx, type }) => {
+ if (tx.status === '0x1' || tx.status === true) {
+ return ( )
+ }
+ if (type === 'call' || type === 'unknownCall' || type === 'unknown') {
+ return (call )
+ } else if (tx.status === '0x0' || tx.status === false) {
+ return ( )
+ } else {
+ return ( )
+ }
+}
+
+export default CheckTxStatus
diff --git a/libs/remix-ui/terminal/src/lib/components/Context.tsx b/libs/remix-ui/terminal/src/lib/components/Context.tsx
new file mode 100644
index 0000000000..a354c4cab4
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/components/Context.tsx
@@ -0,0 +1,62 @@
+import React from 'react' // eslint-disable-line
+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 data = opts.tx || ''
+ const from = opts.from ? helper.shortenHexData(opts.from) : ''
+ let to = opts.to
+ if (data.to) to = to + ' ' + helper.shortenHexData(data.to)
+ const val = data.value
+ let hash = data.hash ? helper.shortenHexData(data.hash) : ''
+ const input = data.input ? helper.shortenHexData(data.input) : ''
+ const logs = data.logs && data.logs.decoded && data.logs.decoded.length ? data.logs.decoded.length : 0
+ 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') {
+ return (
+
+
+ [vm]
+ from: {from}
+ to: {to}
+ value: {value} wei
+ data: {input}
+ logs: {logs}
+ hash: {hash}
+
+
)
+ } else if (blockchain.getProvider() !== 'vm' && data.resolvedData) {
+ return (
+
+
+ [block:{block} txIndex:{i}]
+ from: {from}
+ to: {to}
+ value: {value} wei
+ data: {input}
+ logs: {logs}
+ hash: {hash}
+
+
)
+ } else {
+ hash = helper.shortenHexData(data.blockHash)
+ return (
+
+
+ [block:{block} txIndex:{i}]
+ from: {from}
+ to: {to}
+ value: {value} wei
+ data: {input}
+ logs: {logs}
+ hash: {hash}
+
+
)
+ }
+}
+
+export default Context
diff --git a/libs/remix-ui/terminal/src/lib/components/RenderCall.tsx b/libs/remix-ui/terminal/src/lib/components/RenderCall.tsx
new file mode 100644
index 0000000000..3e94f1e3aa
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/components/RenderCall.tsx
@@ -0,0 +1,61 @@
+import React, { useState } from 'react' // eslint-disable-line
+
+import helper from 'apps/remix-ide/src/lib/helper'
+import CheckTxStatus from './ChechTxStatus' // eslint-disable-line
+import showTable from './Table'
+import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
+
+const remixLib = require('@remix-project/remix-lib')
+const typeConversion = remixLib.execution.typeConversion
+
+const RenderCall = ({ tx, resolvedData, logs, index, plugin, showTableHash, txDetails, modal }) => {
+ const to = resolvedData.contractName + '.' + resolvedData.fn
+ const from = tx.from ? tx.from : ' - '
+ const input = tx.input ? helper.shortenHexData(tx.input) : ''
+ const txType = 'call'
+
+ const debug = (event, tx) => {
+ event.stopPropagation()
+ if (tx.isCall && tx.envMode !== 'vm') {
+ modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
+ } else {
+ plugin.event.trigger('debuggingRequested', [tx.hash])
+ }
+ }
+
+ return (
+
+ txDetails(event, tx)}>
+
+
+ [call]
+ from: {from}
+ to: {to}
+ data: {input}
+
+
+
debug(event, tx)}>Debug
+
+
+
+ {showTableHash.includes(tx.hash) ? showTable({
+ hash: tx.hash,
+ isCall: tx.isCall,
+ contractAddress: tx.contractAddress,
+ data: tx,
+ from,
+ to,
+ gas: tx.gas,
+ input: tx.input,
+ 'decoded input': resolvedData && resolvedData.params ? JSON.stringify(typeConversion.stringify(resolvedData.params), null, '\t') : ' - ',
+ 'decoded output': resolvedData && resolvedData.decodedReturnValue ? JSON.stringify(typeConversion.stringify(resolvedData.decodedReturnValue), null, '\t') : ' - ',
+ val: tx.value,
+ logs: logs,
+ transactionCost: tx.transactionCost,
+ executionCost: tx.executionCost
+ }, showTableHash) : null}
+
+ )
+}
+
+export default RenderCall
diff --git a/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx b/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx
new file mode 100644
index 0000000000..79e1ba4c8a
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx
@@ -0,0 +1,56 @@
+
+import React, { useState } from 'react' // eslint-disable-line
+import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
+import CheckTxStatus from './ChechTxStatus' // eslint-disable-line
+import Context from './Context' // eslint-disable-line
+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 debug = (event, tx) => {
+ event.stopPropagation()
+ if (tx.isCall && tx.envMode !== 'vm') {
+ modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
+ } else {
+ plugin.event.trigger('debuggingRequested', [tx.hash])
+ }
+ }
+
+ const from = tx.from
+ const to = resolvedData.contractName + '.' + resolvedData.fn
+ const txType = 'knownTx'
+ const options = { from, to, tx }
+ return (
+
+ txDetails(event, tx)}>
+
+
+
+
debug(event, tx)}>Debug
+
+
+
+ {showTableHash.includes(tx.hash) ? showTable({
+ hash: tx.hash,
+ status: receipt !== null ? receipt.status : null,
+ isCall: tx.isCall,
+ contractAddress: tx.contractAddress,
+ data: tx,
+ from,
+ to,
+ gas: tx.gas,
+ input: tx.input,
+ 'decoded input': resolvedData && resolvedData.params ? JSON.stringify(typeConversion.stringify(resolvedData.params), null, '\t') : ' - ',
+ 'decoded output': resolvedData && resolvedData.decodedReturnValue ? JSON.stringify(typeConversion.stringify(resolvedData.decodedReturnValue), null, '\t') : ' - ',
+ logs: logs,
+ val: tx.value,
+ transactionCost: tx.transactionCost,
+ executionCost: tx.executionCost
+ }, showTableHash) : null}
+
+ )
+}
+
+export default RenderKnownTransactions
diff --git a/libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx b/libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx
new file mode 100644
index 0000000000..0a8d43835b
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx
@@ -0,0 +1,50 @@
+import React, { useState } from 'react' // eslint-disable-line
+import { ModalDialog } from '@remix-ui/modal-dialog'// eslint-disable-line
+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 debug = (event, tx) => {
+ event.stopPropagation()
+ if (tx.isCall && tx.envMode !== 'vm') {
+ modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
+ } else {
+ plugin.event.trigger('debuggingRequested', [tx.hash])
+ }
+ }
+
+ const from = tx.from
+ const to = tx.to
+ const txType = 'unknown' + (tx.isCall ? 'Call' : 'Tx')
+ const options = { from, to, tx }
+ return (
+
+ txDetails(event, tx)}>
+
+
+
+
debug(event, tx)}>Debug
+
+
+
+ {showTableHash.includes(tx.hash) ? showTable({
+ hash: tx.hash,
+ status: receipt !== null ? receipt.status : null,
+ isCall: tx.isCall,
+ contractAddress: tx.contractAddress,
+ data: tx,
+ from,
+ to,
+ gas: tx.gas,
+ input: tx.input,
+ 'decoded output': ' - ',
+ val: tx.value,
+ transactionCost: tx.transactionCost,
+ executionCost: tx.executionCost
+ }, showTableHash) : null}
+
+ )
+}
+
+export default RenderUnKnownTransactions
diff --git a/libs/remix-ui/terminal/src/lib/components/Table.tsx b/libs/remix-ui/terminal/src/lib/components/Table.tsx
new file mode 100644
index 0000000000..33e9cabf58
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/components/Table.tsx
@@ -0,0 +1,165 @@
+import React, { useState } from 'react' // eslint-disable-line
+import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
+import helper from 'apps/remix-ide/src/lib/helper'
+
+const remixLib = require('@remix-project/remix-lib')
+const typeConversion = remixLib.execution.typeConversion
+
+const showTable = (opts, showTableHash) => {
+ let msg = ''
+ let toHash
+ const data = opts.data // opts.data = data.tx
+ if (data.to) {
+ toHash = opts.to + ' ' + data.to
+ } else {
+ toHash = opts.to
+ }
+ let callWarning = ''
+ if (opts.isCall) {
+ callWarning = '(Cost only applies when called by a contract)'
+ }
+ if (!opts.isCall) {
+ if (opts.status !== undefined && opts.status !== null) {
+ if (opts.status === '0x0' || opts.status === false) {
+ msg = 'Transaction mined but execution failed'
+ } else if (opts.status === '0x1' || opts.status === true) {
+ msg = 'Transaction mined and execution succeed'
+ }
+ } else {
+ msg = 'Status not available at the moment'
+ }
+ }
+
+ let stringified = ' - '
+ if (opts.logs && opts.logs.decoded) {
+ stringified = typeConversion.stringify(opts.logs.decoded)
+ }
+ const val = opts.val != null ? typeConversion.toInt(opts.val) : 0
+ return (
+
+
+
+ status
+ {`${opts.status} ${msg}`}
+
+ {opts.hash ? (
+ transaction hash
+ {opts.hash}
+
+
+ ) : null }
+ {
+ opts.contractAddress ? (
+
+ contract address
+ {opts.contractAddress}
+
+
+
+ ) : null
+ }
+ {
+ opts.from ? (
+
+ from
+ {opts.from}
+
+
+
+ ) : null
+ }
+ {
+ opts.to ? (
+
+ to
+ {toHash}
+
+
+
+ ) : null
+ }
+ {
+ opts.gas ? (
+
+ gas
+ {opts.gas} gas
+
+
+
+ ) : null
+ }
+ {
+ opts.transactionCost ? (
+
+ transaction cost
+ {opts.transactionCost} gas {callWarning}
+
+
+
+ ) : null
+ }
+ {
+ opts.executionCost ? (
+
+ execution cost
+ {opts.executionCost} gas {callWarning}
+
+
+
+ ) : null
+ }
+ {opts.hash ? (
+
+ hash
+ {opts.hash}
+
+
+
+ ) : null}
+ {opts.input ? (
+
+ input
+ {helper.shortenHexData(opts.input)}
+
+
+
+ ) : null}
+ {opts['decoded input'] ? (
+
+ decoded input
+ {opts['decoded input'].trim()}
+
+
+
+ ) : null}
+ {opts['decoded output'] ? (
+
+ decoded output
+ {opts['decoded output']}
+
+
+
+ ) : null}
+ {opts.logs ? (
+
+ logs
+
+ {JSON.stringify(stringified, null, '\t')}
+
+
+
+
+ ) : null}
+ {opts.val ? (
+
+ val
+ {val} wei
+
+
+
+ ) : null}
+
+
+ )
+}
+export default showTable
diff --git a/libs/remix-ui/terminal/src/lib/custom-hooks/useKeyPress.tsx b/libs/remix-ui/terminal/src/lib/custom-hooks/useKeyPress.tsx
new file mode 100644
index 0000000000..12bbc1cefc
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/custom-hooks/useKeyPress.tsx
@@ -0,0 +1,29 @@
+import React, {useEffect, useState} from "react" // eslint-disable-line
+
+export const useKeyPress = (targetKey: string): boolean => {
+// State for keeping track of whether key is pressed
+ const [keyPressed, setKeyPressed] = useState(false)
+ // If pressed key is our target key then set to true
+ function downHandler ({ key }): void {
+ if (key === targetKey) {
+ setKeyPressed(true)
+ }
+ }
+ // If released key is our target key then set to false
+ const upHandler = ({ key }): void => {
+ if (key === targetKey) {
+ setKeyPressed(false)
+ }
+ }
+ // Add event listeners
+ useEffect(() => {
+ window.addEventListener('keydown', downHandler)
+ window.addEventListener('keyup', upHandler)
+ // Remove event listeners on cleanup
+ return () => {
+ window.removeEventListener('keydown', downHandler)
+ window.removeEventListener('keyup', upHandler)
+ }
+ }, []) // Empty array ensures that effect is only run on mount and unmount
+ return keyPressed
+}
diff --git a/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
new file mode 100644
index 0000000000..7568737eae
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
@@ -0,0 +1,195 @@
+import { CLEAR_CONSOLE, CMD_HISTORY, EMPTY_BLOCK, ERROR, HTML, INFO, KNOWN_TRANSACTION, LISTEN_ON_NETWORK, LOG, NEW_TRANSACTION, SCRIPT, UNKNOWN_TRANSACTION, WARN } from '../types/terminalTypes'
+
+export const initialState = {
+ journalBlocks: [
+ ],
+ data: {
+ // lineLength: props.options.lineLength || 80,
+ session: [],
+ activeFilters: { commands: {}, input: '' },
+ filterFns: {}
+ },
+ _commandHistory: [],
+ _commands: {},
+ commands: {},
+ _JOURNAL: [],
+ _jobs: [],
+ _INDEX: {
+ },
+ _INDEXall: [],
+ _INDEXallMain: [],
+ _INDEXcommands: {},
+ _INDEXcommandsMain: {},
+ message: []
+}
+
+export const registerCommandReducer = (state, action) => {
+ switch (action.type) {
+ case HTML :
+ return {
+ ...state,
+ _commands: Object.assign(initialState._commands, action.payload._commands),
+ commands: Object.assign(initialState.commands, action.payload.commands),
+ data: Object.assign(initialState.data, { ...action.payload.data })
+ }
+ case LOG:
+ return {
+ ...state,
+ _commands: Object.assign(initialState._commands, action.payload._commands),
+ commands: Object.assign(initialState.commands, action.payload.commands),
+ data: Object.assign(initialState.data, { ...action.payload.data })
+
+ }
+ case INFO:
+ return {
+ ...state,
+ _commands: Object.assign(initialState._commands, action.payload._commands),
+ commands: Object.assign(initialState.commands, action.payload.commands),
+ data: Object.assign(initialState.data, action.payload.data)
+ }
+ case WARN:
+ return {
+ ...state,
+ _commands: Object.assign(initialState._commands, action.payload._commands),
+ commands: Object.assign(initialState.commands, action.payload.commands),
+ data: Object.assign(initialState.data, action.payload.data)
+ }
+ case ERROR:
+ return {
+ ...state,
+ _commands: Object.assign(initialState._commands, action.payload._commands),
+ commands: Object.assign(initialState.commands, action.payload.commands),
+ data: Object.assign(initialState.data, action.payload.data)
+ }
+ case SCRIPT:
+ return {
+ ...state,
+ _commands: Object.assign(initialState._commands, action.payload._commands),
+ commands: Object.assign(initialState.commands, action.payload.commands),
+ data: Object.assign(initialState.data, action.payload.data)
+ }
+ case CLEAR_CONSOLE:
+ return {
+ ...state,
+ ...state.journalBlocks.splice(0)
+ }
+ case LISTEN_ON_NETWORK:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' })
+ }
+ default :
+ return { state }
+ }
+}
+
+export const registerFilterReducer = (state, action) => {
+ switch (action.type) {
+ case LOG:
+ return {
+ ...state,
+ data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
+
+ }
+ case INFO:
+ return {
+ ...state,
+ data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
+ }
+ case WARN:
+ return {
+ ...state,
+ data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
+ }
+ case ERROR:
+ return {
+ ...state,
+ data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
+ }
+ case SCRIPT:
+ return {
+ ...state,
+ data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
+ }
+ default :
+ return { state }
+ }
+}
+
+export const addCommandHistoryReducer = (state, action) => {
+ switch (action.type) {
+ case CMD_HISTORY:
+ return {
+ ...state,
+ _commandHistory: initialState._commandHistory.unshift(action.payload.script)
+
+ }
+ default :
+ return { state }
+ }
+}
+
+export const remixWelcomeTextReducer = (state, action) => {
+ switch (action.type) {
+ case 'welcomeText' :
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push(action.payload.welcomeText)
+ }
+ }
+}
+
+export const registerScriptRunnerReducer = (state, action) => {
+ switch (action.type) {
+ case HTML:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' })
+ }
+ case LOG:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' })
+ }
+ case INFO:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' })
+ }
+ case WARN:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-warning' })
+ }
+ case ERROR:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-danger' })
+ }
+ case SCRIPT:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' })
+ }
+ case KNOWN_TRANSACTION:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction' })
+ }
+ case UNKNOWN_TRANSACTION:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'unknownTransaction' })
+ }
+ case EMPTY_BLOCK:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'emptyBlock' })
+ }
+ case NEW_TRANSACTION:
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '' })
+ }
+ }
+}
diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
new file mode 100644
index 0000000000..6c3d777539
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
@@ -0,0 +1,421 @@
+
+element.style {
+ height: 323px !important;
+}
+#terminalCliInput{
+ width: 95%;
+ background: transparent;
+ border: none;
+ font-weight: bold;
+ color: #a2a3b4;
+ border-top-style: hidden;
+
+
+
+
+ border-right-style: hidden;
+ border-left-style: hidden;
+ border-bottom-style: hidden;
+}
+#terminalCliInput:focus {
+ outline: none;
+}
+
+.border-primary {
+ border-color: #007aa6!important;
+}
+
+/* seleted option should reflect the theme color */
+.selectedOptions {
+ background-color: #222336;
+}
+
+.panel {
+ position : relative;
+ display : flex;
+ flex-direction : column;
+ font-size : 12px;
+ min-height : 3em;
+ }
+ .bar {
+ display : flex;
+ z-index : 2;
+ }
+ .menu {
+ position : relative;
+ display : flex;
+ align-items : center;
+ width : 100%;
+ max-height : 35px;
+ min-height : 35px;
+ }
+ .toggleTerminal {
+ cursor : pointer;
+ }
+ .toggleTerminal:hover {
+ color : var(--secondary);
+ }
+ .terminal_container {
+ display : flex;
+ flex-direction : column;
+ height : 100%;
+ overflow-y : auto;
+ font-family : monospace;
+ margin : 0px;
+ background-repeat : no-repeat;
+ background-position : center 15%;
+ background-size : auto calc(75% - 1.7em);
+ }
+ .terminal {
+ position : relative;
+ display : flex;
+ flex-direction : column;
+ height : 100%;
+ }
+ .journal {
+ margin-top : auto;
+ font-family : monospace;
+ }
+ .block {
+ word-break : break-word;
+ white-space : pre-wrap;
+ line-height : 2ch;
+ padding : 1ch;
+ margin-top : 2ch;
+ }
+ .block > pre {
+ max-height : 200px;
+ }
+ .cli {
+ line-height : 1.7em;
+ font-family : monospace;
+ padding : .4em;
+ color : var(--primary);
+ }
+ .prompt {
+ margin-right : 0.5em;
+ font-family : monospace;
+ font-weight : bold;
+ font-size : 14px;
+ color : lightgray;
+ }
+ .input {
+ word-break : break-word;
+ outline : none;
+ font-family : monospace;
+ }
+ .search {
+ display : flex;
+ align-items : center;
+ width : 330px;
+ padding-left : 20px;
+ height : 100%;
+ padding-top : 1px;
+ padding-bottom : 1px;
+ }
+ .filter {
+ height : 80%;
+ white-space : nowrap;
+ overflow : hidden;
+ text-overflow : ellipsis;
+ }
+ .searchIcon {
+ width : 25px;
+ border-top-left-radius : 3px;
+ border-bottom-left-radius : 3px;
+ display : flex;
+ align-items : center;
+ justify-content : center;
+ margin-right : 5px;
+ }
+ .listen {
+ margin-right : 30px;
+ min-width : 40px;
+ height : 13px;
+ display : flex;
+ align-items : center;
+ }
+ .listenLabel {
+ min-width: 50px;
+ }
+ .verticalLine {
+ border-left : 1px solid var(--secondary);
+ height : 65%;
+ }
+ .dragbarHorizontal {
+ position : absolute;
+ top : 0;
+ height : 0.2em;
+ right : 0;
+ left : 0;
+ cursor : row-resize;
+ z-index : 999;
+ }
+
+ .console {
+ cursor : pointer;
+ }
+
+ .dragbarHorizontal:hover {
+ background-color: #007AA6;
+ border:2px solid #007AA6;
+ }
+ .listenOnNetwork {
+ min-height: auto;
+ }
+ .ghostbar {
+ position : absolute;
+ height : 6px;
+ opacity : 0.5;
+ cursor : row-resize;
+ z-index : 9999;
+ left : 0;
+ right : 0;
+ }
+
+
+ .divider-hitbox {
+ color: white;
+ cursor: row-resize;
+ align-self: stretch;
+ display: flex;
+ align-items: center;
+ padding: 0 1rem;
+ }
+
+ .ul {margin-left: 0; padding-left: 20px;}
+
+ .popup {
+ position : absolute;
+ text-align : left;
+ width : 95%;
+ font-family : monospace;
+ background-color : var(--secondary);
+ overflow : auto;
+ padding-bottom : 13px;
+ z-index : 80;
+ bottom : 1em;
+ border-width : 4px;
+ left : 2em;
+ }
+
+ .autoCompleteItem {
+ padding : 4px;
+ border-radius : 2px;
+ }
+
+ .popup a {
+ cursor : pointer;
+ }
+
+ .listHandlerShow {
+ display : block;
+ }
+
+ .listHandlerHide {
+ display : none;
+ }
+
+ .listHandlerButtonShow {
+ position : fixed;
+ width : 46%;
+ }
+
+ .pageNumberAlignment {
+ font-size : 10px;
+ float : right;
+ }
+
+ .modalContent {
+ position : absolute;
+ margin-left : 20%;
+ margin-bottom : 32px;
+ bottom : 0px;
+ padding : 0;
+ line-height : 18px;
+ font-size : 12px;
+ width : 40%;
+ box-shadow : 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
+ -webkit-animation-name: animatebottom;
+ -webkit-animation-duration: 0.4s;
+ animation-name : animatetop;
+ animation-duration: 0.4s
+ }
+
+ @-webkit-keyframes animatetop {
+ from {bottom: -300px; opacity: 0}
+ to {bottom: 0; opacity: 1}
+ }
+
+ @keyframes animatetop {
+ from {bottom: -300px; opacity: 0}
+ to {bottom: 0; opacity: 1}
+ }
+
+
+
+
+ /* tx logger css*/
+
+ .log {
+ display: flex;
+ cursor: pointer;
+ align-items: center;
+ cursor: pointer;
+ }
+ .log:hover {
+ opacity: 0.8;
+ }
+
+ .txStatus {
+ display: flex;
+ font-size: 20px;
+ margin-right: 20px;
+ float: left;
+ }
+ .succeeded {
+ color: var(--success);
+ }
+ .failed {
+ color: var(--danger);
+ }
+
+ .terminal_arrow {
+ color: var(--text-info);
+ font-size: 20px;
+ cursor: pointer;
+ display: flex;
+ margin-left: 10px;
+ }
+ .terminal_arrow:hover {
+ color: var(--secondary);
+ }
+ .notavailable {
+ }
+ .call {
+ font-size: 7px;
+ border-radius: 50%;
+ min-width: 20px;
+ min-height: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: var(--text-info);
+ text-transform: uppercase;
+ font-weight: bold;
+ }
+ .txItem {
+ color: var(--text-info);
+ margin-right: 5px;
+ float: left;
+ }
+ .txItemTitle {
+ font-weight: bold;
+ }
+ .tx {
+ color: var(--text-info);
+ font-weight: bold;
+ float: left;
+ margin-right: 10px;
+ }
+ .txTable,
+ .tr,
+ .td {
+ border-collapse: collapse;
+ font-size: 10px;
+ color: var(--text-info);
+ border: 1px solid var(--text-info);
+ transition: max-height 0.3s, padding 0.3s;
+ }
+ table .active {
+ transition: max-height 0.6s, padding 0.6s;
+ }
+ #txTable {
+ margin-top: 1%;
+ margin-bottom: 5%;
+ align-self: center;
+ width: 85%;
+ }
+ .tr, .td {
+ padding: 4px;
+ vertical-align: baseline;
+ }
+ .td:first-child {
+ min-width: 30%;
+ width: 30%;
+ align-items: baseline;
+ font-weight: bold;
+ }
+ .tableTitle {
+ width: 25%;
+ }
+ .buttons {
+ display: flex;
+ margin-left: auto;
+ }
+ .debug {
+ white-space: nowrap;
+ }
+ .debug:hover {
+ opacity: 0.8;
+ }
+
+
+ /* Style the accordion section */
+.accordion__section {
+ display: flex;
+ flex-direction: column;
+}
+
+/* Style the buttons that are used to open and close the accordion panel */
+.accordion {
+ background-color: #eee;
+ color: #444;
+ cursor: pointer;
+ padding: 18px;
+ display: flex;
+ align-items: center;
+ border: none;
+ outline: none;
+ transition: background-color 0.6s ease;
+}
+
+/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
+/* .accordion:hover,
+.active {
+ background-color: #ccc;
+} */
+
+/* Style the accordion content title */
+.accordion__title {
+ font-family: "Open Sans", sans-serif;
+ font-weight: 600;
+ font-size: 14px;
+}
+
+/* Style the accordion chevron icon */
+.accordion__icon {
+ margin-left: auto;
+ transition: transform 0.6s ease;
+}
+
+/* Style to rotate icon when state is active */
+.rotate {
+ transform: rotate(90deg);
+}
+
+/* Style the accordion content panel. Note: hidden by default */
+.accordion__content {
+ background-color: white;
+ overflow: hidden;
+ transition: max-height 0.6s ease;
+}
+
+/* Style the accordion content text */
+.accordion__text {
+ font-family: "Open Sans", sans-serif;
+ font-weight: 400;
+ font-size: 14px;
+ padding: 18px;
+}
+
diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
new file mode 100644
index 0000000000..8b729f07d4
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
@@ -0,0 +1,574 @@
+import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line
+import { registerCommandAction, registerLogScriptRunnerAction, registerInfoScriptRunnerAction, registerErrorScriptRunnerAction, registerWarnScriptRunnerAction, listenOnNetworkAction, initListeningOnNetwork } from './actions/terminalAction'
+import { initialState, registerCommandReducer, addCommandHistoryReducer, registerScriptRunnerReducer } from './reducers/terminalReducer'
+import { getKeyOf, getValueOf, Objectfilter, matched } from './utils/utils'
+import {allCommands, allPrograms} from './commands' // eslint-disable-line
+import TerminalWelcomeMessage from './terminalWelcome' // eslint-disable-line
+import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
+import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
+
+import './remix-ui-terminal.css'
+import vm from 'vm'
+import javascriptserialize from 'javascript-serialize'
+import jsbeautify from 'js-beautify'
+import RenderUnKnownTransactions from './components/RenderUnknownTransactions' // eslint-disable-line
+import RenderCall from './components/RenderCall' // eslint-disable-line
+import RenderKnownTransactions from './components/RenderKnownTransactions' // eslint-disable-line
+import parse from 'html-react-parser'
+import { EMPTY_BLOCK, KNOWN_TRANSACTION, RemixUiTerminalProps, UNKNOWN_TRANSACTION } from './types/terminalTypes'
+import { wrapScript } from './utils/wrapScript'
+
+/* eslint-disable-next-line */
+export interface ClipboardEvent extends SyntheticEvent {
+ clipboardData: DataTransfer;
+}
+
+export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
+ const { call, _deps, on, config, event, gistHandler, logHtmlResponse, logResponse, version } = props.plugin
+ const [toggleDownUp, setToggleDownUp] = useState('fa-angle-double-down')
+ const [_cmdIndex, setCmdIndex] = useState(-1)
+ const [_cmdTemp, setCmdTemp] = useState('')
+ // dragable state
+ const [leftHeight, setLeftHeight] = useState(undefined)
+ const [separatorYPosition, setSeparatorYPosition] = useState(undefined)
+ const [dragging, setDragging] = useState(false)
+
+ const [newstate, dispatch] = useReducer(registerCommandReducer, initialState)
+ const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState)
+ const [, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState)
+ const [toaster, setToaster] = useState(false)
+ const [toastProvider, setToastProvider] = useState({ show: false, fileName: '' })
+ const [modalState, setModalState] = useState({
+ message: '',
+ title: '',
+ okLabel: '',
+ cancelLabel: '',
+ hide: true,
+ cancelFn: () => {},
+ handleHide: () => {}
+ })
+
+ const [clearConsole, setClearConsole] = useState(false)
+ const [paste, setPaste] = useState(false)
+ const [autoCompletState, setAutoCompleteState] = useState({
+ activeSuggestion: 0,
+ data: {
+ _options: []
+ },
+ _startingElement: 0,
+ autoCompleteSelectedItem: {},
+ _elementToShow: 4,
+ _selectedElement: 0,
+ filteredCommands: [],
+ filteredPrograms: [],
+ showSuggestions: false,
+ text: '',
+ userInput: '',
+ extraCommands: [],
+ commandHistoryIndex: 0
+ })
+
+ const [searchInput, setSearchInput] = useState('')
+ const [showTableHash, setShowTableHash] = useState([])
+
+ // terminal inputRef
+ const inputEl = useRef(null)
+ const messagesEndRef = useRef(null)
+
+ // terminal dragable
+ const leftRef = useRef(null)
+ const panelRef = useRef(null)
+
+ const scrollToBottom = () => {
+ messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
+ }
+
+ useEffect(() => {
+ scriptRunnerDispatch({ type: 'html', payload: { message: logHtmlResponse } })
+ }, [logHtmlResponse])
+
+ useEffect(() => {
+ scriptRunnerDispatch({ type: 'log', payload: { message: logResponse } })
+ }, [logResponse])
+
+ // events
+ useEffect(() => {
+ initListeningOnNetwork(props.plugin, scriptRunnerDispatch)
+ registerLogScriptRunnerAction(on, 'log', newstate.commands, scriptRunnerDispatch)
+ registerInfoScriptRunnerAction(on, 'info', newstate.commands, scriptRunnerDispatch)
+ registerWarnScriptRunnerAction(on, 'warn', newstate.commands, scriptRunnerDispatch)
+ registerErrorScriptRunnerAction(on, 'error', newstate.commands, scriptRunnerDispatch)
+ registerCommandAction('html', _blocksRenderer('html'), { activate: true }, dispatch)
+ registerCommandAction('log', _blocksRenderer('log'), { activate: true }, dispatch)
+ registerCommandAction('info', _blocksRenderer('info'), { activate: true }, dispatch)
+ registerCommandAction('warn', _blocksRenderer('warn'), { activate: true }, dispatch)
+ registerCommandAction('error', _blocksRenderer('error'), { activate: true }, dispatch)
+
+ registerCommandAction('script', function execute (args, scopedCommands) {
+ var script = String(args[0])
+ _shell(script, scopedCommands, function (error, output) {
+ if (error) scriptRunnerDispatch({ type: 'error', payload: { message: error } })
+ if (output) scriptRunnerDispatch({ type: 'script', payload: { message: '5' } })
+ })
+ }, { activate: true }, dispatch)
+ }, [autoCompletState.text])
+
+ useEffect(() => {
+ scrollToBottom()
+ }, [newstate.journalBlocks.length, logHtmlResponse.length, toaster])
+
+ function execute (file, cb) {
+ function _execute (content, cb) {
+ if (!content) {
+ setToaster(true)
+ if (cb) cb()
+ return
+ }
+ newstate.commands.script(content)
+ }
+
+ if (typeof file === 'undefined') {
+ const content = _deps.editor.currentContent()
+ _execute(content, cb)
+ return
+ }
+
+ const provider = _deps.fileManager.fileProviderOf(file)
+ console.log({ provider })
+
+ if (!provider) {
+ // toolTip(`provider for path ${file} not found`)
+ setToastProvider({ show: true, fileName: file })
+ if (cb) cb()
+ return
+ }
+
+ provider.get(file, (error, content) => {
+ console.log({ content })
+ if (error) {
+ // toolTip(error)
+ // TODO: pop up
+ if (cb) cb()
+ return
+ }
+
+ _execute(content, cb)
+ })
+ }
+
+ function loadgist (id, cb) {
+ gistHandler.loadFromGist({ gist: id }, _deps.fileManager)
+ if (cb) cb()
+ }
+
+ const _shell = async (script, scopedCommands, done) => { // default shell
+ if (script.indexOf('remix:') === 0) {
+ return done(null, 'This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.')
+ }
+ if (script.indexOf('remix.') === 0) {
+ // we keep the old feature. This will basically only be called when the command is querying the "remix" object.
+ // for all the other case, we use the Code Executor plugin
+ const context = { remix: { exeCurrent: (script: any) => { return execute(undefined, script) }, loadgist: (id: any) => { return loadgist(id, () => {}) }, execute: (fileName, callback) => { return execute(fileName, callback) } } }
+ try {
+ const cmds = vm.createContext(context)
+ const result = vm.runInContext(script, cmds) // eslint-disable-line
+ console.log({ result })
+ return done(null, result)
+ } catch (error) {
+ return done(error.message)
+ }
+ }
+ try {
+ if (script.trim().startsWith('git')) {
+ // await this.call('git', 'execute', script) code might be used in the future
+ } else {
+ await call('scriptRunner', 'execute', script)
+ }
+ done()
+ } catch (error) {
+ done(error.message || error)
+ }
+ }
+
+ const handleMinimizeTerminal = (e) => {
+ e.preventDefault()
+ e.stopPropagation()
+ if (toggleDownUp === 'fa-angle-double-down') {
+ setToggleDownUp('fa-angle-double-up')
+ event.trigger('resize', [])
+ } else {
+ const terminalTopOffset = config.get('terminal-top-offset')
+ event.trigger('resize', [terminalTopOffset])
+ setToggleDownUp('fa-angle-double-down')
+ }
+ }
+
+ const focusinput = () => {
+ inputEl.current.focus()
+ }
+
+ const handleKeyDown = (event) => {
+ const suggestionCount = autoCompletState.activeSuggestion
+ if (autoCompletState.userInput !== '' && (event.which === 27 || event.which === 8 || event.which === 46)) {
+ // backspace or any key that should remove the autocompletion
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false }))
+ }
+ if (autoCompletState.showSuggestions && (event.which === 13 || event.which === 9)) {
+ if (autoCompletState.userInput.length === 1) {
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: 0, showSuggestions: false, userInput: Object.keys(autoCompletState.data._options[0]).toString() }))
+ } else {
+ if (autoCompletState.showSuggestions && (event.which === 13 || event.which === 9)) {
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: 0, showSuggestions: false, userInput: autoCompletState.data._options[autoCompletState.activeSuggestion] ? Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion]).toString() : inputEl.current.value }))
+ } else {
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: 0, showSuggestions: false, userInput: autoCompletState.data._options.length === 1 ? Object.keys(autoCompletState.data._options[0]).toString() : inputEl.current.value }))
+ }
+ }
+ }
+ if (event.which === 13 && !autoCompletState.showSuggestions) {
+ if (event.ctrlKey) { //
+ // on enter, append the value in the cli input to the journal
+ inputEl.current.focus()
+ } else { //
+ event.preventDefault()
+ setCmdIndex(-1)
+ setCmdTemp('')
+ const script = autoCompletState.userInput.trim() // inputEl.current.innerText.trim()
+ if (script.length) {
+ cmdHistoryDispatch({ type: 'cmdHistory', payload: { script } })
+ newstate.commands.script(wrapScript(script))
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, userInput: '' }))
+ inputEl.current.innerText = ''
+ inputEl.current.focus()
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false }))
+ }
+ } else if (newstate._commandHistory.length && event.which === 38 && !autoCompletState.showSuggestions && (autoCompletState.userInput === '')) {
+ event.preventDefault()
+ setAutoCompleteState(prevState => ({ ...prevState, userInput: newstate._commandHistory[0] }))
+ } else if (event.which === 38 && autoCompletState.showSuggestions) {
+ event.preventDefault()
+ if (autoCompletState.activeSuggestion === 0) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount - 1, userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion]).toString() }))
+ } else if (event.which === 38 && !autoCompletState.showSuggestions) { //
+ if (cmdHistory.length - 1 > _cmdIndex) {
+ setCmdIndex(prevState => prevState++)
+ }
+ inputEl.current.innerText = cmdHistory[_cmdIndex]
+ inputEl.current.focus()
+ } else if (event.which === 40 && autoCompletState.showSuggestions) {
+ event.preventDefault()
+ if ((autoCompletState.activeSuggestion + 1) === autoCompletState.data._options.length) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount + 1, userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion + 1]).toString() }))
+ } else if (event.which === 40 && !autoCompletState.showSuggestions) {
+ if (_cmdIndex > -1) {
+ setCmdIndex(prevState => prevState--)
+ }
+ inputEl.current.innerText = _cmdIndex >= 0 ? cmdHistory[_cmdIndex] : _cmdTemp
+ inputEl.current.focus()
+ } else {
+ setCmdTemp(inputEl.current.innerText)
+ }
+ }
+
+ /* start of mouse events */
+
+ const mousedown = (event: MouseEvent) => {
+ setSeparatorYPosition(event.clientY)
+ leftRef.current.style.backgroundColor = '#007AA6'
+ leftRef.current.style.border = '2px solid #007AA6'
+ setDragging(true)
+ }
+
+ const onMouseMove: any = (e: MouseEvent) => {
+ e.preventDefault()
+ if (dragging && leftHeight && separatorYPosition) {
+ const newLeftHeight = leftHeight + separatorYPosition - e.clientY
+ setSeparatorYPosition(e.clientY)
+ setLeftHeight(newLeftHeight)
+ event.trigger('resize', [newLeftHeight + 32])
+ }
+ }
+
+ const onMouseUp = () => {
+ leftRef.current.style.backgroundColor = ''
+ leftRef.current.style.border = ''
+ setDragging(false)
+ }
+
+ /* end of mouse event */
+
+ useEffect(() => {
+ document.addEventListener('mousemove', onMouseMove)
+ document.addEventListener('mouseup', onMouseUp)
+
+ return () => {
+ document.removeEventListener('mousemove', onMouseMove)
+ document.removeEventListener('mouseup', onMouseUp)
+ }
+ }, [onMouseMove, onMouseUp])
+
+ React.useEffect(() => {
+ if (panelRef) {
+ if (!leftHeight) {
+ setLeftHeight(panelRef.current.offsetHeight)
+ return
+ }
+ panelRef.current.style.height = `${leftHeight}px`
+ }
+ }, [leftHeight, setLeftHeight, panelRef])
+
+ /* block contents that gets rendered from scriptRunner */
+
+ const _blocksRenderer = (mode) => {
+ if (mode === 'html') {
+ return function logger (args) {
+ if (args.length) {
+ return args[0]
+ }
+ }
+ }
+ mode = {
+ log: 'text-log',
+ info: 'text-info',
+ warn: 'text-warning',
+ error: 'text-danger'
+ }[mode] // defaults
+
+ if (mode) {
+ const filterUndefined = (el) => el !== undefined && el !== null
+ return function logger (args) {
+ const types = args.filter(filterUndefined).map(type => type)
+ const values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) {
+ if (typeof args[idx] === 'string') {
+ const el = document.createElement('div')
+ el.innerHTML = args[idx].replace(/(\r\n|\n|\r)/gm, ' ')
+ val = el.children.length === 0 ? el.firstChild : el
+ }
+ if (types[idx] === 'element') val = jsbeautify.html(val)
+ return val
+ })
+ if (values.length) {
+ return values
+ }
+ }
+ } else {
+ throw new Error('mode is not supported')
+ }
+ }
+
+ /* end of block content that gets rendered from script Runner */
+
+ const handleClearConsole = () => {
+ setClearConsole(true)
+ dispatch({ type: 'clearconsole', payload: [] })
+ inputEl.current.focus()
+ }
+ /* start of autoComplete */
+
+ const listenOnNetwork = (e: any) => {
+ const isListening = e.target.checked
+ // setIsListeningOnNetwork(isListening)
+ listenOnNetworkAction(event, isListening)
+ }
+
+ const onChange = (event: any) => {
+ event.preventDefault()
+ const inputString = event.target.value
+ if (matched(allPrograms, inputString) || inputString.includes('.')) {
+ if (paste) {
+ setPaste(false)
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: inputString }))
+ } else {
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: true, userInput: inputString }))
+ }
+ const textList = inputString.split('.')
+ if (textList.length === 1) {
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
+ const result = Objectfilter(allPrograms, autoCompletState.userInput)
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
+ } else {
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
+ const result = Objectfilter(allCommands, autoCompletState.userInput)
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
+ }
+ } else {
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: inputString }))
+ }
+ }
+
+ const handleSelect = (event) => {
+ const suggestionCount = autoCompletState.activeSuggestion
+ if (event.keyCode === 38) {
+ if (autoCompletState.activeSuggestion === 0) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount - 1 }))
+ } else if (event.keyCode === 40) {
+ if (autoCompletState.activeSuggestion - 1 === autoCompletState.data._options.length) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount + 1 }))
+ }
+ }
+
+ const modal = (title: string, message: string, okLabel: string, hide: boolean, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => {
+ setModalState(prevState => ({ ...prevState, message, okLabel, okFn, cancelLabel, cancelFn, hide }))
+ }
+
+ const handleHideModal = () => {
+ setModalState(prevState => ({ ...prevState, hide: true }))
+ }
+
+ const txDetails = (event, tx) => {
+ if (showTableHash.includes(tx.hash)) {
+ const index = showTableHash.indexOf(tx.hash)
+ if (index > -1) {
+ setShowTableHash((prevState) => prevState.filter((x) => x !== tx.hash))
+ }
+ } else {
+ setShowTableHash((prevState) => ([...prevState, tx.hash]))
+ }
+ }
+
+ const handleAutoComplete = () => (
+ 0 ? 'block' : 'none' }}>
+
+ {autoCompletState.data._options.map((item, index) => {
+ return (
+
+
+ {getKeyOf(item)}
+
+
+ {getValueOf(item)}
+
+
+ )
+ })}
+
+
+ )
+ /* end of autoComplete */
+
+ const handlePaste = () => {
+ setPaste(true)
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: 0, showSuggestions: false }))
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
0
+
+
+
+ listen on network
+
+
+
+
+ setSearchInput(event.target.value.trim()) }
+ type="text"
+ className="border filter form-control"
+ id="searchInput"
+ placeholder="Search with transaction hash or address"
+ data-id="terminalInputSearch" />
+
+
+
+
+ {
+ handleAutoComplete()
+ }
+
+
+
+ {!clearConsole &&
}
+ {newstate.journalBlocks && newstate.journalBlocks.map((x, index) => {
+ if (x.name === EMPTY_BLOCK) {
+ return (
+
+
+ [block:{x.message} - 0 {'transactions'} ]
+
+ )
+ } else if (x.name === UNKNOWN_TRANSACTION) {
+ return x.message.filter(x => x.tx.hash.includes(searchInput) || x.tx.from.includes(searchInput) || (x.tx.to.includes(searchInput))).map((trans) => {
+ return ( { }
)
+ })
+ } else if (x.name === KNOWN_TRANSACTION) {
+ return x.message.map((trans) => {
+ return ( { trans.tx.isCall ? : () }
)
+ })
+ } else if (Array.isArray(x.message)) {
+ return x.message.map((msg, i) => {
+ if (typeof msg === 'object') {
+ return (
+ { msg.value ? parse(msg.value) : JSON.stringify(msg) }
+ )
+ } else {
+ return (
+ { msg ? msg.toString().replace(/,/g, '') : msg }
+ )
+ }
+ })
+ } else {
+ if (typeof x.message !== 'function') {
+ return (
+ {x.message}
+ )
+ }
+ }
+ })}
+
+
+
+ {'>'}
+ onChange(event)} onKeyDown={(event) => handleKeyDown(event) } value={autoCompletState.userInput} onPaste={handlePaste}>
+
+
+
+
+ {toaster &&
}
+ {toastProvider.show &&
}
+
+ )
+}
+
+export default RemixUiTerminal
diff --git a/libs/remix-ui/terminal/src/lib/terminalWelcome.tsx b/libs/remix-ui/terminal/src/lib/terminalWelcome.tsx
new file mode 100644
index 0000000000..6f8cca225b
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/terminalWelcome.tsx
@@ -0,0 +1,29 @@
+import React from 'react' // eslint-disable-line
+
+const TerminalWelcomeMessage = ({ packageJson }) => {
+ return (
+
+
- Welcome to Remix {packageJson} -
+
You can use this terminal to:
+
+ Check transactions details and start debugging.
+ Execute JavaScript scripts:
+
+ - Input a script directly in the command line interface
+
+ - Select a Javascript file in the file explorer and then run \`remix.execute()\` or \`remix.exeCurrent()\` in the command line interface
+
+ - Right click on a JavaScript file in the file explorer and then click \`Run\`
+
+
+
The following libraries are accessible:
+
+
+ )
+}
+
+export default TerminalWelcomeMessage
diff --git a/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts b/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts
new file mode 100644
index 0000000000..66eea4d655
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/types/terminalTypes.ts
@@ -0,0 +1,28 @@
+
+export interface ROOTS {
+ steps: any,
+ cmd: string,
+ gidx: number,
+ idx: number
+}
+
+export const KNOWN_TRANSACTION = 'knownTransaction'
+export const UNKNOWN_TRANSACTION = 'unknownTransaction'
+export const EMPTY_BLOCK = 'emptyBlock'
+export const NEW_TRANSACTION = 'newTransaction'
+export const NEW_BLOCK = 'newBlock'
+export const NEW_CALL = 'newCall'
+
+export const HTML = 'html'
+export const LOG = 'log'
+export const INFO = 'info'
+export const WARN = 'warn'
+export const ERROR = 'error'
+export const SCRIPT = 'script'
+export const CLEAR_CONSOLE = 'clearconsole'
+export const LISTEN_ON_NETWORK = 'listenOnNetWork'
+export const CMD_HISTORY = 'cmdHistory'
+
+export interface RemixUiTerminalProps {
+ plugin: any
+}
diff --git a/libs/remix-ui/terminal/src/lib/utils/utils.ts b/libs/remix-ui/terminal/src/lib/utils/utils.ts
new file mode 100644
index 0000000000..62091ee9c8
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/utils/utils.ts
@@ -0,0 +1,42 @@
+
+export const getKeyOf = (item) => {
+ return Object.keys(item)[0]
+}
+
+export const getValueOf = (item) => {
+ return Object.values(item)[0]
+}
+
+export const Objectfilter = (obj: any, filterValue: any) =>
+ obj.filter((item: any) => Object.keys(item)[0].indexOf(filterValue) > -1)
+
+export const matched = (arr, value) => arr.map(x => Object.keys(x).some(x => x.startsWith(value))).some(x => x === true)
+
+const findDeep = (object, fn, found = { break: false, value: undefined }) => {
+ if (typeof object !== 'object' || object === null) return
+ for (var i in object) {
+ if (found.break) break
+ var el = object[i]
+ if (el && el.innerText !== undefined && el.innerText !== null) el = el.innerText
+ if (fn(el, i, object)) {
+ found.value = el
+ found.break = true
+ break
+ } else {
+ findDeep(el, fn, found)
+ }
+ }
+ return found.value
+}
+
+export const find = (args, query) => {
+ query = query.trim()
+ const isMatch = !!findDeep(args, function check (value) {
+ if (value === undefined || value === null) return false
+ if (typeof value === 'function') return false
+ if (typeof value === 'object') return false
+ const contains = String(value).indexOf(query.trim()) !== -1
+ return contains
+ })
+ return isMatch
+}
diff --git a/libs/remix-ui/terminal/src/lib/utils/wrapScript.ts b/libs/remix-ui/terminal/src/lib/utils/wrapScript.ts
new file mode 100644
index 0000000000..3d9ddcdeea
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/utils/wrapScript.ts
@@ -0,0 +1,16 @@
+export const wrapScript = (script) => {
+ const isKnownScript = ['remix.', 'git'].some(prefix => script.trim().startsWith(prefix))
+ if (isKnownScript) return script
+ return `
+ try {
+ const ret = ${script};
+ if (ret instanceof Promise) {
+ ret.then((result) => { console.log(result) }).catch((error) => { console.log(error) })
+ } else {
+ console.log(ret)
+ }
+ } catch (e) {
+ console.log(e.message)
+ }
+ `
+}
diff --git a/libs/remix-ui/terminal/tsconfig.json b/libs/remix-ui/terminal/tsconfig.json
new file mode 100644
index 0000000000..d52e31ad74
--- /dev/null
+++ b/libs/remix-ui/terminal/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "compilerOptions": {
+ "jsx": "react",
+ "allowJs": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ }
+ ]
+}
diff --git a/libs/remix-ui/terminal/tsconfig.lib.json b/libs/remix-ui/terminal/tsconfig.lib.json
new file mode 100644
index 0000000000..b560bc4dec
--- /dev/null
+++ b/libs/remix-ui/terminal/tsconfig.lib.json
@@ -0,0 +1,13 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../../dist/out-tsc",
+ "types": ["node"]
+ },
+ "files": [
+ "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
+ "../../../node_modules/@nrwl/react/typings/image.d.ts"
+ ],
+ "exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
+ "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
+}
diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
index 88fb39baed..2707079c15 100644
--- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
+++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
@@ -332,7 +332,7 @@ export const Workspace = (props: WorkspaceProps) => {
return (
-
{
handleHide={ handleHideModal }>
{ (typeof state.modal.message !== 'string') && state.modal.message }
+ }
resetFocus(true)}>
diff --git a/nx.json b/nx.json
index 45d68149ae..e86b587935 100644
--- a/nx.json
+++ b/nx.json
@@ -121,6 +121,9 @@
"remix-ui-renderer": {
"tags": []
},
+ "remix-ui-terminal": {
+ "tags": []
+ },
"solidity-compiler": {
"tags": []
},
diff --git a/package.json b/package.json
index cc17c169f2..fc6bbc53ad 100644
--- a/package.json
+++ b/package.json
@@ -45,8 +45,8 @@
"workspace-schematic": "nx workspace-schematic",
"dep-graph": "nx dep-graph",
"help": "nx help",
- "lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,remix-ui-plugin-manager",
- "build:libs": "nx run-many --target=build --parallel=false --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
+ "lint:libs": "nx run-many --target=lint --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd,remix-ui-tree-view,remix-ui-modal-dialog,remix-ui-toaster,remix-ui-file-explorer,remix-ui-debugger-ui,remix-ui-workspace,remix-ui-static-analyser,remix-ui-checkbox,remix-ui-settings,remix-core-plugin,remix-ui-renderer,remix-ui-publish-to-storage,remix-ui-solidity-compiler,remix-ui-plugin-manager,remix-ui-terminal",
+ "build:libs": "nx run-many --target=build --parallel=false --with-deps=true --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"test:libs": "nx run-many --target=test --projects=remix-analyzer,remix-astwalker,remix-debug,remix-lib,remix-simulator,remix-solidity,remix-tests,remix-url-resolver,remixd",
"publish:libs": "npm run build:libs && lerna publish --skip-git && npm run bumpVersion:libs",
"build:e2e": "tsc -p apps/remix-ide-e2e/tsconfig.e2e.json",
@@ -164,6 +164,7 @@
"file-saver": "^2.0.5",
"form-data": "^4.0.0",
"fs-extra": "^3.0.1",
+ "html-react-parser": "^1.3.0",
"http-server": "^0.11.1",
"intro.js": "^4.1.0",
"isbinaryfile": "^3.0.2",
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 86f477732d..38538c202a 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -43,8 +43,9 @@
"@remix-project/core-plugin": ["libs/remix-core-plugin/src/index.ts"],
"@remix-ui/solidity-compiler": ["libs/remix-ui/solidity-compiler/src/index.ts"],
"@remix-ui/publish-to-storage": ["libs/remix-ui/publish-to-storage/src/index.ts"],
- "@remix-ui/plugin-manager": ["libs/remix-ui/plugin-manager/src/index.ts"],
- "@remix-ui/renderer": ["libs/remix-ui/renderer/src/index.ts"]
+ "@remix-ui/renderer": ["libs/remix-ui/renderer/src/index.ts"],
+ "@remix-ui/terminal": ["libs/remix-ui/terminal/src/index.ts"],
+ "@remix-ui/plugin-manager": ["libs/remix-ui/plugin-manager/src/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]
diff --git a/workspace.json b/workspace.json
index cc763f7202..8d6c4d0e5c 100644
--- a/workspace.json
+++ b/workspace.json
@@ -778,9 +778,25 @@
}
}
},
+ "remix-ui-terminal": {
+ "root": "libs/remix-ui/terminal",
+ "sourceRoot": "libs/remix-ui/terminal/src",
+ "projectType": "library",
+ "schematics": {},
+ "architect": {
+ "lint": {
+ "builder": "@nrwl/linter:lint",
+ "options": {
+ "linter": "eslint",
+ "tsConfig": ["libs/remix-ui/terminal/tsconfig.lib.json"],
+ "exclude": ["**/node_modules/**", "!libs/remix-ui/terminal/**/*"]
+ }
+ }
+ }
+ },
"remix-ui-plugin-manager": {
- "root": "libs/remix-ui/plugin-manager",
- "sourceRoot": "libs/remix-ui/plugin-manager/src",
+ "root": "libs/remix-ui/plugin-manager",
+ "sourceRoot": "libs/remix-ui/plugin-manager/src",
"projectType": "library",
"schematics": {},
"architect": {