Merge branch 'master' into intl

pull/5370/head
drafish 2 years ago
commit e0cd663967
  1. 1
      apps/remix-ide-e2e/src/helpers/hardhat_compilation_7839ba878952cc00ff316061405f273a.json
  2. 113556
      apps/remix-ide-e2e/src/helpers/hardhat_compilation_8a7ab689ec618720f53ce867a3031c03.json
  3. 4
      apps/remix-ide-e2e/src/helpers/hardhat_compilation_Lock.dbg.json
  4. 74
      apps/remix-ide-e2e/src/helpers/hardhat_compilation_Lock.json
  5. 2
      apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts
  6. 231
      apps/remix-ide-e2e/src/tests/remixd.test.ts
  7. 1
      apps/remix-ide/ci/browser_test.sh
  8. 1
      apps/remix-ide/ci/flaky.sh
  9. 0
      apps/remix-ide/contracts/foundry/foundry.toml
  10. 0
      apps/remix-ide/contracts/foundry/out/Counter.s.sol/CounterScript.json
  11. 0
      apps/remix-ide/contracts/foundry/out/Counter.s.sol/CounterScript.metadata.json
  12. 0
      apps/remix-ide/contracts/foundry/out/Counter.sol/Counter.json
  13. 0
      apps/remix-ide/contracts/foundry/out/Counter.sol/Counter.metadata.json
  14. 0
      apps/remix-ide/contracts/foundry/out/Counter.t.sol/CounterTest.json
  15. 0
      apps/remix-ide/contracts/foundry/out/Counter.t.sol/CounterTest.metadata.json
  16. 0
      apps/remix-ide/contracts/foundry/out/Script.sol/Script.json
  17. 0
      apps/remix-ide/contracts/foundry/out/Script.sol/Script.metadata.json
  18. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/Test.json
  19. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/Test.metadata.json
  20. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/stdError.json
  21. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/stdError.metadata.json
  22. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/stdMath.json
  23. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/stdMath.metadata.json
  24. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/stdStorage.json
  25. 0
      apps/remix-ide/contracts/foundry/out/Test.sol/stdStorage.metadata.json
  26. 0
      apps/remix-ide/contracts/foundry/out/Vm.sol/Vm.json
  27. 0
      apps/remix-ide/contracts/foundry/out/Vm.sol/Vm.metadata.json
  28. 0
      apps/remix-ide/contracts/foundry/out/console.sol/console.json
  29. 0
      apps/remix-ide/contracts/foundry/out/console.sol/console.metadata.json
  30. 0
      apps/remix-ide/contracts/foundry/out/console2.sol/console2.json
  31. 0
      apps/remix-ide/contracts/foundry/out/console2.sol/console2.metadata.json
  32. 0
      apps/remix-ide/contracts/foundry/out/test.sol/DSTest.json
  33. 0
      apps/remix-ide/contracts/foundry/out/test.sol/DSTest.metadata.json
  34. 14
      apps/remix-ide/contracts/foundry/src/Counter.sol
  35. 0
      apps/remix-ide/contracts/hardhat/artifacts/build-info/.gitgnore
  36. 1
      apps/remix-ide/contracts/hardhat/artifacts/build-info/7839ba878952cc00ff316061405f273a.json
  37. 0
      apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/.gitgnore
  38. 1
      apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.dbg.json
  39. 1
      apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.json
  40. 17
      apps/remix-ide/contracts/hardhat/compiler_config.json
  41. 34
      apps/remix-ide/contracts/hardhat/contracts/Lock.sol
  42. 0
      apps/remix-ide/contracts/hardhat/hardhat.config.js
  43. 0
      apps/remix-ide/contracts/truffle/build/contracts/.gitignore
  44. 18
      apps/remix-ide/contracts/truffle/contracts/Migrations.sol
  45. 0
      apps/remix-ide/contracts/truffle/truffle-config.js
  46. 2
      apps/remix-ide/src/app/files/foundry-handle.js
  47. 2
      apps/remix-ide/src/app/files/hardhat-handle.js
  48. 2
      apps/remix-ide/src/app/files/truffle-handle.js
  49. 3
      apps/remix-ide/src/app/plugins/config.ts
  50. 17
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  51. 23
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  52. 3
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  53. 7
      apps/remix-ide/src/config.js
  54. 6
      apps/remix-ide/src/remixAppManager.js
  55. 11
      libs/remix-ui/editor/src/lib/providers/completionProvider.ts
  56. 12
      libs/remix-ui/run-tab/src/lib/actions/deploy.ts
  57. 3
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  58. 31
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  59. 4
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  60. 1
      libs/remix-ui/run-tab/src/lib/types/index.ts
  61. 1
      libs/remix-ui/run-tab/src/lib/types/run-tab.d.ts
  62. 7
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  63. 53
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  64. 8
      libs/remix-ws-templates/src/templates/ozerc1155/index.ts
  65. 8
      libs/remix-ws-templates/src/templates/ozerc20/index.ts
  66. 8
      libs/remix-ws-templates/src/templates/ozerc721/index.ts
  67. 2
      libs/remixd/src/bin/remixd.ts
  68. 57
      libs/remixd/src/services/foundryClient.ts
  69. 101
      libs/remixd/src/services/hardhatClient.ts
  70. 73
      libs/remixd/src/services/truffleClient.ts
  71. 3
      package.json
  72. 2
      yarn.lock

File diff suppressed because one or more lines are too long

@ -0,0 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/7839ba878952cc00ff316061405f273a.json"
}

@ -0,0 +1,74 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "Lock",
"sourceName": "contracts/Lock.sol",
"abi": [
{
"inputs": [
{
"internalType": "uint256",
"name": "_unlockTime",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "when",
"type": "uint256"
}
],
"name": "Withdrawal",
"type": "event"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address payable",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "unlockTime",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "withdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x60806040526040516105e13803806105e1833981810160405281019061002591906100f9565b804210610067576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161005e906101a9565b60405180910390fd5b60006206ef9190508160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050506101c9565b600080fd5b6000819050919050565b6100d6816100c3565b81146100e157600080fd5b50565b6000815190506100f3816100cd565b92915050565b60006020828403121561010f5761010e6100be565b5b600061011d848285016100e4565b91505092915050565b600082825260208201905092915050565b7f556e6c6f636b2074696d652073686f756c6420626520696e207468652066757460008201527f7572650000000000000000000000000000000000000000000000000000000000602082015250565b6000610193602383610126565b915061019e82610137565b604082019050919050565b600060208201905081810360008301526101c281610186565b9050919050565b610409806101d86000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063251c1aa3146100465780633ccfd60b146100645780638da5cb5b1461006e575b600080fd5b61004e61008c565b60405161005b919061024a565b60405180910390f35b61006c610092565b005b61007661020b565b60405161008391906102a6565b60405180910390f35b60005481565b6000544210156100d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ce9061031e565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610167576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015e9061038a565b60405180910390fd5b7fbf2ed60bd5b5965d685680c01195c9514e4382e28e3a5a2d2d5244bf59411b9347426040516101989291906103aa565b60405180910390a1600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f19350505050158015610208573d6000803e3d6000fd5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61024481610231565b82525050565b600060208201905061025f600083018461023b565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061029082610265565b9050919050565b6102a081610285565b82525050565b60006020820190506102bb6000830184610297565b92915050565b600082825260208201905092915050565b7f596f752063616e27742077697468647261772079657400000000000000000000600082015250565b60006103086016836102c1565b9150610313826102d2565b602082019050919050565b60006020820190508181036000830152610337816102fb565b9050919050565b7f596f75206172656e277420746865206f776e6572000000000000000000000000600082015250565b60006103746014836102c1565b915061037f8261033e565b602082019050919050565b600060208201905081810360008301526103a381610367565b9050919050565b60006040820190506103bf600083018561023b565b6103cc602083018461023b565b939250505056fea264697066735822122036a0d1608af22fc2fc867e67c4fede260661a25d94e8d38a0f76a2324a7cebe564736f6c63430008110033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063251c1aa3146100465780633ccfd60b146100645780638da5cb5b1461006e575b600080fd5b61004e61008c565b60405161005b919061024a565b60405180910390f35b61006c610092565b005b61007661020b565b60405161008391906102a6565b60405180910390f35b60005481565b6000544210156100d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100ce9061031e565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610167576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161015e9061038a565b60405180910390fd5b7fbf2ed60bd5b5965d685680c01195c9514e4382e28e3a5a2d2d5244bf59411b9347426040516101989291906103aa565b60405180910390a1600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f19350505050158015610208573d6000803e3d6000fd5b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000819050919050565b61024481610231565b82525050565b600060208201905061025f600083018461023b565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061029082610265565b9050919050565b6102a081610285565b82525050565b60006020820190506102bb6000830184610297565b92915050565b600082825260208201905092915050565b7f596f752063616e27742077697468647261772079657400000000000000000000600082015250565b60006103086016836102c1565b9150610313826102d2565b602082019050919050565b60006020820190508181036000830152610337816102fb565b9050919050565b7f596f75206172656e277420746865206f776e6572000000000000000000000000600082015250565b60006103746014836102c1565b915061037f8261033e565b602082019050919050565b600060208201905081810360008301526103a381610367565b9050919050565b60006040820190506103bf600083018561023b565b6103cc602083018461023b565b939250505056fea264697066735822122036a0d1608af22fc2fc867e67c4fede260661a25d94e8d38a0f76a2324a7cebe564736f6c63430008110033",
"linkReferences": {},
"deployedLinkReferences": {}
}

@ -112,7 +112,7 @@ module.exports = {
return actions. return actions.
sendKeys(' someaddress;') sendKeys(' someaddress;')
.sendKeys(this.Keys.ENTER) .sendKeys(this.Keys.ENTER)
}).pause(2000) }).pause(4000)
.perform(function () { .perform(function () {
const actions = this.actions({ async: true }); const actions = this.actions({ async: true });
return actions. return actions.

@ -1,11 +1,18 @@
'use strict' 'use strict'
import { NightwatchBrowser } from 'nightwatch' import { NightwatchBrowser } from 'nightwatch'
import { writeFileSync } from 'fs'
import init from '../helpers/init' import init from '../helpers/init'
import * as hardhatCompilation from '../helpers/hardhat_compilation_8a7ab689ec618720f53ce867a3031c03.json' import { join } from 'path'
import { ChildProcess, spawn } from 'child_process'
import { writeFileSync } from 'fs'
import * as hardhatCompilation from '../helpers/hardhat_compilation_7839ba878952cc00ff316061405f273a.json'
import * as hardhat_compilation_Lock_dbg from '../helpers/hardhat_compilation_Lock.dbg.json'
import * as hardhat_compilation_Lock from '../helpers/hardhat_compilation_Lock.json'
import * as foundryCompilation from '../helpers/foundry_compilation.json' import * as foundryCompilation from '../helpers/foundry_compilation.json'
import * as truffle_compilation from '../helpers/truffle_compilation.json' import * as truffle_compilation from '../helpers/truffle_compilation.json'
import kill from 'tree-kill'
let remixd: ChildProcess
const assetsTestContract = `import "./contract.sol"; const assetsTestContract = `import "./contract.sol";
contract Assets { contract Assets {
uint[] proposals; uint[] proposals;
@ -53,24 +60,38 @@ module.exports = {
before: function (browser, done) { before: function (browser, done) {
init(browser, done) init(browser, done)
}, },
after: function (browser) {
browser.perform((done) => {
console.log('remixd', remixd.pid)
kill(remixd.pid)
done()
})
},
'@sources': function () { '@sources': function () {
return sources return sources
}, },
'start Remixd': function (browser) {
startRemixd(browser)
},
'run Remixd tests #group4': function (browser) { 'run Remixd tests #group4': function (browser) {
runTests(browser) browser.perform((done) => {
remixd = spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts'))
console.log('working directory', process.cwd())
connectRemixd(browser, done)
})
.perform((done) => {
runTests(browser, done)
})
}, },
'Import from node_modules #group1': function (browser) { 'Import from node_modules #group1': function (browser) {
/* /*
when a relative import is used (i.e import "openzeppelin-solidity/contracts/math/SafeMath.sol") when a relative import is used (i.e import "openzeppelin-solidity/contracts/math/SafeMath.sol")
remix (as well as truffle) try to resolve it against the node_modules and installed_contracts folder. remix (as well as truffle) try to resolve it against the node_modules and installed_contracts folder.
*/ */
browser.perform((done) => {
browser.waitForElementVisible('#icon-panel', 2000) remixd = spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts'))
console.log('working directory', process.cwd())
connectRemixd(browser, done)
})
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.click('[data-path="ballot.sol"]') .click('[data-path="ballot.sol"]')
.addFile('test_import_node_modules.sol', sources[3]['test_import_node_modules.sol']) .addFile('test_import_node_modules.sol', sources[3]['test_import_node_modules.sol'])
@ -79,30 +100,35 @@ module.exports = {
.testContracts('test_import_node_modules.sol', sources[3]['test_import_node_modules.sol'], ['SafeMath']) .testContracts('test_import_node_modules.sol', sources[3]['test_import_node_modules.sol'], ['SafeMath'])
}, },
'Import from node_modules and reference a github import #group2': function (browser) { 'Import from node_modules and reference a github import #group2': function (browser) {
browser.waitForElementVisible('#icon-panel', 2000) browser.perform((done) => {
remixd = spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts'))
console.log('working directory', process.cwd())
connectRemixd(browser, done)
})
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.addFile('test_import_node_modules_with_github_import.sol', sources[4]['test_import_node_modules_with_github_import.sol']) .addFile('test_import_node_modules_with_github_import.sol', sources[4]['test_import_node_modules_with_github_import.sol'])
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.setSolidityCompilerVersion('soljson-v0.8.0+commit.c7dfd78e.js') // open-zeppelin moved to pragma ^0.8.0 .setSolidityCompilerVersion('soljson-v0.8.0+commit.c7dfd78e.js') // open-zeppelin moved to pragma ^0.8.0
.testContracts('test_import_node_modules_with_github_import.sol', sources[4]['test_import_node_modules_with_github_import.sol'], ['ERC20', 'test11']) .testContracts('test_import_node_modules_with_github_import.sol', sources[4]['test_import_node_modules_with_github_import.sol'], ['ERC20', 'test11'])
}, },
'Static Analysis run with remixd #group3': function (browser) { 'Static Analysis run with remixd #group3': '' + function (browser) {
browser.testContracts('test_static_analysis_with_remixd_and_hardhat.sol', sources[5]['test_static_analysis_with_remixd_and_hardhat.sol'], ['test5']).pause(2000) browser.testContracts('test_static_analysis_with_remixd_and_hardhat.sol', sources[5]['test_static_analysis_with_remixd_and_hardhat.sol'], ['test5']).pause(2000)
.clickLaunchIcon('solidityStaticAnalysis') .clickLaunchIcon('solidityStaticAnalysis')
/* /*
.click('#staticanalysisButton button').pause(4000) .click('#staticanalysisButton button').pause(4000)
.waitForElementPresent('#staticanalysisresult .warning', 2000, true, function () { .waitForElementPresent('#staticanalysisresult .warning', 2000, true, function () {
browser browser
.waitForElementVisible('[data-id="staticAnalysisModuleMiscellaneous1Button"]') .waitForElementVisible('[data-id="staticAnalysisModuleMiscellaneous1Button"]')
.click('[data-id="staticAnalysisModuleMiscellaneous1Button"]') .click('[data-id="staticAnalysisModuleMiscellaneous1Button"]')
.waitForElementVisible('.highlightLine16', 60000) .waitForElementVisible('.highlightLine16', 60000)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf( browser.assert.ok(content.indexOf(
'function _sendLogPayload(bytes memory payload) private view {') !== -1, 'function _sendLogPayload(bytes memory payload) private view {') !== -1,
'code has not been loaded') 'code has not been loaded')
}) })
}) })
*/ */
}, },
'Run git status': '' + function (browser) { 'Run git status': '' + function (browser) {
@ -112,38 +138,85 @@ module.exports = {
.journalLastChildIncludes('On branch ') .journalLastChildIncludes('On branch ')
}, },
'Close Remixd #group3': function (browser) { 'Close Remixd #group3': '' + function (browser) {
browser browser
.clickLaunchIcon('pluginManager') .clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager *[data-id="pluginManagerComponentDeactivateButtonremixd"]') .scrollAndClick('#pluginManager *[data-id="pluginManagerComponentDeactivateButtonremixd"]')
}, },
'Should listen on compilation result from hardhat #group5': function (browser: NightwatchBrowser) { 'Should listen on compilation result from hardhat #group5': function (browser: NightwatchBrowser) {
browser.perform((done) => { browser.perform((done) => {
remixd = spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/hardhat'))
console.log('working directory', process.cwd()) console.log('working directory', process.cwd())
writeFileSync('./apps/remix-ide/contracts/artifacts/build-info/c7062fdd360381a85af23eeef31c98f8.json', JSON.stringify(hardhatCompilation)) connectRemixd(browser, done)
done()
}) })
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from hardhat').before(60000) .perform((done) => {
console.log('generating compilation result')
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/build-info/7839ba878952cc00ff316061405f273a.json', JSON.stringify(hardhatCompilation))
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.json', JSON.stringify(hardhat_compilation_Lock))
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.dbg.json', JSON.stringify(hardhat_compilation_Lock_dbg))
done()
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from hardhat').before(60000)
browser.clickLaunchIcon('udapp') browser.clickLaunchIcon('filePanel')
.assert.textContains('*[data-id="udappCompiledBy"]', 'Compiled by hardhat') .openFile('contracts')
.openFile('contracts/Lock.sol')
.clickLaunchIcon('udapp')
.selectContract('Lock') .selectContract('Lock')
.createContract('1') .createContract('1')
.expect.element('*[data-id="terminalJournal"]').text.to.contain('Unlock time should be in the future').before(60000) .expect.element('*[data-id="terminalJournal"]').text.to.contain('Unlock time should be in the future').before(60000)
},
'Should listen on compilation result from foundry #group6': function (browser: NightwatchBrowser) {
},
'Should load compilation result from hardhat when remixd connects #group6': function (browser: NightwatchBrowser) {
// artifacts/build-info/c7062fdd360381a85af23eeef31c98f8.json has already been created
browser
.perform((done) => {
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.dbg.json', JSON.stringify(hardhat_compilation_Lock_dbg))
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/contracts/Lock.sol/Lock.json', JSON.stringify(hardhat_compilation_Lock))
writeFileSync('./apps/remix-ide/contracts/hardhat/artifacts/build-info/7839ba878952cc00ff316061405f273a.json', JSON.stringify(hardhatCompilation))
done()
})
.perform((done) => {
remixd = spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/hardhat'))
console.log('working directory', process.cwd())
connectRemixd(browser, done)
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from hardhat').before(60000)
browser.clickLaunchIcon('filePanel')
.openFile('contracts')
.openFile('contracts/Lock.sol')
.clickLaunchIcon('udapp')
.selectContract('Lock')
.createContract('1')
.expect.element('*[data-id="terminalJournal"]').text.to.contain('Unlock time should be in the future').before(60000)
},
'Should listen on compilation result from foundry #group7': function (browser: NightwatchBrowser) {
browser.perform((done) => { browser.perform((done) => {
remixd = spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/foundry'))
console.log('working directory', process.cwd()) console.log('working directory', process.cwd())
writeFileSync('./apps/remix-ide/contracts/out/Counter.sol/Counter.json', JSON.stringify(foundryCompilation)) connectRemixd(browser, done)
done()
}) })
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from foundry').before(60000) .perform((done) => {
writeFileSync('./apps/remix-ide/contracts/foundry/out/Counter.sol/Counter.json', JSON.stringify(foundryCompilation))
done()
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from foundry').before(60000)
let contractAaddress let contractAaddress
browser.clickLaunchIcon('udapp') browser.clickLaunchIcon('filePanel')
.assert.textContains('*[data-id="udappCompiledBy"]', 'Compiled by foundry') .openFile('src')
.openFile('src/Counter.sol')
.clickLaunchIcon('udapp')
.selectContract('Counter') .selectContract('Counter')
.createContract('') .createContract('')
.getAddressAtPosition(0, (address) => { .getAddressAtPosition(0, (address) => {
@ -157,48 +230,39 @@ module.exports = {
done() done()
}) })
}) })
},
'Should listen on compilation result from truffle #group7': function (browser: NightwatchBrowser) {
},
'Should listen on compilation result from truffle #group8': function (browser: NightwatchBrowser) {
browser.perform((done) => { browser.perform((done) => {
remixd = spawnRemixd(join(process.cwd(), '/apps/remix-ide', '/contracts/truffle'))
console.log('working directory', process.cwd()) console.log('working directory', process.cwd())
writeFileSync('./apps/remix-ide/contracts/build/contracts/Migrations.json', JSON.stringify(truffle_compilation)) connectRemixd(browser, done)
done()
}) })
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from truffle').before(60000) .perform((done) => {
writeFileSync('./apps/remix-ide/contracts/truffle/build/contracts/Migrations.json', JSON.stringify(truffle_compilation))
done()
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from truffle').before(60000)
browser.clickLaunchIcon('udapp') browser.clickLaunchIcon('filePanel')
.assert.textContains('*[data-id="udappCompiledBy"]', 'Compiled by truffle') .openFile('contracts')
.openFile('contracts/Migrations.sol')
.clickLaunchIcon('udapp')
.selectContract('Migrations') .selectContract('Migrations')
.createContract('') .createContract('')
.testFunction('last', .testFunction('last',
{ {
status: 'true Transaction mined and execution succeed' status: 'true Transaction mined and execution succeed'
}) })
}
}
function startRemixd (browser: NightwatchBrowser) {
const browserName = browser.options.desiredCapabilities.browserName
if (browserName === 'safari' || browserName === 'internet explorer') {
console.log('do not run remixd test for ' + browserName + ': sauce labs doesn\'t seems to handle websocket')
browser.end()
return
}
browser }
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('filePanel')
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager *[data-id="pluginManagerComponentActivateButtonremixd"]')
.waitForElementVisible('*[data-id="remixdConnect-modal-footer-ok-react"]', 2000)
.pause(2000)
.click('*[data-id="remixdConnect-modal-footer-ok-react"]')
.pause(10000)
// .click('*[data-id="workspacesModalDialog-modal-footer-ok-react"]')
} }
function runTests (browser: NightwatchBrowser) { function runTests(browser: NightwatchBrowser, done: any) {
const browserName = browser.options.desiredCapabilities.browserName const browserName = browser.options.desiredCapabilities.browserName
browser.clickLaunchIcon('filePanel') browser.clickLaunchIcon('filePanel')
.waitForElementVisible('[data-path="folder1"]') .waitForElementVisible('[data-path="folder1"]')
@ -230,10 +294,11 @@ function runTests (browser: NightwatchBrowser) {
.waitForElementVisible('[data-path="folder1/renamed_contract_' + browserName + '.sol"]') // check if renamed file is preset .waitForElementVisible('[data-path="folder1/renamed_contract_' + browserName + '.sol"]') // check if renamed file is preset
.waitForElementNotPresent('[data-path="folder1/contract_' + browserName + '.sol"]') // check if renamed (old) file is not present .waitForElementNotPresent('[data-path="folder1/contract_' + browserName + '.sol"]') // check if renamed (old) file is not present
.waitForElementNotPresent('[data-path="folder1/contract_' + browserName + '_toremove.sol"]') // check if removed (old) file is not present .waitForElementNotPresent('[data-path="folder1/contract_' + browserName + '_toremove.sol"]') // check if removed (old) file is not present
.perform(done())
// .click('[data-path="folder1/renamed_contract_' + browserName + '.sol"]') // .click('[data-path="folder1/renamed_contract_' + browserName + '.sol"]')
} }
function testImportFromRemixd (browser: NightwatchBrowser, callback: VoidFunction) { function testImportFromRemixd(browser: NightwatchBrowser, callback: VoidFunction) {
browser browser
.waitForElementVisible('[data-path="src"]', 100000) .waitForElementVisible('[data-path="src"]', 100000)
.click('[data-path="src"]') .click('[data-path="src"]')
@ -245,3 +310,37 @@ function testImportFromRemixd (browser: NightwatchBrowser, callback: VoidFunctio
.verifyContracts(['Assets', 'gmbh']) .verifyContracts(['Assets', 'gmbh'])
.perform(() => { callback() }) .perform(() => { callback() })
} }
function spawnRemixd(path: string) {
const remixd = spawn('yarn run remixd', [`-s ${path}`], { cwd: process.cwd(), shell: true, detached: true })
remixd.stdout.on('data', function (data) {
console.log('stdout: ' + data.toString())
})
remixd.stderr.on('err', function (data) {
console.log('err: ' + data.toString())
})
return remixd
}
function connectRemixd(browser: NightwatchBrowser, done: any) {
const browserName = browser.options.desiredCapabilities.browserName
if (browserName === 'safari' || browserName === 'internet explorer') {
console.log('do not run remixd test for ' + browserName + ': sauce labs doesn\'t seems to handle websocket')
browser.end()
done()
return
}
browser
.pause(5000)
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('filePanel')
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager *[data-id="pluginManagerComponentActivateButtonremixd"]')
.waitForElementVisible('*[data-id="remixdConnect-modal-footer-ok-react"]', 2000)
.pause(2000)
.click('*[data-id="remixdConnect-modal-footer-ok-react"]')
.pause(5000)
.perform(() => done())
}

@ -11,7 +11,6 @@ TEST_EXITCODE=0
yarn run ganache-cli & yarn run ganache-cli &
yarn run serve:production & yarn run serve:production &
echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' & echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' &
yarn run remixd &
sleep 5 sleep 5

@ -21,7 +21,6 @@ TEST_EXITCODE=0
yarn run ganache-cli & yarn run ganache-cli &
yarn run serve:production & yarn run serve:production &
echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' & echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' &
yarn run remixd &
npx nx serve remix-ide-e2e-src-local-plugin & npx nx serve remix-ide-e2e-src-local-plugin &
sleep 5 sleep 5

@ -0,0 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
function increment() public {
number++;
}
}

@ -0,0 +1 @@
{"_format":"hh-sol-dbg-1","buildInfo":"../../build-info/7839ba878952cc00ff316061405f273a.json","default":{"_format":"hh-sol-dbg-1","buildInfo":"../../build-info/7839ba878952cc00ff316061405f273a.json"}}

File diff suppressed because one or more lines are too long

@ -0,0 +1,17 @@
{
"language": "Solidity",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"": ["ast"],
"*": ["abi", "metadata", "devdoc", "userdoc", "storageLayout", "evm.legacyAssembly", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "evm.gasEstimates", "evm.assembly"]
}
},
"evmVersion": "byzantium"
}
}

@ -0,0 +1,34 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
// Uncomment this line to use console.log
// import "hardhat/console.sol";
contract Lock {
uint public unlockTime;
address payable public owner;
event Withdrawal(uint amount, uint when);
constructor(uint _unlockTime) payable {
require(
block.timestamp < _unlockTime,
"Unlock time should be in the future"
);
uint p = 454545;
unlockTime = _unlockTime;
owner = payable(msg.sender);
}
function withdraw() public {
// Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
require(block.timestamp >= unlockTime, "You can't withdraw yet");
require(msg.sender == owner, "You aren't the owner");
emit Withdrawal(address(this).balance, block.timestamp);
owner.transfer(address(this).balance);
}
}

@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
// pragma solidity >=0.4.22 <0.8.0;
contract Migrations {
address public owner = msg.sender;
uint public last_completed_migration;
modifier restricted() {
require(
msg.sender == owner,
"This function is restricted to the contract's owner"
);
_;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
}

@ -5,7 +5,7 @@ const profile = {
name: 'foundry', name: 'foundry',
displayName: 'Foundry', displayName: 'Foundry',
url: 'ws://127.0.0.1:65525', url: 'ws://127.0.0.1:65525',
methods: [], methods: ['sync'],
description: 'Using Remixd daemon, allow to access foundry API', description: 'Using Remixd daemon, allow to access foundry API',
kind: 'other', kind: 'other',
version: packageJson.version version: packageJson.version

@ -5,7 +5,7 @@ const profile = {
name: 'hardhat', name: 'hardhat',
displayName: 'Hardhat', displayName: 'Hardhat',
url: 'ws://127.0.0.1:65522', url: 'ws://127.0.0.1:65522',
methods: ['compile'], methods: ['compile', 'sync'],
description: 'Using Remixd daemon, allow to access hardhat API', description: 'Using Remixd daemon, allow to access hardhat API',
kind: 'other', kind: 'other',
version: packageJson.version version: packageJson.version

@ -5,7 +5,7 @@ const profile = {
name: 'truffle', name: 'truffle',
displayName: 'truffle', displayName: 'truffle',
url: 'ws://127.0.0.1:65524', url: 'ws://127.0.0.1:65524',
methods: ['compile'], methods: ['compile', 'sync'],
description: 'Using Remixd daemon, allow to access truffle API', description: 'Using Remixd daemon, allow to access truffle API',
kind: 'other', kind: 'other',
version: packageJson.version version: packageJson.version

@ -6,7 +6,8 @@ const profile = {
name: 'config', name: 'config',
displayName: 'Config', displayName: 'Config',
description: 'Config', description: 'Config',
methods: ['getAppParameter', 'setAppParameter'] methods: ['getAppParameter', 'setAppParameter'],
events: ['configChanged']
} }
export class ConfigPlugin extends Plugin { export class ConfigPlugin extends Plugin {

@ -147,8 +147,25 @@ export class CodeParser extends Plugin {
this.compilerService.compiler.loadVersion(true, `${url}?t=${Date.now()}`) this.compilerService.compiler.loadVersion(true, `${url}?t=${Date.now()}`)
}) })
this.on('config', 'configChanged', async (config) => {
await this.reload()
})
this.on('settings', 'configChanged', async (config) => {
await this.reload()
})
await this.compilerService.init() await this.compilerService.init()
this.on('solidity', 'compilerLoaded', async () => {
await this.reload()
})
}
async reload(){
await this.call('editor', 'discardLineTexts')
await this.call('fileDecorator', 'clearFileDecorators')
await this.call('editor', 'clearErrorMarkers', [this.currentFile])
await this.handleChangeEvents()
} }
/** /**

@ -34,19 +34,20 @@ enum State {
export class RemixdHandle extends WebsocketPlugin { export class RemixdHandle extends WebsocketPlugin {
localhostProvider: any localhostProvider: any
appManager: PluginManager appManager: PluginManager
dependentPlugins: Array<string>
constructor (localhostProvider, appManager) { constructor (localhostProvider, appManager) {
super(profile) super(profile)
this.localhostProvider = localhostProvider this.localhostProvider = localhostProvider
this.appManager = appManager this.appManager = appManager
this.dependentPlugins = ['hardhat', 'truffle', 'slither', 'foundry']
} }
async deactivate () { async deactivate () {
for (const plugin of this.dependentPlugins) {
await this.appManager.deactivatePlugin(plugin)
}
if (super.socket) super.deactivate() if (super.socket) super.deactivate()
// this.appManager.deactivatePlugin('git') // plugin call doesn't work.. see issue https://github.com/ethereum/remix-plugin/issues/342 // this.appManager.deactivatePlugin('git') // plugin call doesn't work.. see issue https://github.com/ethereum/remix-plugin/issues/342
if (this.appManager.isActive('hardhat')) this.appManager.deactivatePlugin('hardhat')
if (this.appManager.isActive('truffle')) this.appManager.deactivatePlugin('truffle')
if (this.appManager.isActive('slither')) this.appManager.deactivatePlugin('slither')
if (this.appManager.isActive('foundry')) this.appManager.deactivatePlugin('foundry')
this.localhostProvider.close((error) => { this.localhostProvider.close((error) => {
if (error) console.log(error) if (error) console.log(error)
}) })
@ -58,6 +59,9 @@ export class RemixdHandle extends WebsocketPlugin {
} }
async canceled () { async canceled () {
for (const plugin of this.dependentPlugins) {
await this.appManager.deactivatePlugin(plugin)
}
await this.appManager.deactivatePlugin('remixd') await this.appManager.deactivatePlugin('remixd')
} }
@ -68,7 +72,7 @@ export class RemixdHandle extends WebsocketPlugin {
* @param {String} txHash - hash of the transaction * @param {String} txHash - hash of the transaction
*/ */
async connectToLocalhost () { async connectToLocalhost () {
const connection = (error?:any) => { const connection = async (error?:any) => {
if (error) { if (error) {
console.log(error) console.log(error)
const alert:AlertModal = { const alert:AlertModal = {
@ -91,11 +95,10 @@ export class RemixdHandle extends WebsocketPlugin {
}, 3000) }, 3000)
this.localhostProvider.init(() => { this.localhostProvider.init(() => {
this.call('filePanel', 'setWorkspace', { name: LOCALHOST, isLocalhost: true }, true) this.call('filePanel', 'setWorkspace', { name: LOCALHOST, isLocalhost: true }, true)
}) });
this.call('manager', 'activatePlugin', 'hardhat') for (const plugin of this.dependentPlugins) {
this.call('manager', 'activatePlugin', 'truffle') await this.appManager.activatePlugin(plugin)
this.call('manager', 'activatePlugin', 'slither') }
this.call('manager', 'activatePlugin', 'foundry')
} }
} }
if (this.localhostProvider.isConnected()) { if (this.localhostProvider.isConnected()) {

@ -33,6 +33,9 @@ module.exports = class SettingsTab extends ViewPlugin {
constructor (config, editor) { constructor (config, editor) {
super(profile) super(profile)
this.config = config this.config = config
this.config.events.on('configChanged', (changedConfig) => {
this.emit('configChanged', changedConfig)
})
this.editor = editor this.editor = editor
this._deps = { this._deps = {
themeModule: Registry.getInstance().get('themeModule').api, themeModule: Registry.getInstance().get('themeModule').api,

@ -3,7 +3,7 @@
var CONFIG_FILE = '.remix.config' var CONFIG_FILE = '.remix.config'
const EventEmitter = require('events') const EventEmitter = require('events')
function Config (storage) { function Config(storage) {
this.items = {} this.items = {}
this.unpersistedItems = {} this.unpersistedItems = {}
this.events = new EventEmitter() this.events = new EventEmitter()
@ -15,7 +15,7 @@ function Config (storage) {
this.items = JSON.parse(config) this.items = JSON.parse(config)
} }
} catch (exception) { } catch (exception) {
/* Do nothing. */ /* Do nothing. */
} }
this.exists = function (key) { this.exists = function (key) {
@ -30,9 +30,10 @@ function Config (storage) {
this.items[key] = content this.items[key] = content
try { try {
storage.set(CONFIG_FILE, JSON.stringify(this.items)) storage.set(CONFIG_FILE, JSON.stringify(this.items))
this.events.emit('configChanged', { key, content })
this.events.emit(key + '_changed', content) this.events.emit(key + '_changed', content)
} catch (exception) { } catch (exception) {
/* Do nothing. */ /* Do nothing. */
} }
} }

@ -14,7 +14,7 @@ const requiredModules = [ // services + layout views + system views
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter'] 'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter']
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd) // dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
const dependentModules = ['hardhat', 'truffle', 'slither'] const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither']
const sensitiveCalls = { const sensitiveCalls = {
'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'], 'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'],
@ -93,7 +93,7 @@ export class RemixAppManager extends PluginManager {
} }
onPluginActivated(plugin) { onPluginActivated(plugin) {
this.pluginLoader.set(plugin, this.actives) this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin)))
this.event.emit('activate', plugin) this.event.emit('activate', plugin)
this.emit('activate', plugin) this.emit('activate', plugin)
if (!requiredModules.includes(plugin.name)) _paq.push(['trackEvent', 'pluginManager', 'activate', plugin.name]) if (!requiredModules.includes(plugin.name)) _paq.push(['trackEvent', 'pluginManager', 'activate', plugin.name])
@ -110,7 +110,7 @@ export class RemixAppManager extends PluginManager {
} }
onPluginDeactivated(plugin) { onPluginDeactivated(plugin) {
this.pluginLoader.set(plugin, this.actives) this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin)))
this.event.emit('deactivate', plugin) this.event.emit('deactivate', plugin)
_paq.push(['trackEvent', 'pluginManager', 'deactivate', plugin.name]) _paq.push(['trackEvent', 'pluginManager', 'deactivate', plugin.name])
} }

@ -258,6 +258,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
// if no nodes exits at position, try to get the block of which the position is in // if no nodes exits at position, try to get the block of which the position is in
const block = await this.props.plugin.call('codeParser', 'getANTLRBlockAtPosition', cursorPosition, null) const block = await this.props.plugin.call('codeParser', 'getANTLRBlockAtPosition', cursorPosition, null)
const fileNodes = await this.props.plugin.call('codeParser', 'getCurrentFileNodes') const fileNodes = await this.props.plugin.call('codeParser', 'getCurrentFileNodes')
if (!nodesAtPosition.length) { if (!nodesAtPosition.length) {
if (block) { if (block) {
nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', block.start) nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', block.start)
@ -274,7 +275,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
nodes = [...Object.values(contractNodes.baseNodesWithBaseContractScope), ...nodes] nodes = [...Object.values(contractNodes.baseNodesWithBaseContractScope), ...nodes]
nodes = [...Object.values(fileNodes.imports), ...nodes] nodes = [...Object.values(fileNodes.imports), ...nodes]
// add the nodes at the block itself // add the nodes at the block itself
if (node.nodeType === 'ContractDefinition' && block && block.name) { if (block && block.name) {
const contractNodes = fileNodes.contracts[node.name].contractNodes const contractNodes = fileNodes.contracts[node.name].contractNodes
for (const contractNode of Object.values(contractNodes)) { for (const contractNode of Object.values(contractNodes)) {
if (contractNode['name'] === block.name if (contractNode['name'] === block.name
@ -288,6 +289,14 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
} }
} }
} }
} else { // we use the block info from the nodesAtPosition
const contractNodes = fileNodes.contracts[node.name].contractNodes
for (const contractNode of Object.values(contractNodes)) {
if((contractNode as any).nodeType === 'Block'){
const nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode as any).id)
nodes = [...nodes, ...nodeOfScope]
}
}
} }
// filter private nodes, only allow them when contract ID is the same as the current contract // filter private nodes, only allow them when contract ID is the same as the current contract
nodes = nodes.filter(node => { nodes = nodes.filter(node => {

@ -244,6 +244,18 @@ export const getContext = (plugin: RunTab) => {
return plugin.blockchain.context() return plugin.blockchain.context()
} }
export const syncContractsInternal = async (plugin: RunTab) => {
if (await plugin.call('manager', 'isActive', 'truffle')) {
plugin.call('truffle', 'sync')
}
if (await plugin.call('manager', 'isActive', 'hardhat')) {
plugin.call('hardhat', 'sync')
}
if (await plugin.call('manager', 'isActive', 'foundry')) {
plugin.call('foundry', 'sync')
}
}
export const runTransactions = ( export const runTransactions = (
plugin: RunTab, plugin: RunTab,
dispatch: React.Dispatch<any>, dispatch: React.Dispatch<any>,

@ -6,7 +6,7 @@ import { createNewBlockchainAccount, fillAccountsList, setExecutionContext, sign
import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt, import { clearInstances, clearPopUp, removeInstance, setAccount, setGasFee, setMatchPassphrasePrompt,
setNetworkNameFromProvider, setPassphrasePrompt, setSelectedContract, setSendTransactionValue, setUnit, setNetworkNameFromProvider, setPassphrasePrompt, setSelectedContract, setSendTransactionValue, setUnit,
updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath } from './actions' updateBaseFeePerGas, updateConfirmSettings, updateGasPrice, updateGasPriceStatus, updateMaxFee, updateMaxPriorityFee, updateScenarioPath } from './actions'
import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions, updateInstanceBalance } from './deploy' import { createInstance, getContext, getFuncABIInputs, getSelectedContract, loadAddress, runTransactions, updateInstanceBalance, syncContractsInternal } from './deploy'
import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts' import { CompilerAbstract as CompilerAbstractType } from '@remix-project/remix-solidity-ts'
import { ContractData, FuncABI } from "@remix-project/core-plugin" import { ContractData, FuncABI } from "@remix-project/core-plugin"
import { DeployMode, MainnetPrompt } from '../types' import { DeployMode, MainnetPrompt } from '../types'
@ -61,3 +61,4 @@ export const setScenarioPath = (path: string) => updateScenarioPath(dispatch, pa
export const getFuncABIValues = (funcABI: FuncABI) => getFuncABIInputs(plugin, funcABI) export const getFuncABIValues = (funcABI: FuncABI) => getFuncABIInputs(plugin, funcABI)
export const setNetworkName = (networkName: string) => setNetworkNameFromProvider(dispatch, networkName) export const setNetworkName = (networkName: string) => setNetworkNameFromProvider(dispatch, networkName)
export const updateSelectedContract = (contractName) => setSelectedContract(dispatch, contractName) export const updateSelectedContract = (contractName) => setSelectedContract(dispatch, contractName)
export const syncContracts = () => syncContractsInternal(plugin)

@ -6,6 +6,7 @@ import { ContractData, FuncABI } from '@remix-project/core-plugin'
import * as ethJSUtil from 'ethereumjs-util' import * as ethJSUtil from 'ethereumjs-util'
import { ContractGUI } from './contractGUI' import { ContractGUI } from './contractGUI'
import { deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper' import { deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export function ContractDropdownUI (props: ContractDropdownProps) { export function ContractDropdownUI (props: ContractDropdownProps) {
const intl = useIntl() const intl = useIntl()
@ -100,6 +101,15 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
initSelectedContract() initSelectedContract()
}, [contractList]) }, [contractList])
useEffect(() => {
// if the file change the ui is already feed with another bunch of contracts.
// we also need to update the state
const contracts = contractList[currentFile]
if (contracts && contracts.length > 0) {
props.setSelectedContract(contracts[0].alias)
}
}, [currentFile])
const initSelectedContract = () => { const initSelectedContract = () => {
const contracts = contractList[currentFile] const contracts = contractList[currentFile]
@ -230,10 +240,23 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
return ( return (
<div className="udapp_container" data-id="contractDropdownContainer"> <div className="udapp_container" data-id="contractDropdownContainer">
<div className='d-flex justify-content-between'> <div className='d-flex justify-content-between'>
<label className="udapp_settingsLabel"> <div className="d-flex justify-content-between align-items-end">
<FormattedMessage id='udapp.contract' defaultMessage='Contract' /> <label className="udapp_settingsLabel pr-1">
</label> <FormattedMessage id='udapp.contract' defaultMessage='Contract' />
{ Object.keys(props.contracts.contractList).length > 0 && compilationSource !== '' && <label data-id="udappCompiledBy">Compiled by {compilationSource} </label> } </label>
<div className="d-flex">{ Object.keys(props.contracts.contractList).length > 0 && compilationSource !== '' && <label className="text-capitalize" style={{maxHeight: '0.6rem', lineHeight: '1rem'}} data-id="udappCompiledBy">(Compiled by {compilationSource})</label>}</div>
</div>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-sync-compiled-contract">
<div>Click here to import contracts compiled from an external framework.</div>
<div>This action is enabled when Remix is connected to an external framework (hardhat, truffle, foundry) through remixd.</div>
</Tooltip>
}>
<button className="btn d-flex py-0" onClick={_ => props.syncContracts()}>
<i style={{ cursor: 'pointer' }} className="fa fa-refresh mr-2 mt-2" aria-hidden="true"></i>
<label data-id="" className="mt-2">HardHat</label>
</button>
</OverlayTrigger>
</div> </div>
<div className="udapp_subcontainer"> <div className="udapp_subcontainer">
<select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} title={contractOptions.title} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}> <select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} title={contractOptions.title} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}>

@ -26,7 +26,8 @@ import {
executeTransactions, loadFromAddress, executeTransactions, loadFromAddress,
storeNewScenario, runScenario, storeNewScenario, runScenario,
setScenarioPath, getFuncABIValues, setScenarioPath, getFuncABIValues,
setNetworkName, updateSelectedContract setNetworkName, updateSelectedContract,
syncContracts
} from './actions' } from './actions'
import './css/run-tab.css' import './css/run-tab.css'
import { PublishToStorage } from '@remix-ui/publish-to-storage' import { PublishToStorage } from '@remix-ui/publish-to-storage'
@ -221,6 +222,7 @@ export function RunTabUI (props: RunTabProps) {
passphrase={runTab.passphrase} passphrase={runTab.passphrase}
/> />
<ContractDropdownUI <ContractDropdownUI
syncContracts={syncContracts}
exEnvironment={runTab.selectExEnv} exEnvironment={runTab.selectExEnv}
contracts={runTab.contracts} contracts={runTab.contracts}
getSelectedContract={fetchSelectedContract} getSelectedContract={fetchSelectedContract}

@ -139,6 +139,7 @@ export interface ContractDropdownProps {
isSuccessful: boolean, isSuccessful: boolean,
error: string error: string
}, },
syncContracts: () => void,
getSelectedContract: (contractName: string, compiler: CompilerAbstract) => ContractData, getSelectedContract: (contractName: string, compiler: CompilerAbstract) => ContractData,
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
passphrase: string, passphrase: string,

@ -36,6 +36,7 @@ export class RunTab extends ViewPlugin {
onReady(api: any): void; onReady(api: any): void;
onInitDone(): void; onInitDone(): void;
recorder: Recorder; recorder: Recorder;
// syncContracts(): void
} }
import { ViewPlugin } from "@remixproject/engine-web/lib/view"; import { ViewPlugin } from "@remixproject/engine-web/lib/view";
import { Blockchain } from "./blockchain"; import { Blockchain } from "./blockchain";

@ -53,7 +53,12 @@ export const createWorkspace = async (workspaceName: string, workspaceTemplateNa
await plugin.setWorkspaces(await getWorkspaces()) await plugin.setWorkspaces(await getWorkspaces())
await plugin.workspaceCreated(workspaceName) await plugin.workspaceCreated(workspaceName)
if (isGitRepo) await plugin.call('dGitProvider', 'init') if (isGitRepo) {
await plugin.call('dGitProvider', 'init')
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
}
if (!isEmpty) await loadWorkspacePreset(workspaceTemplateName, opts) if (!isEmpty) await loadWorkspacePreset(workspaceTemplateName, opts)
cb && cb(null, workspaceName) cb && cb(null, workspaceName)

@ -17,8 +17,11 @@ export function Workspace () {
const [selectedWorkspace, setSelectedWorkspace] = useState<{ name: string, isGitRepo: boolean}>(null) const [selectedWorkspace, setSelectedWorkspace] = useState<{ name: string, isGitRepo: boolean}>(null)
const [showDropdown, setShowDropdown] = useState<boolean>(false) const [showDropdown, setShowDropdown] = useState<boolean>(false)
const displayOzCustomRef = useRef<HTMLDivElement>() const displayOzCustomRef = useRef<HTMLDivElement>()
const ozFeatures = useRef({mintable: false, burnable: false, pausable: false}) const mintableCheckboxRef = useRef()
const upgradeable = useRef() const burnableCheckboxRef = useRef()
const pausableCheckboxRef = useRef()
const transparentRadioRef = useRef()
const uupsRadioRef = useRef()
const global = useContext(FileSystemContext) const global = useContext(FileSystemContext)
const workspaceRenameInput = useRef() const workspaceRenameInput = useRef()
const workspaceCreateInput = useRef() const workspaceCreateInput = useRef()
@ -127,10 +130,15 @@ export function Workspace () {
// @ts-ignore: Object is possibly 'null'. // @ts-ignore: Object is possibly 'null'.
const workspaceTemplateName = workspaceCreateTemplateInput.current.value || 'remixDefault' const workspaceTemplateName = workspaceCreateTemplateInput.current.value || 'remixDefault'
const initGitRepo = initGitRepoRef.current.checked const initGitRepo = initGitRepoRef.current.checked
const features = ozFeatures.current
const opts = { const opts = {
upgradeable: upgradeable.current, // @ts-ignore: Object is possibly 'null'.
features mintable: mintableCheckboxRef.current.checked,
// @ts-ignore: Object is possibly 'null'.
burnable: burnableCheckboxRef.current.checked,
// @ts-ignore: Object is possibly 'null'.
pausable: pausableCheckboxRef.current.checked,
// @ts-ignore: Object is possibly 'null'.
upgradeable: transparentRadioRef.current.checked ? transparentRadioRef.current.value : ( uupsRadioRef.current.checked ? uupsRadioRef.current.value : false )
} }
try { try {
@ -169,8 +177,16 @@ export function Workspace () {
// @ts-ignore // @ts-ignore
if (workspaceCreateTemplateInput.current.value.startsWith('oz') && displayOzCustomRef && displayOzCustomRef.current) { if (workspaceCreateTemplateInput.current.value.startsWith('oz') && displayOzCustomRef && displayOzCustomRef.current) {
displayOzCustomRef.current.style.display = 'block' displayOzCustomRef.current.style.display = 'block'
upgradeable.current = undefined // @ts-ignore
ozFeatures.current = {mintable: false, burnable: false, pausable: false} mintableCheckboxRef.current.checked = false
// @ts-ignore
burnableCheckboxRef.current.checked = false
// @ts-ignore
pausableCheckboxRef.current.checked = false
// @ts-ignore
transparentRadioRef.current.checked = false
// @ts-ignore
uupsRadioRef.current.checked = false
} else displayOzCustomRef.current.style.display = 'none' } else displayOzCustomRef.current.style.display = 'none'
// @ts-ignore // @ts-ignore
@ -191,18 +207,11 @@ export function Workspace () {
setShowDropdown(isOpen) setShowDropdown(isOpen)
} }
const handleUpgradeability = (e) => { const handleUpgradeability = () => {
// @ts-ignore
upgradeable.current = e.target.value
// @ts-ignore // @ts-ignore
workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value + '_upgradeable'}_${Date.now()}` workspaceCreateInput.current.value = `${workspaceCreateTemplateInput.current.value + '_upgradeable'}_${Date.now()}`
} }
const handleFeatures = (e) => {
// @ts-ignore
ozFeatures.current[e.target.value] = e.target.checked
}
const createModalMessage = () => { const createModalMessage = () => {
return ( return (
<> <>
@ -226,29 +235,29 @@ export function Workspace () {
<label className="form-check-label d-block mb-2" style={{fontWeight: "bolder"}}>Customize template</label> <label className="form-check-label d-block mb-2" style={{fontWeight: "bolder"}}>Customize template</label>
<label id="wsName" className="form-check-label d-block mb-1">Features</label> <label id="wsName" className="form-check-label d-block mb-1">Features</label>
<div className="mb-2" onChange={(e) => handleFeatures(e)}> <div className="mb-2">
<div className="d-flex ml-2 custom-control custom-checkbox"> <div className="d-flex ml-2 custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="feature" value="mintable" id="mintable" /> <input className="custom-control-input" type="checkbox" name="feature" value="mintable" id="mintable" ref={mintableCheckboxRef} />
<label className="form-check-label custom-control-label" htmlFor="mintable" data-id="featureTypeMintable" >Mintable</label> <label className="form-check-label custom-control-label" htmlFor="mintable" data-id="featureTypeMintable" >Mintable</label>
</div> </div>
<div className="d-flex ml-2 custom-control custom-checkbox"> <div className="d-flex ml-2 custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="feature" value="burnable" id="burnable" /> <input className="custom-control-input" type="checkbox" name="feature" value="burnable" id="burnable" ref={burnableCheckboxRef} />
<label className="form-check-label custom-control-label" htmlFor="burnable" data-id="featureTypeBurnable" >Burnable</label> <label className="form-check-label custom-control-label" htmlFor="burnable" data-id="featureTypeBurnable" >Burnable</label>
</div> </div>
<div className="d-flex ml-2 custom-control custom-checkbox"> <div className="d-flex ml-2 custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="feature" value="pausable" id="pausable" /> <input className="custom-control-input" type="checkbox" name="feature" value="pausable" id="pausable" ref={pausableCheckboxRef} />
<label className="form-check-label custom-control-label" htmlFor="pausable" data-id="featureTypePausable" >Pausable</label> <label className="form-check-label custom-control-label" htmlFor="pausable" data-id="featureTypePausable" >Pausable</label>
</div> </div>
</div> </div>
<label id="wsName" className="form-check-label d-block mb-1">Upgradeability</label> <label id="wsName" className="form-check-label d-block mb-1">Upgradeability</label>
<div onChange={(e) => handleUpgradeability(e)}> <div onChange={handleUpgradeability}>
<div className="d-flex ml-2 custom-control custom-radio"> <div className="d-flex ml-2 custom-control custom-radio">
<input className="custom-control-input" type="radio" name="upgradeability" value="transparent" id="transparent" /> <input className="custom-control-input" type="radio" name="upgradeability" value="transparent" id="transparent" ref={transparentRadioRef} />
<label className="form-check-label custom-control-label" htmlFor="transparent" data-id="upgradeTypeTransparent" >Transparent</label> <label className="form-check-label custom-control-label" htmlFor="transparent" data-id="upgradeTypeTransparent" >Transparent</label>
</div> </div>
<div className="d-flex ml-2 custom-control custom-radio"> <div className="d-flex ml-2 custom-control custom-radio">
<input className="custom-control-input" type="radio" name="upgradeability" value="uups" id="uups" /> <input className="custom-control-input" type="radio" name="upgradeability" value="uups" id="uups" ref={uupsRadioRef} />
<label className="form-check-label custom-control-label" htmlFor="uups" data-id="upgradeTypeUups" >UUPS</label> <label className="form-check-label custom-control-label" htmlFor="uups" data-id="upgradeTypeUups" >UUPS</label>
</div> </div>
</div> </div>

@ -1,10 +1,10 @@
import { erc1155 } from '@openzeppelin/wizard'; import { erc1155 } from '@openzeppelin/wizard';
export default async (opts) => { export default async (opts) => {
if (opts.features) { if (opts) {
erc1155.defaults.mintable = opts.features.mintable erc1155.defaults.mintable = opts.mintable
erc1155.defaults.burnable = opts.features.burnable erc1155.defaults.burnable = opts.burnable
erc1155.defaults.pausable = opts.features.pausable erc1155.defaults.pausable = opts.pausable
} }
const filesObj = { const filesObj = {

@ -1,10 +1,10 @@
import { erc20 } from '@openzeppelin/wizard'; import { erc20 } from '@openzeppelin/wizard';
export default async (opts) => { export default async (opts) => {
if (opts.features) { if (opts) {
erc20.defaults.mintable = opts.features.mintable erc20.defaults.mintable = opts.mintable
erc20.defaults.burnable = opts.features.burnable erc20.defaults.burnable = opts.burnable
erc20.defaults.pausable = opts.features.pausable erc20.defaults.pausable = opts.pausable
} }
const filesObj = { const filesObj = {

@ -1,10 +1,10 @@
import { erc721 } from '@openzeppelin/wizard'; import { erc721 } from '@openzeppelin/wizard';
export default async (opts) => { export default async (opts) => {
if (opts.features) { if (opts) {
erc721.defaults.mintable = opts.features.mintable erc721.defaults.mintable = opts.mintable
erc721.defaults.burnable = opts.features.burnable erc721.defaults.burnable = opts.burnable
erc721.defaults.pausable = opts.features.pausable erc721.defaults.pausable = opts.pausable
} }
const filesObj = { const filesObj = {

@ -100,7 +100,7 @@ function errorHandler (error: any, service: string) {
try { try {
startService('folder', (ws: WS, sharedFolderClient: servicesList.Sharedfolder, error: any) => { startService('folder', (ws: WS, sharedFolderClient: servicesList.Sharedfolder, error: any) => {
if (error) { if (error) {
errorHandler(error, 'hardhat') errorHandler(error, 'folder')
return false return false
} }
sharedFolderClient.setWebSocket(ws) sharedFolderClient.setWebSocket(ws)

@ -12,10 +12,11 @@ export class FoundryClient extends PluginClient {
currentSharedFolder: string currentSharedFolder: string
watcher: chokidar.FSWatcher watcher: chokidar.FSWatcher
warnlog: boolean warnlog: boolean
buildPath: string
constructor (private readOnly = false) { constructor (private readOnly = false) {
super() super()
this.methods = ['compile'] this.methods = ['compile', 'sync']
} }
setWebSocket (websocket: WS): void { setWebSocket (websocket: WS): void {
@ -28,6 +29,7 @@ export class FoundryClient extends PluginClient {
sharedFolder (currentSharedFolder: string): void { sharedFolder (currentSharedFolder: string): void {
this.currentSharedFolder = currentSharedFolder this.currentSharedFolder = currentSharedFolder
this.buildPath = utils.absolutePath('out', this.currentSharedFolder)
this.listenOnFoundryCompilation() this.listenOnFoundryCompilation()
} }
@ -58,33 +60,38 @@ export class FoundryClient extends PluginClient {
}) })
} }
listenOnFoundryCompilation () { private async processArtifact () {
try { const folderFiles = await fs.readdir(this.buildPath) // "out" folder
const buildPath = utils.absolutePath('out', this.currentSharedFolder) // name of folders are file names
this.watcher = chokidar.watch(buildPath, { depth: 3, ignorePermissionErrors: true, ignoreInitial: true }) for (const file of folderFiles) {
const path = join(this.buildPath, file) // out/Counter.sol/
const compilationResult = { const compilationResult = {
input: {}, input: {},
output: { output: {
contracts: {}, contracts: {},
sources: {} sources: {}
}, },
solcVersion: null solcVersion: null,
compilationTarget: null
} }
const processArtifact = async () => { await this.readContract(path, compilationResult)
const folderFiles = await fs.readdir(buildPath) this.emit('compilationFinished', compilationResult.compilationTarget, { sources: compilationResult.input } , 'soljson', compilationResult.output, compilationResult.solcVersion)
// name of folders are file names }
for (const file of folderFiles) { if (!this.warnlog) {
await this.readContract(join(buildPath, file), compilationResult) // @ts-ignore
} this.call('terminal', 'log', 'receiving compilation result from foundry')
if (!this.warnlog) { this.warnlog = true
// @ts-ignore }
this.call('terminal', 'log', 'receiving compilation result from foundry') }
this.warnlog = true
} listenOnFoundryCompilation () {
this.emit('compilationFinished', '', { sources: compilationResult.input } , 'soljson', compilationResult.output, compilationResult.solcVersion) try {
} this.watcher = chokidar.watch(this.buildPath, { depth: 3, ignorePermissionErrors: true, ignoreInitial: true })
this.watcher.on('change', async (f: string) => processArtifact())
this.watcher.on('add', async (f: string) => processArtifact()) this.watcher.on('change', async (f: string) => this.processArtifact())
this.watcher.on('add', async (f: string) => this.processArtifact())
// process the artifact on activation
setTimeout(() => this.processArtifact(), 1000)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
@ -115,6 +122,7 @@ export class FoundryClient extends PluginClient {
} }
} else { } else {
const contractName = basename(path).replace('.json', '') const contractName = basename(path).replace('.json', '')
compilationResultPart.compilationTarget = contentJSON.ast.absolutePath
// extract data // extract data
if (!compilationResultPart.output['sources'][contentJSON.ast.absolutePath]) compilationResultPart.output['sources'][contentJSON.ast.absolutePath] = {} if (!compilationResultPart.output['sources'][contentJSON.ast.absolutePath]) compilationResultPart.output['sources'][contentJSON.ast.absolutePath] = {}
compilationResultPart.output['sources'][contentJSON.ast.absolutePath] = { compilationResultPart.output['sources'][contentJSON.ast.absolutePath] = {
@ -135,4 +143,11 @@ export class FoundryClient extends PluginClient {
} }
} }
} }
async sync () {
console.log('syncing from foundry')
this.processArtifact()
// @ts-ignore
this.call('terminal', 'log', 'synced with foundry')
}
} }

@ -3,6 +3,7 @@ import { PluginClient } from '@remixproject/plugin'
import * as chokidar from 'chokidar' import * as chokidar from 'chokidar'
import * as utils from '../utils' import * as utils from '../utils'
import * as fs from 'fs-extra' import * as fs from 'fs-extra'
import { basename, join } from 'path'
const { spawn } = require('child_process') // eslint-disable-line const { spawn } = require('child_process') // eslint-disable-line
export class HardhatClient extends PluginClient { export class HardhatClient extends PluginClient {
@ -10,23 +11,25 @@ export class HardhatClient extends PluginClient {
websocket: WS websocket: WS
currentSharedFolder: string currentSharedFolder: string
watcher: chokidar.FSWatcher watcher: chokidar.FSWatcher
warnlog: boolean warnLog: boolean
buildPath: string
constructor (private readOnly = false) { constructor (private readOnly = false) {
super() super()
this.methods = ['compile'] this.methods = ['compile', 'sync']
} }
setWebSocket (websocket: WS): void { setWebSocket (websocket: WS): void {
this.websocket = websocket this.websocket = websocket
this.websocket.addEventListener('close', () => { this.websocket.addEventListener('close', () => {
this.warnlog = false this.warnLog = false
if (this.watcher) this.watcher.close() if (this.watcher) this.watcher.close()
}) })
} }
sharedFolder (currentSharedFolder: string): void { sharedFolder (currentSharedFolder: string): void {
this.currentSharedFolder = currentSharedFolder this.currentSharedFolder = currentSharedFolder
this.buildPath = utils.absolutePath('artifacts/contracts', this.currentSharedFolder)
this.listenOnHardhatCompilation() this.listenOnHardhatCompilation()
} }
@ -57,28 +60,90 @@ export class HardhatClient extends PluginClient {
}) })
} }
listenOnHardhatCompilation () { private async processArtifact () {
try { // resolving the files
const buildPath = utils.absolutePath('artifacts/build-info', this.currentSharedFolder) const folderFiles = await fs.readdir(this.buildPath)
this.watcher = chokidar.watch(buildPath, { depth: 0, ignorePermissionErrors: true, ignoreInitial: true }) // name of folders are file names
for (const file of folderFiles) { // ["artifacts/contracts/Greeter.sol/"]
const contractFilePath = join(this.buildPath, file)
const stat = await fs.stat(contractFilePath)
if (!stat.isDirectory()) continue
console.log('pp ', contractFilePath)
const files = await fs.readdir(contractFilePath)
const compilationResult = {
input: {},
output: {
contracts: {},
sources: {}
},
solcVersion: null,
target: null
}
for (const file of files) {
if (file.endsWith('.dbg.json')) { // "artifacts/contracts/Greeter.sol/Greeter.dbg.json"
const stdFile = file.replace('.dbg.json', '.json')
const contentStd = await fs.readFile(join(contractFilePath, stdFile), { encoding: 'utf-8' })
const contentDbg = await fs.readFile(join(contractFilePath, file), { encoding: 'utf-8' })
const jsonDbg = JSON.parse(contentDbg)
const jsonStd = JSON.parse(contentStd)
compilationResult.target = jsonStd.sourceName
const processArtifact = async (path: string) => { // this is the full compilation result
if (path.endsWith('.json')) { console.log('processing hardhat artifact', file)
const path = join(contractFilePath, jsonDbg.buildInfo)
const content = await fs.readFile(path, { encoding: 'utf-8' }) const content = await fs.readFile(path, { encoding: 'utf-8' })
const compilationResult = JSON.parse(content)
if (!this.warnlog) { await this.feedContractArtifactFile(content, compilationResult)
// @ts-ignore }
this.call('terminal', 'log', 'receiving compilation result from hardhat') if (compilationResult.target) {
this.warnlog = true this.emit('compilationFinished', compilationResult.target, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion)
}
this.emit('compilationFinished', '', { sources: compilationResult.input.sources }, 'soljson', compilationResult.output, compilationResult.solcVersion)
} }
} }
}
if (!this.warnLog) {
// @ts-ignore
this.call('terminal', 'log', 'receiving compilation result from hardhat')
this.warnLog = true
}
}
listenOnHardhatCompilation () {
try {
this.watcher = chokidar.watch(this.buildPath, { depth: 1, ignorePermissionErrors: true, ignoreInitial: true })
this.watcher.on('change', (path: string) => processArtifact(path)) this.watcher.on('change', () => this.processArtifact())
this.watcher.on('add', (path: string) => processArtifact(path)) this.watcher.on('add', () => this.processArtifact())
// process the artifact on activation
setTimeout(() => this.processArtifact(), 1000)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
} }
async sync () {
console.log('syncing from hardhat')
this.processArtifact()
// @ts-ignore
this.call('terminal', 'log', 'synced with hardhat')
}
async feedContractArtifactFile (artifactContent, compilationResultPart) {
const contentJSON = JSON.parse(artifactContent)
compilationResultPart.solcVersion = contentJSON.solcVersion
for (const file in contentJSON.input.sources) {
const source = contentJSON.input.sources[file]
const absPath = join(this.currentSharedFolder, file)
if (fs.existsSync(absPath)) { // if not that is a lib
const contentOnDisk = await fs.readFile(absPath, { encoding: 'utf-8' })
if (contentOnDisk === source.content) {
compilationResultPart.input[file] = source
compilationResultPart.output['sources'][file] = contentJSON.output.sources[file]
compilationResultPart.output['contracts'][file] = contentJSON.output.contracts[file]
if (contentJSON.output.errors && contentJSON.output.errors.length) {
compilationResultPart.output['errors'] = contentJSON.output.errors.filter(error => error.sourceLocation.file === file)
}
}
}
}
}
} }

@ -12,10 +12,11 @@ export class TruffleClient extends PluginClient {
currentSharedFolder: string currentSharedFolder: string
watcher: chokidar.FSWatcher watcher: chokidar.FSWatcher
warnLog: boolean warnLog: boolean
buildPath: string
constructor (private readOnly = false) { constructor (private readOnly = false) {
super() super()
this.methods = ['compile'] this.methods = ['compile', 'sync']
} }
setWebSocket (websocket: WS): void { setWebSocket (websocket: WS): void {
@ -28,6 +29,7 @@ export class TruffleClient extends PluginClient {
sharedFolder (currentSharedFolder: string): void { sharedFolder (currentSharedFolder: string): void {
this.currentSharedFolder = currentSharedFolder this.currentSharedFolder = currentSharedFolder
this.buildPath = utils.absolutePath('build/contracts', this.currentSharedFolder)
this.listenOnTruffleCompilation() this.listenOnTruffleCompilation()
} }
@ -58,36 +60,41 @@ export class TruffleClient extends PluginClient {
}) })
} }
listenOnTruffleCompilation () { private async processArtifact () {
try { const folderFiles = await fs.readdir(this.buildPath)
const buildPath = utils.absolutePath('build/contracts', this.currentSharedFolder) // name of folders are file names
this.watcher = chokidar.watch(buildPath, { depth: 3, ignorePermissionErrors: true, ignoreInitial: true }) for (const file of folderFiles) {
const compilationResult = { if (file.endsWith('.json')) {
input: {}, const compilationResult = {
output: { input: {},
contracts: {}, output: {
sources: {} contracts: {},
}, sources: {}
solcVersion: null },
} solcVersion: null,
const processArtifact = async () => { compilationTarget: null
const folderFiles = await fs.readdir(buildPath)
// name of folders are file names
for (const file of folderFiles) {
if (file.endsWith('.json')) {
const content = await fs.readFile(join(buildPath, file), { encoding: 'utf-8' })
await this.feedContractArtifactFile(file, content, compilationResult)
}
} }
if (!this.warnLog) { const content = await fs.readFile(join(this.buildPath, file), { encoding: 'utf-8' })
// @ts-ignore await this.feedContractArtifactFile(file, content, compilationResult)
this.call('terminal', 'log', 'receiving compilation result from truffle') this.emit('compilationFinished', compilationResult.compilationTarget, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion)
this.warnLog = true
}
this.emit('compilationFinished', '', { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion)
} }
this.watcher.on('change', async (f: string) => processArtifact()) }
this.watcher.on('add', async (f: string) => processArtifact()) if (!this.warnLog) {
// @ts-ignore
this.call('terminal', 'log', 'receiving compilation result from truffle')
this.warnLog = true
}
}
listenOnTruffleCompilation () {
try {
this.watcher = chokidar.watch(this.buildPath, { depth: 3, ignorePermissionErrors: true, ignoreInitial: true })
this.watcher.on('change', async (f: string) => this.processArtifact())
this.watcher.on('add', async (f: string) => this.processArtifact())
// process the artifact on activation
setTimeout(() => this.processArtifact(), 1000)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
@ -97,6 +104,7 @@ export class TruffleClient extends PluginClient {
const contentJSON = JSON.parse(content) const contentJSON = JSON.parse(content)
const contractName = basename(path).replace('.json', '') const contractName = basename(path).replace('.json', '')
compilationResultPart.solcVersion = contentJSON.compiler.version compilationResultPart.solcVersion = contentJSON.compiler.version
compilationResultPart.compilationTarget = contentJSON.ast.absolutePath
compilationResultPart.input[path] = { content: contentJSON.source } compilationResultPart.input[path] = { content: contentJSON.source }
// extract data // extract data
const relPath = utils.relativePath(contentJSON.ast.absolutePath, this.currentSharedFolder) const relPath = utils.relativePath(contentJSON.ast.absolutePath, this.currentSharedFolder)
@ -127,4 +135,11 @@ export class TruffleClient extends PluginClient {
} }
} }
} }
async sync () {
console.log('syncing from truffle')
this.processArtifact()
// @ts-ignore
this.call('terminal', 'log', 'synced with truffle')
}
} }

@ -102,7 +102,7 @@
"nightwatch_local_search": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/search.test.js --env=chromeDesktop", "nightwatch_local_search": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/search.test.js --env=chromeDesktop",
"nightwatch_local_providers": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/providers.test.js --env=chromeDesktop", "nightwatch_local_providers": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/providers.test.js --env=chromeDesktop",
"onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint", "onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint",
"remixd": "nx build remixd && chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js -s ./apps/remix-ide/contracts --remix-ide http://127.0.0.1:8080", "remixd": "nx build remixd && chmod +x dist/libs/remixd/src/bin/remixd.js && dist/libs/remixd/src/bin/remixd.js --remix-ide http://127.0.0.1:8080",
"selenium": "selenium-standalone start", "selenium": "selenium-standalone start",
"selenium-install": "selenium-standalone install", "selenium-install": "selenium-standalone install",
"sourcemap": "exorcist --root ../ apps/remix-ide/build/app.js.map > apps/remix-ide/build/app.js", "sourcemap": "exorcist --root ../ apps/remix-ide/build/app.js.map > apps/remix-ide/build/app.js",
@ -219,6 +219,7 @@
"string-similarity": "^4.0.4", "string-similarity": "^4.0.4",
"swarmgw": "^0.3.1", "swarmgw": "^0.3.1",
"time-stamp": "^2.2.0", "time-stamp": "^2.2.0",
"tree-kill": "^1.2.2",
"ts-loader": "^9.2.6", "ts-loader": "^9.2.6",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"web3": "^1.7.5", "web3": "^1.7.5",

@ -22877,7 +22877,7 @@ transform-ast@^2.2.1, transform-ast@^2.4.0:
merge-source-map "1.0.4" merge-source-map "1.0.4"
nanobench "^2.1.1" nanobench "^2.1.1"
tree-kill@1.2.2, tree-kill@~1.2.0: tree-kill@1.2.2, tree-kill@^1.2.2, tree-kill@~1.2.0:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==

Loading…
Cancel
Save