Merge branch 'master' into installSlitherCmd

pull/2450/head
Aniket 3 years ago committed by GitHub
commit 4b1ce9a116
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      .github/workflows/publish-action.yml
  2. 30
      apps/remix-ide-e2e/src/commands/verifyContracts.ts
  3. 38
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  4. 1
      apps/remix-ide-e2e/src/tests/compiler_api.test.ts
  5. 6
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  6. 15
      apps/remix-ide-e2e/src/tests/publishContract.test.ts
  7. 4
      apps/remix-ide-e2e/src/tests/search.test.ts
  8. 8
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  9. 15
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  10. 2
      apps/remix-ide-e2e/src/types/index.d.ts
  11. 10
      apps/remix-ide/ci/publishIpfs
  12. 59
      apps/remix-ide/src/app/editor/editor.js
  13. 4
      apps/remix-ide/src/app/files/fileManager.ts
  14. 3
      apps/remix-ide/src/app/panels/layout.ts
  15. 7
      apps/remix-ide/src/app/panels/tab-proxy.js
  16. 25
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  17. 4
      apps/remix-ide/src/app/tabs/compile-tab.js
  18. 68
      apps/solidity-compiler/src/app/compiler-api.ts
  19. 8
      libs/remix-analyzer/package.json
  20. 6
      libs/remix-astwalker/package.json
  21. 10
      libs/remix-debug/package.json
  22. 4
      libs/remix-lib/package.json
  23. 1
      libs/remix-lib/src/types/ICompilerApi.ts
  24. 6
      libs/remix-simulator/package.json
  25. 2
      libs/remix-simulator/src/methods/blocks.ts
  26. 6
      libs/remix-solidity/package.json
  27. 10
      libs/remix-tests/package.json
  28. 2
      libs/remix-ui/editor/src/lib/remix-plugin-types.ts
  29. 4
      libs/remix-ui/editor/src/lib/web-types.ts
  30. 6
      libs/remix-ui/helper/src/lib/helper-components.tsx
  31. 12
      libs/remix-ui/home-tab/src/lib/components/rssFeed.css
  32. 42
      libs/remix-ui/home-tab/src/lib/components/rssFeed.tsx
  33. 19
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  34. 75
      libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx
  35. 9
      libs/remix-ui/publish-to-storage/src/lib/publishOnSwarm.tsx
  36. 46
      libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx
  37. 2
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  38. 4
      libs/remix-ui/search/src/lib/context/context.tsx
  39. 1
      libs/remix-ui/settings/src/lib/constants.ts
  40. 111
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  41. 9
      libs/remix-ui/settings/src/lib/settingsAction.ts
  42. 128
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  43. 10
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  44. 17
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  45. 4
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  46. 1
      libs/remix-ui/workspace/src/lib/css/remix-ui-workspace.css
  47. 4
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  48. 4
      libs/remix-url-resolver/package.json
  49. 50
      libs/remix-ws-templates/package.json
  50. 23
      libs/remix-ws-templates/src/templates/ozerc20/scripts/ethers-lib.ts
  51. 19
      libs/remix-ws-templates/src/templates/ozerc20/scripts/web3-lib.ts
  52. 23
      libs/remix-ws-templates/src/templates/ozerc721/scripts/ethers-lib.ts
  53. 19
      libs/remix-ws-templates/src/templates/ozerc721/scripts/web3-lib.ts
  54. 21
      libs/remix-ws-templates/src/templates/remixDefault/scripts/ethers-lib.ts
  55. 21
      libs/remix-ws-templates/src/templates/remixDefault/scripts/web3-lib.ts
  56. 23
      libs/remix-ws-templates/src/templates/zeroxErc20/scripts/ethers-lib.ts
  57. 19
      libs/remix-ws-templates/src/templates/zeroxErc20/scripts/web3-lib.ts
  58. 2
      libs/remixd/package.json
  59. 5
      package.json

@ -9,18 +9,20 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- run: yarn install - uses: actions/setup-node@v3
with:
node-version: 14.17.6
- run: yarn install
- run: ls - run: ls
- run: pwd - run: pwd
- run: yarn run downloadsolc_assets - run: yarn run downloadsolc_assets
- run: yarn run build:production - run: yarn run build:production
- run: echo "action_state=$('./apps/remix-ide/ci/publishIpfs')" >> $GITHUB_ENV - run: echo "action_state=$('./apps/remix-ide/ci/publishIpfs' ${{ secrets.IPFS_PROJET_ID }} ${{ secrets.IPFS_PROJECT_SECRET }})" >> $GITHUB_ENV
- uses: mshick/add-pr-comment@v1 - uses: mshick/add-pr-comment@v1
with: with:
message: | message: |
ipfs://${{ env.action_state }} ipfs://${{ env.action_state }}
https://ipfs.remixproject.org/ipfs/${{ env.action_state }} https://remix-project.infura-ipfs.io/ipfs/${{ env.action_state }}
https://gateway.ipfs.io/ipfs/${{ env.action_state }}
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
repo-token-user-login: 'github-actions[bot]' # The user.login for temporary GitHub tokens repo-token-user-login: 'github-actions[bot]' # The user.login for temporary GitHub tokens
allow-repeats: false # This is the default allow-repeats: false # This is the default

@ -2,7 +2,7 @@ import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events' import EventEmitter from 'events'
class VerifyContracts extends EventEmitter { class VerifyContracts extends EventEmitter {
command (this: NightwatchBrowser, compiledContractNames: string[], opts = { wait: 1000, version: null }): NightwatchBrowser { command (this: NightwatchBrowser, compiledContractNames: string[], opts = { wait: 1000, version: null, runs: '200' }): NightwatchBrowser {
this.api.perform((done) => { this.api.perform((done) => {
verifyContracts(this.api, compiledContractNames, opts, () => { verifyContracts(this.api, compiledContractNames, opts, () => {
done() done()
@ -13,13 +13,13 @@ class VerifyContracts extends EventEmitter {
} }
} }
function verifyContracts (browser: NightwatchBrowser, compiledContractNames: string[], opts: { wait: number, version?: string }, callback: VoidFunction) { function verifyContracts (browser: NightwatchBrowser, compiledContractNames: string[], opts: { wait: number, version?: string, runs?: string }, callback: VoidFunction) {
browser browser
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.pause(opts.wait) .pause(opts.wait)
.pause(5000) .pause(5000)
.waitForElementPresent('*[data-id="compiledContracts"] option', 60000) .waitForElementPresent('*[data-id="compiledContracts"] option', 60000)
.perform((done) => { .perform(async (done) => {
if (opts.version) { if (opts.version) {
browser browser
.click('*[data-id="compilation-details"]') .click('*[data-id="compilation-details"]')
@ -36,10 +36,28 @@ function verifyContracts (browser: NightwatchBrowser, compiledContractNames: str
done() done()
callback() callback()
}) })
} else { } if (opts.runs) {
compiledContractNames.forEach((name) => { browser
browser.waitForElementContainsText('[data-id="compiledContracts"]', name, 60000) .click('*[data-id="compilation-details"]')
.waitForElementVisible('*[data-id="remixui_treeviewitem_metadata"]')
.pause(2000)
.click('*[data-id="remixui_treeviewitem_metadata"]')
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemsettings"]')
.pause(2000)
.click('*[data-id="treeViewDivtreeViewItemsettings"]')
.waitForElementVisible('*[data-id="treeViewDivtreeViewItemoptimizer"]')
.click('*[data-id="treeViewDivtreeViewItemoptimizer"]')
.waitForElementVisible('*[data-id="treeViewDivruns"]')
.assert.containsText('*[data-id="treeViewDivruns"]', `${opts.runs}`)
.click('[data-id="workspacesModalDialog-modal-footer-ok-react"]')
.perform(() => {
done()
callback()
}) })
} else {
for (const index in compiledContractNames) {
await browser.waitForElementContainsText('[data-id="compiledContracts"]', compiledContractNames[index], 60000)
}
done() done()
callback() callback()
} }

@ -122,6 +122,24 @@ module.exports = {
}) })
// Test in Udapp UI , treeViewDiv0 shows returned value on method click // Test in Udapp UI , treeViewDiv0 shows returned value on method click
.assert.containsText('*[data-id="treeViewDiv0"]', 'bytes32: winnerName_ 0x48656c6c6f20576f726c64210000000000000000000000000000000000000000') .assert.containsText('*[data-id="treeViewDiv0"]', 'bytes32: winnerName_ 0x48656c6c6f20576f726c64210000000000000000000000000000000000000000')
},
'Compile Ballot using config file': function (browser: NightwatchBrowser) {
browser
.addFile('cf.json', {content: configFile})
.clickLaunchIcon('solidity')
.waitForElementVisible('*[data-id="scConfigExpander"]')
.click('*[data-id="scConfigExpander"]')
.waitForElementVisible('*[data-id="scFileConfiguration"]', 10000)
.click('*[data-id="scFileConfiguration"]')
.waitForElementVisible('*[data-id="scConfigChangeFilePath"]', 10000)
.click('*[data-id="scConfigChangeFilePath"]')
.waitForElementVisible('*[data-id="scConfigFilePathInput"]', 10000)
.clearValue('*[data-id="scConfigFilePathInput"]')
.setValue('*[data-id="scConfigFilePathInput"]', 'cf.json')
.sendKeys('*[data-id$="scConfigFilePathInput"]', browser.Keys.ENTER)
.openFile('Untitled.sol')
.verifyContracts(['Ballot'], {wait: 2000, runs: '300'})
.end() .end()
} }
} }
@ -190,6 +208,7 @@ const stateCheck = {
immutable: false immutable: false
} }
} }
const ballotABI = `[ const ballotABI = `[
{ {
"inputs": [ "inputs": [
@ -356,3 +375,22 @@ const ballotABI = `[
"type": "function" "type": "function"
} }
]` ]`
const configFile = `
{
"language": "Solidity",
"settings": {
"optimizer": {
"enabled": true,
"runs": 300
},
"outputSelection": {
"*": {
"": ["ast"],
"*": ["abi", "metadata", "devdoc", "userdoc", "storageLayout", "evm.legacyAssembly", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "evm.gasEstimates", "evm.assembly"]
}
},
"evmVersion": "byzantium"
}
}
`

@ -168,3 +168,4 @@ contract DoesNotCompile {
function fStackLimit(uint u1, uint u2, uint u3, uint u4, uint u5, uint u6, uint u7, uint u8, uint u9, uint u10, uint u11, uint u12) public { function fStackLimit(uint u1, uint u2, uint u3, uint u4, uint u5, uint u6, uint u7, uint u8, uint u9, uint u10, uint u11, uint u12) public {
} }
}` }`

@ -231,7 +231,6 @@ module.exports = {
'Should get current files #group7': async function (browser: NightwatchBrowser) { 'Should get current files #group7': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'fileManager:readdir', { await clickAndCheckLog(browser, 'fileManager:readdir', {
'compiler_config.json': { isDirectory: false },
contracts: { isDirectory: true }, contracts: { isDirectory: true },
scripts: { isDirectory: true }, scripts: { isDirectory: true },
tests: { isDirectory: true }, tests: { isDirectory: true },
@ -286,15 +285,12 @@ module.exports = {
'Should create empty workspace #group2': async function (browser: NightwatchBrowser) { 'Should create empty workspace #group2': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, ['emptyworkspace', true]) await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, ['emptyworkspace', true])
await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'emptyworkspace', isLocalhost: false, absolutePath: '.workspaces/emptyworkspace' }, null, null) await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'emptyworkspace', isLocalhost: false, absolutePath: '.workspaces/emptyworkspace' }, null, null)
await clickAndCheckLog(browser, 'fileManager:readdir', { await clickAndCheckLog(browser, 'fileManager:readdir', {}, null, '/')
'compiler_config.json': { isDirectory: false }
}, null, '/')
}, },
'Should create workspace #group2': async function (browser: NightwatchBrowser) { 'Should create workspace #group2': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, 'testspace') await clickAndCheckLog(browser, 'filePanel:createWorkspace', null, null, 'testspace')
await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'testspace', isLocalhost: false, absolutePath: '.workspaces/testspace' }, null, null) await clickAndCheckLog(browser, 'filePanel:getCurrentWorkspace', { name: 'testspace', isLocalhost: false, absolutePath: '.workspaces/testspace' }, null, null)
await clickAndCheckLog(browser, 'fileManager:readdir', { await clickAndCheckLog(browser, 'fileManager:readdir', {
'compiler_config.json': { isDirectory: false },
contracts: { isDirectory: true }, contracts: { isDirectory: true },
scripts: { isDirectory: true }, scripts: { isDirectory: true },
tests: { isDirectory: true }, tests: { isDirectory: true },

@ -19,17 +19,22 @@ module.exports = {
.openFile('contracts/3_Ballot.sol') .openFile('contracts/3_Ballot.sol')
.verifyContracts(['Ballot']) .verifyContracts(['Ballot'])
.click('#publishOnIpfs') .click('#publishOnIpfs')
.pause(2000)
.waitForElementVisible('[data-id="publishToStorageModalDialogModalBody-react"]', 60000)
.click('[data-id="publishToStorage-modal-footer-ok-react"]')
.pause(8000) .pause(8000)
.waitForElementVisible('[data-id="publishToStorageModalDialogModalBody-react"]', 60000) .waitForElementVisible('[data-id="publishToStorageModalDialogModalBody-react"]', 60000)
.getText('[data-id="publishToStorageModalDialogModalBody-react"]', (result) => { .getText('[data-id="publishToStorageModalDialogModalBody-react"]', (result) => {
const value = <string>(result.value) const value = <string>(result.value)
browser.perform((done) => { browser.perform((done) => {
if (value.indexOf('Metadata of "ballot" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed') if (value.indexOf('Metadata and sources of "ballot" were published successfully.') === -1) browser.assert.fail('ipfs deploy failed')
done() done()
}) })
}) })
.click('[data-id="publishToStorage-modal-footer-ok-react"]') .click('[data-id="publishToStorage-modal-footer-ok-react"]')
.openFile('ipfs/QmSUodhSvoorFL5m5CNqve8YvmuBpjCq17NbTf4GUX8ydw')
.openFile('ipfs/QmXYUS1ueS22EqNVRaKuZa31EgHLjKZ8uTM8vWhQLxa3pw')
}, },
/* Disableing the test untill refactoring and the new swarm usage /* Disableing the test untill refactoring and the new swarm usage
@ -41,7 +46,7 @@ module.exports = {
const value = <string>(result.value) const value = <string>(result.value)
browser.perform((done) => { browser.perform((done) => {
if (value.indexOf('Metadata of "ballot" was published successfully.') === -1) browser.assert.fail('swarm deploy failed') if (value.indexOf('Metadata and sources of "ballot" were published successfully.') === -1) browser.assert.fail('swarm deploy failed')
if (value.indexOf('bzz') === -1) browser.assert.fail('swarm deploy failed') if (value.indexOf('bzz') === -1) browser.assert.fail('swarm deploy failed')
done() done()
}) })
@ -61,11 +66,13 @@ module.exports = {
.waitForElementVisible('*[data-id="Deploy - transact (not payable)"]') .waitForElementVisible('*[data-id="Deploy - transact (not payable)"]')
.click('*[data-id="Deploy - transact (not payable)"]') .click('*[data-id="Deploy - transact (not payable)"]')
.pause(5000) .pause(5000)
.waitForElementVisible('[data-id="udappModalDialogModalBody-react"]') .waitForElementVisible('[data-id="udappModalDialogModalBody-react"]', 60000)
.modalFooterOKClick('udapp')
.pause(8000)
.getText('[data-id="udappModalDialogModalBody-react"]', (result) => { .getText('[data-id="udappModalDialogModalBody-react"]', (result) => {
const value = typeof result.value === 'string' ? result.value : null const value = typeof result.value === 'string' ? result.value : null
if (value.indexOf('Metadata of "storage" was published successfully.') === -1) browser.assert.fail('ipfs deploy failed') if (value.indexOf('Metadata and sources of "storage" were published successfully.') === -1) browser.assert.fail('ipfs deploy failed')
}) })
.modalFooterOKClick('udapp') .modalFooterOKClick('udapp')
}, },

@ -33,11 +33,11 @@ module.exports = {
.clearValue('*[id="search_include"]').pause(2000) .clearValue('*[id="search_include"]').pause(2000)
.setValue('*[id="search_include"]', '**').sendKeys('*[id="search_include"]', browser.Keys.ENTER).pause(4000) .setValue('*[id="search_include"]', '**').sendKeys('*[id="search_include"]', browser.Keys.ENTER).pause(4000)
.elements('css selector', '.search_plugin_search_line', (res) => { .elements('css selector', '.search_plugin_search_line', (res) => {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 48) Array.isArray(res.value) && browser.assert.equal(res.value.length, 61)
}) })
.setValue('*[id="search_exclude"]', ',contracts/**').sendKeys('*[id="search_exclude"]', browser.Keys.ENTER).pause(4000) .setValue('*[id="search_exclude"]', ',contracts/**').sendKeys('*[id="search_exclude"]', browser.Keys.ENTER).pause(4000)
.elements('css selector', '.search_plugin_search_line', (res) => { .elements('css selector', '.search_plugin_search_line', (res) => {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 42) Array.isArray(res.value) && browser.assert.equal(res.value.length, 55)
}) })
.clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt') .clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt')
.clearValue('*[id="search_exclude"]').setValue('*[id="search_exclude"]', '.*/**/*') .clearValue('*[id="search_exclude"]').setValue('*[id="search_exclude"]', '.*/**/*')

@ -291,7 +291,7 @@ const deployWithEthersJs = `
// 'web3Provider' is a remix global variable object // 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner() const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
let factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); let factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let contract = await factory.deploy(...constructorArgs); let contract = await factory.deploy(...constructorArgs);
@ -320,7 +320,7 @@ describe("Storage with lib", function () {
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', 'contracts/artifacts/Storage.json')) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', 'contracts/artifacts/Storage.json'))
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner() const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
let Storage = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); let Storage = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let storage = await Storage.deploy(); let storage = await Storage.deploy();
console.log('storage contract Address: ' + storage.address); console.log('storage contract Address: ' + storage.address);
await storage.deployed() await storage.deployed()
@ -330,7 +330,7 @@ describe("Storage with lib", function () {
it("test updating and retrieving updated value", async function () { it("test updating and retrieving updated value", async function () {
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', 'contracts/artifacts/Storage.json')) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', 'contracts/artifacts/Storage.json'))
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner() const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
let Storage = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); let Storage = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let storage = await Storage.deploy(); let storage = await Storage.deploy();
await storage.deployed() await storage.deployed()
const setValue = await storage.store(56); const setValue = await storage.store(56);
@ -341,7 +341,7 @@ describe("Storage with lib", function () {
it("fail test updating and retrieving updated value", async function () { it("fail test updating and retrieving updated value", async function () {
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', 'contracts/artifacts/Storage.json')) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', 'contracts/artifacts/Storage.json'))
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner() const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
let Storage = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); let Storage = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let storage = await Storage.deploy(); let storage = await Storage.deploy();
await storage.deployed() await storage.deployed()
const setValue = await storage.store(56); const setValue = await storage.store(56);

@ -68,14 +68,14 @@ module.exports = {
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(2000) .pause(2000)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.pause(100) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { `) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]')
@ -107,8 +107,7 @@ module.exports = {
const fileList = document.querySelector('*[data-id="treeViewUltreeViewMenu"]') const fileList = document.querySelector('*[data-id="treeViewUltreeViewMenu"]')
return fileList.getElementsByTagName('li').length; return fileList.getElementsByTagName('li').length;
}, [], function(result){ }, [], function(result){
// check there are no files in FE except config file browser.assert.equal(result.value, 0, 'Incorrect number of files');
browser.assert.equal(result.value, 1, 'Incorrect number of files');
}); });
}, },
@ -146,14 +145,14 @@ module.exports = {
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(100) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.pause(100) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { `) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]')
@ -194,14 +193,14 @@ module.exports = {
.click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/web3-lib.ts"]')
.pause(100) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => {`) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]') .click('*[data-id="treeViewLitreeViewItemscripts/ethers-lib.ts"]')
.pause(100) .pause(100)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { `) !== -1, browser.assert.ok(content.indexOf(`export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {`) !== -1,
'Incorrect content') 'Incorrect content')
}) })
.assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]') .assert.elementPresent('*[data-id="treeViewLitreeViewItemtests"]')

@ -11,7 +11,7 @@ declare module 'nightwatch' {
testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser, testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser,
setEditorValue(value: string, callback?: () => void): NightwatchBrowser, setEditorValue(value: string, callback?: () => void): NightwatchBrowser,
addFile(name: string, content: NightwatchContractContent): NightwatchBrowser, addFile(name: string, content: NightwatchContractContent): NightwatchBrowser,
verifyContracts(compiledContractNames: string[], opts?: { wait: number, version?: string }): NightwatchBrowser, verifyContracts(compiledContractNames: string[], opts?: { wait: number, version?: string, runs?: string }): NightwatchBrowser,
selectAccount(account?: string): NightwatchBrowser, selectAccount(account?: string): NightwatchBrowser,
clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser, clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser,
testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser, testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser,

@ -5,8 +5,14 @@ const { globSource } = IpfsHttpClient
const folder = process.cwd() + '/dist/apps/remix-ide'; const folder = process.cwd() + '/dist/apps/remix-ide';
(async () => { (async () => {
const host = 'ipfs.remixproject.org' const host = 'ipfs.infura.io'
const ipfs = IpfsHttpClient({ host, port: 443, protocol: 'https' }) const projectId = process.argv[2]
const projectSecret = process.argv[3]
const auth = 'Basic ' + Buffer.from(projectId + ':' + projectSecret).toString('base64')
const ipfs = IpfsHttpClient({ port: 5001, host, protocol: 'https', headers: {
authorization: auth
} })
try { try {
let result = await ipfs.add(globSource(folder, { recursive: true}), { pin: false }) let result = await ipfs.add(globSource(folder, { recursive: true}), { pin: false })
const hash = result.cid.toString() const hash = result.cid.toString()

@ -1,5 +1,6 @@
'use strict' 'use strict'
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import { resolve } from 'path'
import { EditorUI } from '@remix-ui/editor' // eslint-disable-line import { EditorUI } from '@remix-ui/editor' // eslint-disable-line
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
@ -140,16 +141,62 @@ class Editor extends Plugin {
this.on('sidePanel', 'pluginDisabled', (name) => { this.on('sidePanel', 'pluginDisabled', (name) => {
this.clearAllDecorationsFor(name) this.clearAllDecorationsFor(name)
}) })
this.on('fileManager', 'fileClosed', (name) => {
if (name === this.currentFile) {
this.currentFile = null
this.renderComponent()
}
})
this.on('theme', 'themeLoaded', (theme) => { this.on('theme', 'themeLoaded', (theme) => {
this.currentThemeType = theme.quality this.currentThemeType = theme.quality
this.renderComponent() this.renderComponent()
}) })
this.on('fileManager', 'currentFileChanged', async (name) => {
if (name.endsWith('.ts')) {
// extract the import, resolve their content
// and add the imported files to Monaco through the `addModel`
// so Monaco can provide auto completion
let content = await this.call('fileManager', 'readFile', name)
const paths = name.split('/')
paths.pop()
const fromPath = paths.join('/') // get current execution context path
for (const match of content.matchAll(/import\s+.*\s+from\s+(?:"(.*?)"|'(.*?)')/g)) {
let path = match[2]
if (path.startsWith('./') || path.startsWith('../')) path = resolve(fromPath, path)
if (path.startsWith('/')) path = path.substring(1)
if (!path.endsWith('.ts')) path = path + '.ts'
if (await this.call('fileManager', 'exists', path)) {
content = await this.call('fileManager', 'readFile', path)
this.emit('addModel', content, 'typescript', path, false)
}
}
}
})
this.on('fileManager', 'noFileSelected', async () => {
this.currentFile = null
this.renderComponent()
})
this.on('fileManager', 'currentFileChanged', async (name) => {
if (name.endsWith('.ts')) {
// extract the import, resolve their content
// and add the imported files to Monaco through the `addModel`
// so Monaco can provide auto completion
let content = await this.call('fileManager', 'readFile', name)
const paths = name.split('/')
paths.pop()
const fromPath = paths.join('/') // get current execution context path
for (const match of content.matchAll(/import\s+.*\s+from\s+(?:"(.*?)"|'(.*?)')/g)) {
let path = match[2]
if (path.startsWith('./') || path.startsWith('../')) path = resolve(fromPath, path)
if (path.startsWith('/')) path = path.substring(1)
if (!path.endsWith('.ts')) path = path + '.ts'
if (await this.call('fileManager', 'exists', path)) {
content = await this.call('fileManager', 'readFile', path)
this.emit('addModel', content, 'typescript', path, false)
}
}
}
})
this.on('fileManager', 'noFileSelected', async () => {
this.currentFile = null
this.renderComponent()
})
try { try {
this.currentThemeType = (await this.call('theme', 'currentTheme')).quality this.currentThemeType = (await this.call('theme', 'currentTheme')).quality
} catch (e) { } catch (e) {

@ -4,7 +4,7 @@ import * as packageJson from '../../../../../package.json'
import Registry from '../state/registry' import Registry from '../state/registry'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { RemixAppManager } from '../../../../../libs/remix-ui/plugin-manager/src/types' import { RemixAppManager } from '../../../../../libs/remix-ui/plugin-manager/src/types'
import { fileChangedToastMsg, storageFullMessage } from '@remix-ui/helper' import { fileChangedToastMsg, recursivePasteToastMsg, storageFullMessage } from '@remix-ui/helper'
import helper from '../../lib/helper.js' import helper from '../../lib/helper.js'
/* /*
@ -275,7 +275,7 @@ class FileManager extends Plugin {
const provider = this.fileProviderOf(src) const provider = this.fileProviderOf(src)
if (provider.isSubDirectory(src, dest)) { if (provider.isSubDirectory(src, dest)) {
this.call('notification', 'toast', 'File(s) to paste is an ancestor of the destination folder') this.call('notification', 'toast', recursivePasteToastMsg())
} else { } else {
await this.inDepthCopy(src, dest) await this.inDepthCopy(src, dest)
} }

@ -57,9 +57,6 @@ export class Layout extends Plugin {
this.panels.main.active = false this.panels.main.active = false
this.event.emit('change', null) this.event.emit('change', null)
}) })
this.on('tabs', 'tabCountChanged', async count => {
if (!count) await this.call('manager', 'activatePlugin', 'home')
})
this.on('manager', 'activate', (profile: Profile) => { this.on('manager', 'activate', (profile: Profile) => {
switch (profile.name) { switch (profile.name) {
case 'filePanel': case 'filePanel':

@ -283,7 +283,12 @@ export class TabProxy extends Plugin {
delete this._handlers[name] delete this._handlers[name]
let previous = currentFileTab let previous = currentFileTab
this.loadedTabs = this.loadedTabs.filter((tab, index) => { this.loadedTabs = this.loadedTabs.filter((tab, index) => {
if (!previous && tab.name === name) previous = this.loadedTabs[index - 1] if (!previous && tab.name === name) {
if(index - 1 >= 0 && this.loadedTabs[index - 1])
previous = this.loadedTabs[index - 1]
else if (index + 1 && this.loadedTabs[index + 1])
previous = this.loadedTabs[index + 1]
}
return tab.name !== name return tab.name !== name
}) })
this.renderComponent() this.renderComponent()

@ -135,23 +135,28 @@ export class RemixdHandle extends WebsocketPlugin {
function remixdDialog () { function remixdDialog () {
const commandText = 'remixd' const commandText = 'remixd'
const fullCommandText = 'remixd -s <path-to-the-shared-folder> -u <remix-ide-instance-URL>'
return (<> return (<>
<div className=''> <div className=''>
<div className='mb-2 text-break'> <div className='mb-2 text-break'>
Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>.<br/><br/> Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>.
Remixd needs to be running in the background to load the files in localhost workspace. For more info, please check the <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">Remixd documentation</a>.
</div> </div>
<div className='mb-2 text-break'> <div className='mb-2 text-break'>
If you are just looking for the remixd command, here it is: Remixd <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">documentation</a>.
<br></br>
Go to your working directory and then run:
<br></br><b>{commandText}</b>
<CopyToClipboard data-id='remixdCopyCommand' content={commandText}></CopyToClipboard>
</div> </div>
<div className='mb-2 text-break'> <div className='mb-2 text-break'>
When connected, a session will be started between <em>{window.location.origin}</em> and your local file system at <i>ws://127.0.0.1:65520</i>. The remixd command is:
The shared folder will be in the "File Explorers" workspace named "localhost". <br/><b>{commandText}</b>
<br/>Read more about other <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a> </div>
<div className='mb-2 text-break'>
The remixd command without options uses the terminal's current directory as the shared directory and the shared Remix domain can only be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org
</div>
<div className='mb-2 text-break'>
Example command with flags: <br/>
<b>{fullCommandText}</b>
</div>
<div className='mb-2 text-break'>
For info about ports, see <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a>
</div> </div>
<div className='mb-2 text-break'> <div className='mb-2 text-break'>
This feature is still in Alpha. We recommend to keep a backup of the shared folder. This feature is still in Alpha. We recommend to keep a backup of the shared folder.

@ -56,6 +56,10 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
this.renderComponent() this.renderComponent()
} }
onFileRemoved () {
this.renderComponent()
}
onNoFileSelected () { onNoFileSelected () {
this.renderComponent() this.renderComponent()
} }

@ -19,6 +19,7 @@ export const CompilerApiMixin = (Base) => class extends Base {
onCurrentFileChanged: (fileName: string) => void onCurrentFileChanged: (fileName: string) => void
// onResetResults: () => void // onResetResults: () => void
onSetWorkspace: (isLocalhost: boolean, workspaceName: string) => void onSetWorkspace: (isLocalhost: boolean, workspaceName: string) => void
onFileRemoved: (path: string) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onCompilationFinished: (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any> }) => void onCompilationFinished: (compilationDetails: { contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any> }) => void
onSessionSwitched: () => void onSessionSwitched: () => void
@ -240,6 +241,10 @@ export const CompilerApiMixin = (Base) => class extends Base {
if (this.onSetWorkspace) this.onSetWorkspace(workspace.isLocalhost, workspace.name) if (this.onSetWorkspace) this.onSetWorkspace(workspace.isLocalhost, workspace.name)
}) })
this.on('fileManager', 'fileRemoved', (path) => {
if (this.onFileRemoved) this.onFileRemoved(path)
})
this.on('remixd', 'rootFolderChanged', () => { this.on('remixd', 'rootFolderChanged', () => {
this.resetResults() this.resetResults()
if (this.onSetWorkspace) this.onSetWorkspace(true, 'localhost') if (this.onSetWorkspace) this.onSetWorkspace(true, 'localhost')
@ -282,36 +287,34 @@ export const CompilerApiMixin = (Base) => class extends Base {
type: 'warning' type: 'warning'
}) })
} else this.statusChanged({ key: 'succeed', title: 'compilation successful', type: 'success' }) } else this.statusChanged({ key: 'succeed', title: 'compilation successful', type: 'success' })
// Store the contracts
this.compilationDetails.contractsDetails = {}
this.compiler.visitContracts((contract) => {
this.compilationDetails.contractsDetails[contract.name] = parseContracts(
contract.name,
contract.object,
this.compiler.getSource(contract.file)
)
})
} else { } else {
const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + (data.error ? 1 : 0)) const count = (data.errors ? data.errors.filter(error => error.severity === 'error').length : 0 + (data.error ? 1 : 0))
this.statusChanged({ key: count, title: `compilation failed with ${count} error${count > 1 ? 's' : ''}`, type: 'error' }) this.statusChanged({ key: count, title: `compilation failed with ${count} error${count > 1 ? 's' : ''}`, type: 'error' })
} }
// Update contract Selection // Store the contracts and Update contract Selection
this.compilationDetails.contractMap = {} if (success) {
if (success) this.compiler.visitContracts((contract) => { this.compilationDetails.contractMap[contract.name] = contract }) this.compilationDetails = await this.visitsContractApi(source, data)
this.compilationDetails.target = source.target } else {
this.compilationDetails = {
contractMap: {},
contractsDetails: {},
target: source.target
}
}
if (this.onCompilationFinished) this.onCompilationFinished(this.compilationDetails) if (this.onCompilationFinished) this.onCompilationFinished(this.compilationDetails)
// set annotations // set annotations
if (data.errors) { if (data.errors) {
for (const error of data.errors) { for (const error of data.errors) {
let pos = helper.getPositionDetails(error.formattedMessage) let pos = helper.getPositionDetails(error.formattedMessage)
if (pos.errFile) { const file = pos.errFile
if (file) {
pos = { pos = {
row: pos.errLine, row: pos.errLine,
column: pos.errCol, column: pos.errCol,
text: error.formattedMessage, text: error.formattedMessage,
type: error.severity type: error.severity
} }
await this.call('editor', 'addAnnotation', pos, pos.errFile) await this.call('editor', 'addAnnotation', pos, file)
} }
} }
} }
@ -332,11 +335,40 @@ export const CompilerApiMixin = (Base) => class extends Base {
// ctrl+s or command+s // ctrl+s or command+s
if ((e.metaKey || e.ctrlKey) && !e.shiftKey && e.keyCode === 83 && this.currentFile !== '') { if ((e.metaKey || e.ctrlKey) && !e.shiftKey && e.keyCode === 83 && this.currentFile !== '') {
e.preventDefault() e.preventDefault()
if(await this.getAppParameter('hardhat-compilation')) this.compileTabLogic.runCompiler('hardhat') if (this.currentFile && (this.currentFile.endsWith('.sol') || this.currentFile.endsWith('.yul'))) {
else if(await this.getAppParameter('truffle-compilation')) this.compileTabLogic.runCompiler('truffle') if(await this.getAppParameter('hardhat-compilation')) this.compileTabLogic.runCompiler('hardhat')
else this.compileTabLogic.runCompiler(undefined) else if(await this.getAppParameter('truffle-compilation')) this.compileTabLogic.runCompiler('truffle')
else this.compileTabLogic.runCompiler(undefined)
}
} }
} }
window.document.addEventListener('keydown', this.data.eventHandlers.onKeyDown) window.document.addEventListener('keydown', this.data.eventHandlers.onKeyDown)
} }
async visitsContractApi (source, data): Promise<{ contractMap: { file: string } | Record<string, any>, contractsDetails: Record<string, any>, target?: string }> {
return new Promise((resolve) => {
if (!data.contracts || (data.contracts && Object.keys(data.contracts).length === 0)) {
return resolve({
contractMap: {},
contractsDetails: {},
target: source.target
})
}
const contractMap = {}
const contractsDetails = {}
this.compiler.visitContracts((contract) => {
contractMap[contract.name] = contract
contractsDetails[contract.name] = parseContracts(
contract.name,
contract.object,
this.compiler.getSource(contract.file)
)
})
return resolve({
contractMap,
contractsDetails,
target: source.target
})
})
}
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-analyzer", "name": "@remix-project/remix-analyzer",
"version": "0.5.22", "version": "0.5.23",
"description": "Tool to perform static analysis on Solidity smart contracts", "description": "Tool to perform static analysis on Solidity smart contracts",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -22,8 +22,8 @@
"@ethereumjs/block": "^3.5.1", "@ethereumjs/block": "^3.5.1",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-astwalker": "^0.0.43", "@remix-project/remix-astwalker": "^0.0.44",
"@remix-project/remix-lib": "^0.5.13", "@remix-project/remix-lib": "^0.5.14",
"async": "^2.6.2", "async": "^2.6.2",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2", "ethers": "^5.4.2",
@ -52,5 +52,5 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-astwalker", "name": "@remix-project/remix-astwalker",
"version": "0.0.43", "version": "0.0.44",
"description": "Tool to walk through Solidity AST", "description": "Tool to walk through Solidity AST",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
@ -37,7 +37,7 @@
"@ethereumjs/block": "^3.5.1", "@ethereumjs/block": "^3.5.1",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.13", "@remix-project/remix-lib": "^0.5.14",
"@types/tape": "^4.2.33", "@types/tape": "^4.2.33",
"async": "^2.6.2", "async": "^2.6.2",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
@ -54,5 +54,5 @@
"tap-spec": "^5.0.0" "tap-spec": "^5.0.0"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-debug", "name": "@remix-project/remix-debug",
"version": "0.5.13", "version": "0.5.14",
"description": "Tool to debug Ethereum transactions", "description": "Tool to debug Ethereum transactions",
"contributors": [ "contributors": [
{ {
@ -22,9 +22,9 @@
"@ethereumjs/common": "^2.5.0", "@ethereumjs/common": "^2.5.0",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-astwalker": "^0.0.43", "@remix-project/remix-astwalker": "^0.0.44",
"@remix-project/remix-lib": "^0.5.13", "@remix-project/remix-lib": "^0.5.14",
"@remix-project/remix-simulator": "^0.2.13", "@remix-project/remix-simulator": "^0.2.14",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.2", "async": "^2.6.2",
"color-support": "^1.1.3", "color-support": "^1.1.3",
@ -68,5 +68,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-lib", "name": "@remix-project/remix-lib",
"version": "0.5.13", "version": "0.5.14",
"description": "Library to various Remix tools", "description": "Library to various Remix tools",
"contributors": [ "contributors": [
{ {
@ -54,5 +54,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -25,6 +25,7 @@ export interface ICompilerApi {
onCurrentFileChanged: (fileName: string) => void onCurrentFileChanged: (fileName: string) => void
// onResetResults: () => void, // onResetResults: () => void,
onSetWorkspace: (isLocalhost: boolean, workspaceName: string) => void onSetWorkspace: (isLocalhost: boolean, workspaceName: string) => void
onFileRemoved: (path: string) => void
onNoFileSelected: () => void onNoFileSelected: () => void
onCompilationFinished: (contractsDetails: any, contractMap: any) => void onCompilationFinished: (contractsDetails: any, contractMap: any) => void
onSessionSwitched: () => void onSessionSwitched: () => void

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-simulator", "name": "@remix-project/remix-simulator",
"version": "0.2.13", "version": "0.2.14",
"description": "Ethereum IDE and tools for the web", "description": "Ethereum IDE and tools for the web",
"contributors": [ "contributors": [
{ {
@ -18,7 +18,7 @@
"@ethereumjs/common": "^2.5.0", "@ethereumjs/common": "^2.5.0",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.13", "@remix-project/remix-lib": "^0.5.14",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^3.1.0", "async": "^3.1.0",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
@ -67,5 +67,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -40,7 +40,6 @@ export class Blocks {
return cb(new Error('block not found')) return cb(new Error('block not found'))
} }
console.log(block.transactions)
const transactions = block.transactions.map((t) => { const transactions = block.transactions.map((t) => {
const hash = '0x' + t.hash().toString('hex') const hash = '0x' + t.hash().toString('hex')
const tx = this.vmContext.txByHash[hash] const tx = this.vmContext.txByHash[hash]
@ -95,7 +94,6 @@ export class Blocks {
eth_getBlockByHash (payload, cb) { eth_getBlockByHash (payload, cb) {
const block = this.vmContext.blocks[payload.params[0]] const block = this.vmContext.blocks[payload.params[0]]
console.log(block.transactions)
const transactions = block.transactions.map((t) => { const transactions = block.transactions.map((t) => {
const hash = '0x' + t.hash().toString('hex') const hash = '0x' + t.hash().toString('hex')
const tx = this.vmContext.txByHash[hash] const tx = this.vmContext.txByHash[hash]

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-solidity", "name": "@remix-project/remix-solidity",
"version": "0.4.13", "version": "0.5.0",
"description": "Tool to load and run Solidity compiler", "description": "Tool to load and run Solidity compiler",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -18,7 +18,7 @@
"@ethereumjs/block": "^3.5.1", "@ethereumjs/block": "^3.5.1",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.13", "@remix-project/remix-lib": "^0.5.14",
"async": "^2.6.2", "async": "^2.6.2",
"eslint-scope": "^5.0.0", "eslint-scope": "^5.0.0",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
@ -61,5 +61,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-tests", "name": "@remix-project/remix-tests",
"version": "0.2.13", "version": "0.2.14",
"description": "Tool to test Solidity smart contracts", "description": "Tool to test Solidity smart contracts",
"main": "src/index.js", "main": "src/index.js",
"types": "./src/index.d.ts", "types": "./src/index.d.ts",
@ -39,9 +39,9 @@
"@ethereumjs/common": "^2.5.0", "@ethereumjs/common": "^2.5.0",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.13", "@remix-project/remix-lib": "^0.5.14",
"@remix-project/remix-simulator": "^0.2.13", "@remix-project/remix-simulator": "^0.2.14",
"@remix-project/remix-solidity": "^0.4.13", "@remix-project/remix-solidity": "^0.5.0",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.0", "async": "^2.6.0",
"axios": ">=0.21.1", "axios": ">=0.21.1",
@ -79,5 +79,5 @@
"typescript": "^3.3.1" "typescript": "^3.3.1"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -226,7 +226,7 @@ declare interface CondensedCompilationInput {
optimize: boolean optimize: boolean
/** e.g: 0.6.8+commit.0bbfe453 */ /** e.g: 0.6.8+commit.0bbfe453 */
version: string version: string
evmVersion?: 'istanbul' | 'petersburg' | 'constantinople' | 'byzantium' | 'spuriousDragon' | 'tangerineWhistle' | 'homestead' evmVersion?: 'berlin' | 'istanbul' | 'petersburg' | 'constantinople' | 'byzantium' | 'spuriousDragon' | 'tangerineWhistle' | 'homestead'
} }
declare interface ContentImport { declare interface ContentImport {

@ -199,6 +199,10 @@ export const loadTypes = async (monaco) => {
const indexWeb3Personal = await import('raw-loader!web3-eth-personal/types/index.d.ts') const indexWeb3Personal = await import('raw-loader!web3-eth-personal/types/index.d.ts')
monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Personal.default, `file:///node_modules/@types/web3-eth-personal/index.d.ts`) monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Personal.default, `file:///node_modules/@types/web3-eth-personal/index.d.ts`)
// @ts-ignore
const indexWeb3Contract = await import('raw-loader!web3-eth-contract/types/index.d.ts')
monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Contract.default, `file:///node_modules/@types/web3-eth-contract/index.d.ts`)
// @ts-ignore // @ts-ignore
const indexWeb3Net = await import('raw-loader!web3-net/types/index.d.ts') const indexWeb3Net = await import('raw-loader!web3-net/types/index.d.ts')
monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Net.default, `file:///node_modules/@types/web3-net/index.d.ts`) monaco.languages.typescript.typescriptDefaults.addExtraLib(indexWeb3Net.default, `file:///node_modules/@types/web3-net/index.d.ts`)

@ -78,3 +78,9 @@ export const storageFullMessage = () => (
</span> </span>
</div> </div>
) )
export const recursivePasteToastMsg = () => (
<div>
File(s) to paste is an ancestor of the destination folder
</div>
)

@ -0,0 +1,12 @@
.RSSFeed-item img {
width: 100%;
}
.RSSFeed-item .truncate {
max-height: 500px;
overflow: hidden;
}
.RSSFeed-item .more-button {
}

@ -0,0 +1,42 @@
import React, { useState, useEffect } from "react";
import Parser from "rss-parser";
import './rssFeed.css';
interface RSSFeedProps {
feedUrl: string,
maxItems: number,
}
export function RSSFeed({ feedUrl, maxItems }: RSSFeedProps) {
const [feed, setFeed] = useState(null);
useEffect(() => {
const fetchData = async () => {
const parser = new Parser()
const feed = await parser.parseURL(feedUrl);
for (const item of feed.items) {
item.content = item['content:encoded']
item.date = new Date(item.pubDate).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric'
})
}
setFeed(feed);
};
fetchData();
}, [feedUrl]);
return (<>
{feed && feed.items.slice(0, maxItems).map((item: any, index: any) => (
<div className='RSSFeed-item' key={index}>
<a target="_blank" href={item.link}><h3>{item.title}</h3></a>
<p>Author: {item.creator}</p>
<h4>{item.date}</h4>
<div className="truncate" dangerouslySetInnerHTML={{ __html: item.content }} />
<a className="more-button btn mb-3" target="_blank" href={item.link}>READ MORE</a>
<hr></hr>
</div>
))}
</>)
}

@ -5,6 +5,7 @@ import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import PluginButton from './components/pluginButton' // eslint-disable-line import PluginButton from './components/pluginButton' // eslint-disable-line
import { ThemeContext, themes } from './themeContext' import { ThemeContext, themes } from './themeContext'
import { RSSFeed } from './components/rssFeed'
declare global { declare global {
interface Window { interface Window {
_paq: any _paq: any
@ -107,14 +108,8 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
scriptTwitter.src = 'https://platform.twitter.com/widgets.js' scriptTwitter.src = 'https://platform.twitter.com/widgets.js'
scriptTwitter.async = true scriptTwitter.async = true
document.body.appendChild(scriptTwitter) document.body.appendChild(scriptTwitter)
// to retrieve medium publications
const scriptMedium = document.createElement('script')
scriptMedium.src = 'https://www.twilik.com/assets/retainable/rss-embed/retainable-rss-embed.js'
scriptMedium.async = true
document.body.appendChild(scriptMedium)
return () => { return () => {
document.body.removeChild(scriptTwitter) document.body.removeChild(scriptTwitter)
document.body.removeChild(scriptMedium)
} }
}, []) }, [])
@ -340,17 +335,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
> >
<div id="remixIDE_MediumBlock" className="p-2 mx-1 mt-3 mb-0 remixui_home_remixHomeMedia" style={ { maxHeight: maxHeight } }> <div id="remixIDE_MediumBlock" className="p-2 mx-1 mt-3 mb-0 remixui_home_remixHomeMedia" style={ { maxHeight: maxHeight } }>
<div id="medium-widget" className="px-3 remixui_home_media" hidden={state.showMediaPanel !== 'medium'} style={ { maxHeight: '10000px' } }> <div id="medium-widget" className="px-3 remixui_home_media" hidden={state.showMediaPanel !== 'medium'} style={ { maxHeight: '10000px' } }>
<div <RSSFeed feedUrl='https://rss.remixproject.org/' maxItems={10} />
id="retainable-rss-embed"
data-rss="https://medium.com/feed/remix-ide"
data-maxcols="1"
data-layout="grid"
data-poststyle="external"
data-readmore="More..."
data-buttonclass="btn mb-3"
data-offset="-100"
>
</div>
</div> </div>
</div> </div>
<div id="remixIDE_TwitterBlock" className="p-2 mx-1 mt-3 mb-0 remixui_home_remixHomeMedia" hidden={state.showMediaPanel !== 'twitter'} style={ { maxHeight: maxHeight, marginRight: '28px' } } > <div id="remixIDE_TwitterBlock" className="p-2 mx-1 mt-3 mb-0 remixui_home_remixHomeMedia" hidden={state.showMediaPanel !== 'twitter'} style={ { maxHeight: maxHeight, marginRight: '28px' } } >

@ -6,6 +6,7 @@ import { publishToSwarm } from './publishOnSwarm'
export const PublishToStorage = (props: RemixUiPublishToStorageProps) => { export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const { api, storage, contract, resetStorage } = props const { api, storage, contract, resetStorage } = props
const [modalShown, setModalShown] = useState(false)
const [state, setState] = useState({ const [state, setState] = useState({
modal: { modal: {
title: '', title: '',
@ -22,15 +23,13 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const storageService = async () => { const storageService = async () => {
if ((contract.metadata === undefined || contract.metadata.length === 0)) { if ((contract.metadata === undefined || contract.metadata.length === 0)) {
modal('Publish To Storage', 'This contract may be abstract, it may not implement an abstract parent\'s methods completely or it may not invoke an inherited contract\'s constructor correctly.') modal('Publish To Storage', 'This contract may be abstract, it may not implement an abstract parent\'s methods completely or it may not invoke an inherited contract\'s constructor correctly.')
} else { } else {
if (storage === 'swarm') { if (storage === 'swarm') {
try { try {
const result = await publishToSwarm(contract, api) const result = await publishToSwarm(contract, api)
modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded)) modal(`Published ${contract.name}'s Metadata and Sources`, publishMessage(result.uploaded))
// triggered each time there's a new verified publish (means hash correspond)
api.writeFile('swarm/' + result.item.hash, result.item.content)
} catch (err) { } catch (err) {
let parseError = err let parseError = err
try { try {
@ -40,14 +39,32 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
modal('Swarm Publish Failed', publishMessageFailed(storage, parseError)) modal('Swarm Publish Failed', publishMessageFailed(storage, parseError))
} }
} else { } else {
try { if (!api.config.get('settings/ipfs-url') && !modalShown) {
const result = await publishToIPFS(contract, api) modal('IPFS Settings', <div>You have not set your own custom IPFS settings.<br></br>
<br></br>
modal(`Published ${contract.name}'s Metadata`, publishMessage(result.uploaded)) We wont be providing a public endpoint anymore for publishing your contracts to IPFS.<br></br>Instead of that, 4 options are now available:<br></br>
// triggered each time there's a new verified publish (means hash correspond) <br></br>
api.writeFile('ipfs/' + result.item.hash, result.item.content) <ul className='pl-3'>
} catch (err) { <li>
modal('IPFS Publish Failed', publishMessageFailed(storage, err)) DEFAULT OPTION:
Use the public INFURA node. This will not guarantee your data will persist.
</li>
<li>
Use your own INFURA IPFS node. This requires a subscription. <a href='https://infura.io/product/ipfs' target={'_blank'}>Learn more</a>
</li>
<li>
Use any external IPFS which doesnt require any authentification.
</li>
<li>
Use your own local ipfs node (which usually runs under http://localhost:5001)
</li>
</ul>
You can update your IPFS settings in the SETTINGS tab.
<br></br>
Now the default option will be used.
</div>, async () => await ipfs(contract, api))
} else {
await ipfs(contract, api)
} }
} }
} }
@ -58,18 +75,29 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
} }
}, [storage]) }, [storage])
const ipfs = async (contract, api) => {
try {
const result = await publishToIPFS(contract, api)
modal(`Published ${contract.name}'s Metadata and Sources`, publishMessage(result.uploaded))
} catch (err) {
modal('IPFS Publish Failed', publishMessageFailed(storage, err))
}
setModalShown(true)
}
const publishMessage = (uploaded) => ( const publishMessage = (uploaded) => (
<span> Metadata of "{contract.name.toLowerCase()}" was published successfully. <br /> <span> Metadata and sources of "{contract.name.toLowerCase()}" were published successfully. <br />
<pre> <pre>
<div> <div>
{ uploaded.map((value, index) => <div key={index}><b>{ value.filename }</b> : <pre>{ value.output.url }</pre></div>) } {uploaded.map((value, index) => <div key={index}><b>{value.filename}</b> : <pre>{value.output.url}</pre></div>)}
</div> </div>
</pre> </pre>
</span> </span>
) )
const publishMessageFailed = (storage, err) => ( const publishMessageFailed = (storage, err) => (
<span>Failed to publish metadata file to { storage }, please check the { storage } gateways is available. <br /> <span>Failed to publish metadata file and sources to {storage}, please check the {storage} gateways is available. <br />
{err} {err}
</span> </span>
) )
@ -81,7 +109,7 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
resetStorage() resetStorage()
} }
const modal = async (title: string, message: string | JSX.Element) => { const modal = async (title: string, message: string | JSX.Element, okFn: any = () => { }) => {
await setState(prevState => { await setState(prevState => {
return { return {
...prevState, ...prevState,
@ -89,7 +117,8 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
...prevState.modal, ...prevState.modal,
hide: false, hide: false,
message, message,
title title,
okFn
} }
} }
}) })
@ -98,13 +127,13 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
return ( return (
<ModalDialog <ModalDialog
id={props.id || 'publishToStorage'} id={props.id || 'publishToStorage'}
title={ state.modal.title } title={state.modal.title}
message={ state.modal.message } message={state.modal.message}
hide={ state.modal.hide } hide={state.modal.hide}
okLabel='OK' okLabel='OK'
okFn={() => {}} okFn={state.modal.okFn}
handleHide={ handleHideModal }> handleHide={handleHideModal}>
{ (typeof state.modal.message !== 'string') && state.modal.message } {(typeof state.modal.message !== 'string') && state.modal.message}
</ModalDialog> </ModalDialog>
) )
} }

@ -79,7 +79,7 @@ export const publishToSwarm = async (contract, api) => {
// publish the list of sources in order, fail if any failed // publish the list of sources in order, fail if any failed
await Promise.all(sources.map(async (item) => { await Promise.all(sources.map(async (item) => {
try { try {
const result = await swarmVerifiedPublish(beeNodes, postageStampId, item.content, item.hash) const result = await swarmVerifiedPublish(beeNodes, postageStampId, item.content, item.hash, api)
try { try {
item.hash = result.url.match('bzz-raw://(.+)')[1] item.hash = result.url.match('bzz-raw://(.+)')[1]
@ -96,9 +96,9 @@ export const publishToSwarm = async (contract, api) => {
} }
})) }))
const metadataContent = JSON.stringify(metadata) const metadataContent = JSON.stringify(metadata, null, '\t')
try { try {
const result = await swarmVerifiedPublish(beeNodes, postageStampId, metadataContent, '') const result = await swarmVerifiedPublish(beeNodes, postageStampId, metadataContent, '', api)
try { try {
contract.metadataHash = result.url.match('bzz-raw://(.+)')[1] contract.metadataHash = result.url.match('bzz-raw://(.+)')[1]
@ -121,7 +121,7 @@ export const publishToSwarm = async (contract, api) => {
return { uploaded, item } return { uploaded, item }
} }
const swarmVerifiedPublish = async (beeNodes: Bee[], postageStampId: string, content, expectedHash): Promise<Record<string, any>> => { const swarmVerifiedPublish = async (beeNodes: Bee[], postageStampId: string, content, expectedHash, api): Promise<Record<string, any>> => {
try { try {
const results = await uploadToBeeNodes(beeNodes, postageStampId, content) const results = await uploadToBeeNodes(beeNodes, postageStampId, content)
const hash = hashFromResults(results) const hash = hashFromResults(results)
@ -129,6 +129,7 @@ const swarmVerifiedPublish = async (beeNodes: Bee[], postageStampId: string, con
if (expectedHash && hash !== expectedHash) { if (expectedHash && hash !== expectedHash) {
return { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'bzz-raw://' + hash, hash } return { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'bzz-raw://' + hash, hash }
} else { } else {
api.writeFile('swarm/' + hash, content)
return { message: 'ok', url: 'bzz-raw://' + hash, hash } return { message: 'ok', url: 'bzz-raw://' + hash, hash }
} }
} catch (error) { } catch (error) {

@ -1,12 +1,26 @@
import IpfsClient from 'ipfs-mini' import IpfsHttpClient from 'ipfs-http-client'
const ipfsNodes = [
new IpfsClient({ host: 'ipfs.remixproject.org', port: 443, protocol: 'https' }),
new IpfsClient({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' }), let ipfsNodes = []
new IpfsClient({ host: '127.0.0.1', port: 5001, protocol: 'http' })
]
export const publishToIPFS = async (contract, api) => { export const publishToIPFS = async (contract, api) => {
ipfsNodes = [
IpfsHttpClient({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' })
]
if (api.config.get('settings/ipfs-url')) {
const auth = api.config.get('settings/ipfs-project-id') ? 'Basic ' + Buffer.from(api.config.get('settings/ipfs-project-id') + ':' + api.config.get('settings/ipfs-project-secret')).toString('base64') : null
const ipfs = IpfsHttpClient({
host: api.config.get('settings/ipfs-url'),
port: api.config.get('settings/ipfs-port'),
protocol: api.config.get('settings/ipfs-protocol'),
headers: {
Authorization: auth
}
})
ipfsNodes.push(ipfs)
}
// gather list of files to publish // gather list of files to publish
const sources = [] const sources = []
let metadata let metadata
@ -58,13 +72,12 @@ export const publishToIPFS = async (contract, api) => {
console.log(error) console.log(error)
reject(error) reject(error)
}) })
}) })
})) }))
// publish the list of sources in order, fail if any failed // publish the list of sources in order, fail if any failed
await Promise.all(sources.map(async (item) => { await Promise.all(sources.map(async (item) => {
try { try {
const result = await ipfsVerifiedPublish(item.content, item.hash) const result = await ipfsVerifiedPublish(item.content, item.hash, api)
try { try {
item.hash = result.url.match('dweb:/ipfs/(.+)')[1] item.hash = result.url.match('dweb:/ipfs/(.+)')[1]
} catch (e) { } catch (e) {
@ -76,10 +89,10 @@ export const publishToIPFS = async (contract, api) => {
throw new Error(error) throw new Error(error)
} }
})) }))
const metadataContent = JSON.stringify(metadata) const metadataContent = JSON.stringify(metadata, null, '\t')
try { try {
const result = await ipfsVerifiedPublish(metadataContent, '') const result = await ipfsVerifiedPublish(metadataContent, '', api)
try { try {
contract.metadataHash = result.url.match('dweb:/ipfs/(.+)')[1] contract.metadataHash = result.url.match('dweb:/ipfs/(.+)')[1]
@ -101,14 +114,15 @@ export const publishToIPFS = async (contract, api) => {
return { uploaded, item } return { uploaded, item }
} }
const ipfsVerifiedPublish = async (content, expectedHash) => { const ipfsVerifiedPublish = async (content, expectedHash, api) => {
try { try {
const results = await severalGatewaysPush(content) const results = await severalGatewaysPush(content)
const hash: any = (results as any).path
if (expectedHash && results !== expectedHash) { if (expectedHash && hash !== expectedHash) {
return { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'dweb:/ipfs/' + results, hash: results } return { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'dweb:/ipfs/' + hash, hash }
} else { } else {
return { message: 'ok', url: 'dweb:/ipfs/' + results, hash: results } api.writeFile('ipfs/' + hash, content)
return { message: 'ok', url: 'dweb:/ipfs/' + hash, hash }
} }
} catch (error) { } catch (error) {
throw new Error(error) throw new Error(error)

@ -19,7 +19,7 @@ export function EnvironmentUI (props: EnvironmentProps) {
Environment Environment
</label> </label>
<div className="udapp_environment"> <div className="udapp_environment">
<select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" className="form-control udapp_select custom-select" value={props.selectedEnv} onChange={(e) => { handleChangeExEnv(e.target.value) }}> <select id="selectExEnvOptions" data-id="settingsSelectEnvOptions" className="form-control udapp_select custom-select" value={props.selectedEnv || ''} onChange={(e) => { handleChangeExEnv(e.target.value) }}>
{ {
props.providers.providerList.map((provider, index) => props.providers.providerList.map((provider, index) =>
<option id={provider.id} key={index} data-id={provider.dataId} <option id={provider.id} key={index} data-id={provider.dataId}

@ -344,8 +344,10 @@ export const SearchProvider = ({
console.log(e) console.log(e)
} }
} }
setTimeout(async () => {
await fetchWorkspace()
}, 500)
fetchWorkspace()
return () => { return () => {
plugin.off('fileManager', 'fileChanged') plugin.off('fileManager', 'fileChanged')

@ -17,6 +17,7 @@ export const enablePersonalModeText = ' Enable Personal Mode for web3 provider.
export const matomoAnalytics = 'Enable Matomo Analytics. We do not collect personally identifiable information (PII). The info is used to improve the site’s UX & UI. See more about ' export const matomoAnalytics = 'Enable Matomo Analytics. We do not collect personally identifiable information (PII). The info is used to improve the site’s UX & UI. See more about '
export const swarmSettingsTitle = 'Swarm Settings' export const swarmSettingsTitle = 'Swarm Settings'
export const swarmSettingsText = 'Swarm Settings' export const swarmSettingsText = 'Swarm Settings'
export const ipfsSettingsText = 'IPFS Settings'
export const labels = { export const labels = {
'gist': { 'gist': {
'link': gitAccessTokenLink, 'link': gitAccessTokenLink,

@ -1,10 +1,10 @@
import React, { useState, useReducer, useEffect, useCallback } from 'react' // eslint-disable-line import React, { useState, useReducer, useEffect, useCallback } from 'react' // eslint-disable-line
import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import { enablePersonalModeText, ethereunVMText, labels, generateContractMetadataText, matomoAnalytics, textDark, textSecondary, warnText, wordWrapText, swarmSettingsTitle } from './constants' import { enablePersonalModeText, ethereunVMText, labels, generateContractMetadataText, matomoAnalytics, textDark, textSecondary, warnText, wordWrapText, swarmSettingsTitle, ipfsSettingsText } from './constants'
import './remix-ui-settings.css' import './remix-ui-settings.css'
import { ethereumVM, generateContractMetadat, personal, textWrapEventAction, useMatomoAnalytics, saveTokenToast, removeTokenToast, saveSwarmSettingsToast } from './settingsAction' import { ethereumVM, generateContractMetadat, personal, textWrapEventAction, useMatomoAnalytics, saveTokenToast, removeTokenToast, saveSwarmSettingsToast, saveIpfsSettingsToast } from './settingsAction'
import { initialState, toastInitialState, toastReducer, settingReducer } from './settingsReducer' import { initialState, toastInitialState, toastReducer, settingReducer } from './settingsReducer'
import { Toaster } from '@remix-ui/toaster'// eslint-disable-line import { Toaster } from '@remix-ui/toaster'// eslint-disable-line
import { RemixUiThemeModule, ThemeModule} from '@remix-ui/theme-module' import { RemixUiThemeModule, ThemeModule} from '@remix-ui/theme-module'
@ -26,6 +26,12 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const [privateBeeAddress, setPrivateBeeAddress] = useState('') const [privateBeeAddress, setPrivateBeeAddress] = useState('')
const [postageStampId, setPostageStampId] = useState('') const [postageStampId, setPostageStampId] = useState('')
const [resetState, refresh] = useState(0) const [resetState, refresh] = useState(0)
const [ipfsUrl, setipfsUrl] = useState('')
const [ipfsPort, setipfsPort] = useState('')
const [ipfsProtocol, setipfsProtocol] = useState('')
const [ipfsProjectId, setipfsProjectId] = useState('')
const [ipfsProjectSecret, setipfsProjectSecret] = useState('')
const initValue = () => { const initValue = () => {
const metadataConfig = props.config.get('settings/generate-contract-metadata') const metadataConfig = props.config.get('settings/generate-contract-metadata')
@ -59,6 +65,29 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
if (configPostageStampId) { if (configPostageStampId) {
setPostageStampId(configPostageStampId) setPostageStampId(configPostageStampId)
} }
const configipfsUrl = props.config.get('settings/ipfs-url')
if (configipfsUrl) {
setipfsUrl(configipfsUrl)
}
const configipfsPort = props.config.get('settings/ipfs-port')
if (configipfsPort) {
setipfsPort(configipfsPort)
}
const configipfsProtocol = props.config.get('settings/ipfs-protocol')
if (configipfsProtocol) {
setipfsProtocol(configipfsProtocol)
}
const configipfsProjectId = props.config.get('settings/ipfs-project-id')
if (configipfsProjectId) {
setipfsProjectId(configipfsProjectId)
}
const configipfsProjectSecret = props.config.get('settings/ipfs-project-secret')
if (configipfsProjectSecret) {
setipfsProjectSecret(configipfsProjectSecret)
}
}, [themeName, state.message]) }, [themeName, state.message])
useEffect(() => { useEffect(() => {
@ -237,6 +266,83 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
</div> </div>
) )
// ipfs settings
const handleSaveIpfsProjectId = useCallback(
(event) => {
setipfsProjectId(event.target.value)
}
, [ipfsProjectId]
)
const handleSaveIpfsSecret = useCallback(
(event) => {
setipfsProjectSecret(event.target.value)
}
, [ipfsProjectSecret]
)
const handleSaveIpfsUrl = useCallback(
(event) => {
setipfsUrl(event.target.value)
}
, [ipfsUrl]
)
const handleSaveIpfsPort = useCallback(
(event) => {
setipfsPort(event.target.value)
}
, [ipfsPort]
)
const handleSaveIpfsProtocol = useCallback(
(event) => {
setipfsProtocol(event.target.value)
}
, [ipfsProtocol]
)
const saveIpfsSettings = () => {
saveIpfsSettingsToast(props.config, dispatchToast, ipfsUrl, ipfsProtocol, ipfsPort, ipfsProjectId, ipfsProjectSecret)
}
const ipfsSettings = () => (
<div className="border-top">
<div className="card-body pt-3 pb-2">
<h6 className="card-title">{ ipfsSettingsText }</h6>
<div className="pt-2 mb-1"><label>IPFS HOST:</label>
<div className="text-secondary mb-0 h6">
<input placeholder='e.g. ipfs.infura.io' id="settingsIpfsUrl" data-id="settingsIpfsUrl" className="form-control" onChange={handleSaveIpfsUrl} value={ ipfsUrl } />
</div>
</div>
<div className=""><label>IPFS PROTOCOL:</label>
<div className="text-secondary mb-0 h6">
<input placeholder='e.g. https' id="settingsIpfsProtocol" data-id="settingsIpfsProtocol" className="form-control" onChange={handleSaveIpfsProtocol} value={ ipfsProtocol } />
</div>
</div>
<div className=""><label>IPFS PORT:</label>
<div className="text-secondary mb-0 h6">
<input placeholder='e.g. 5001' id="settingsIpfsPort" data-id="settingsIpfsPort" className="form-control" onChange={handleSaveIpfsPort} value={ ipfsPort } />
</div>
</div>
<div className=""><label>IPFS PROJECT ID [ INFURA ]:</label>
<div className="text-secondary mb-0 h6">
<input id="settingsIpfsProjectId" data-id="settingsIpfsProjectId" className="form-control" onChange={handleSaveIpfsProjectId} value={ ipfsProjectId } />
</div>
</div>
<div className=""><label>IPFS PROJECT SECRET [ INFURA ]:</label>
<div className="text-secondary mb-0 h6">
<input id="settingsIpfsProjectSecret" data-id="settingsIpfsProjectSecret" className="form-control" type="password" onChange={handleSaveIpfsSecret} value={ ipfsProjectSecret } />
</div>
</div>
<div className="d-flex justify-content-end pt-2">
<input className="btn btn-sm btn-primary ml-2" id="saveIpfssettings" data-id="settingsTabSaveIpfsSettings" onClick={() => saveIpfsSettings()} value="Save" type="button"></input>
</div>
</div>
</div>)
return ( return (
<div> <div>
{state.message ? <Toaster message= {state.message}/> : null} {state.message ? <Toaster message= {state.message}/> : null}
@ -244,6 +350,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
{token('gist')} {token('gist')}
{token('etherscan')} {token('etherscan')}
{swarmSettings()} {swarmSettings()}
{ipfsSettings()}
<RemixUiThemeModule themeModule={props._deps.themeModule} /> <RemixUiThemeModule themeModule={props._deps.themeModule} />
</div> </div>
) )

@ -56,3 +56,12 @@ export const saveSwarmSettingsToast = (config, dispatch, privateBeeAddress, post
config.set('settings/swarm-postage-stamp-id', postageStampId) config.set('settings/swarm-postage-stamp-id', postageStampId)
dispatch({ type: 'save', payload: { message: 'Swarm settings have been saved' } }) dispatch({ type: 'save', payload: { message: 'Swarm settings have been saved' } })
} }
export const saveIpfsSettingsToast = (config, dispatch, ipfsURL, ipfsProtocol, ipfsPort, ipfsProjectId, ipfsProjectSecret) => {
config.set('settings/ipfs-url', ipfsURL)
config.set('settings/ipfs-protocol', ipfsProtocol)
config.set('settings/ipfs-port', ipfsPort)
config.set('settings/ipfs-project-id', ipfsProjectId)
config.set('settings/ipfs-project-secret', ipfsProjectSecret)
dispatch({ type: 'save', payload: { message: 'IPFS settings have been saved' } })
}

@ -12,6 +12,7 @@ import { CopyToClipboard } from '@remix-ui/clipboard'
import { configFileContent } from './compilerConfiguration' import { configFileContent } from './compilerConfiguration'
import './css/style.css' import './css/style.css'
const defaultPath = "compiler_config.json"
declare global { declare global {
interface Window { interface Window {
@ -22,11 +23,23 @@ declare global {
const _paq = window._paq = window._paq || [] //eslint-disable-line const _paq = window._paq = window._paq || [] //eslint-disable-line
export const CompilerContainer = (props: CompilerContainerProps) => { export const CompilerContainer = (props: CompilerContainerProps) => {
const { api, compileTabLogic, tooltip, modal, compiledFileName, updateCurrentVersion, configurationSettings, isHardhatProject, isTruffleProject, workspaceName } = props // eslint-disable-line const {
api,
compileTabLogic,
tooltip,
modal,
compiledFileName,
updateCurrentVersion,
configurationSettings,
isHardhatProject,
isTruffleProject,
workspaceName,
configFilePath,
setConfigFilePath,
} = props // eslint-disable-line
const [state, setState] = useState({ const [state, setState] = useState({
hideWarnings: false, hideWarnings: false,
autoCompile: false, autoCompile: false,
configFilePath: "compiler_config.json",
useFileConfiguration: false, useFileConfiguration: false,
matomoAutocompileOnce: true, matomoAutocompileOnce: true,
optimize: false, optimize: false,
@ -40,7 +53,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
compiledFileName: '', compiledFileName: '',
includeNightlies: false, includeNightlies: false,
language: 'Solidity', language: 'Solidity',
evmVersion: '' evmVersion: '',
createFileOnce: true
}) })
const [showFilePathInput, setShowFilePathInput] = useState<boolean>(false) const [showFilePathInput, setShowFilePathInput] = useState<boolean>(false)
const [toggleExpander, setToggleExpander] = useState<boolean>(false) const [toggleExpander, setToggleExpander] = useState<boolean>(false)
@ -53,17 +67,27 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState) const [compilerContainer, dispatch] = useReducer(compilerReducer, compilerInitialState)
useEffect(() => { useEffect(() => {
api.setAppParameter('configFilePath', "/compiler_config.json") if (workspaceName) {
api.fileExists("/compiler_config.json").then((exists) => { api.setAppParameter('configFilePath', defaultPath)
if (!exists) createNewConfigFile() if (state.useFileConfiguration) {
else { api.fileExists(defaultPath).then((exists) => {
// what to do? discuss if (!exists && state.useFileConfiguration) createNewConfigFile()
})
} }
}) setShowFilePathInput(false)
api.setAppParameter('configFilePath', "/compiler_config.json") }
setShowFilePathInput(false)
}, [workspaceName]) }, [workspaceName])
useEffect(() => {
if (state.useFileConfiguration) {
api.fileExists(defaultPath).then((exists) => {
if (!exists) createNewConfigFile()
})
setToggleExpander(true)
}
}, [state.useFileConfiguration])
useEffect(() => { useEffect(() => {
const listener = (event) => { const listener = (event) => {
if (configFilePathInput.current !== event.target) { if (configFilePathInput.current !== event.target) {
@ -106,8 +130,10 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const hideWarnings = await api.getAppParameter('hideWarnings') as boolean || false const hideWarnings = await api.getAppParameter('hideWarnings') as boolean || false
const includeNightlies = await api.getAppParameter('includeNightlies') as boolean || false const includeNightlies = await api.getAppParameter('includeNightlies') as boolean || false
const useFileConfiguration = await api.getAppParameter('useFileConfiguration') as boolean || false const useFileConfiguration = await api.getAppParameter('useFileConfiguration') as boolean || false
let configFilePath = await api.getAppParameter('configFilePath') let configFilePathSaved = await api.getAppParameter('configFilePath')
if (!configFilePath || configFilePath == '') configFilePath = "/compiler_config.json" if (!configFilePathSaved || configFilePathSaved == '') configFilePathSaved = defaultPath
setConfigFilePath(configFilePathSaved)
setState(prevState => { setState(prevState => {
const params = api.getCompilerParameters() const params = api.getCompilerParameters()
@ -122,7 +148,6 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
autoCompile: autocompile, autoCompile: autocompile,
includeNightlies: includeNightlies, includeNightlies: includeNightlies,
useFileConfiguration: useFileConfiguration, useFileConfiguration: useFileConfiguration,
configFilePath: configFilePath,
optimize: optimize, optimize: optimize,
runs: runs, runs: runs,
evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default', evmVersion: (evmVersion !== null) && (evmVersion !== 'null') && (evmVersion !== undefined) && (evmVersion !== 'undefined') ? evmVersion : 'default',
@ -181,7 +206,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
useEffect(() => { useEffect(() => {
compileTabLogic.setUseFileConfiguration(state.useFileConfiguration) compileTabLogic.setUseFileConfiguration(state.useFileConfiguration)
if (state.useFileConfiguration) compileTabLogic.setConfigFilePath(state.configFilePath) if (state.useFileConfiguration) compileTabLogic.setConfigFilePath(configFilePath)
}, [state.useFileConfiguration]) }, [state.useFileConfiguration])
useEffect(() => { useEffect(() => {
@ -191,6 +216,16 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
}, [configurationSettings]) }, [configurationSettings])
const toggleConfigType = () => { const toggleConfigType = () => {
if (state.useFileConfiguration)
if (state.createFileOnce) {
api.fileExists(defaultPath).then((exists) => {
if (!exists || state.useFileConfiguration ) createNewConfigFile()
})
setState(prevState => {
return { ...prevState, createFileOnce: false }
})
}
setState(prevState => { setState(prevState => {
api.setAppParameter('useFileConfiguration', !state.useFileConfiguration) api.setAppParameter('useFileConfiguration', !state.useFileConfiguration)
return { ...prevState, useFileConfiguration: !state.useFileConfiguration } return { ...prevState, useFileConfiguration: !state.useFileConfiguration }
@ -198,18 +233,17 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
} }
const openFile = async () => { const openFile = async () => {
api.open(state.configFilePath) api.open(configFilePath)
} }
const createNewConfigFile = async () => { const createNewConfigFile = async () => {
let filePath = configFilePathInput.current && configFilePathInput.current.value !== '' ? configFilePathInput.current.value : state.configFilePath let filePath = configFilePathInput.current && configFilePathInput.current.value !== '' ? configFilePathInput.current.value : configFilePath
if (filePath === '') filePath = defaultPath
if (!filePath.endsWith('.json')) filePath = filePath + '.json' if (!filePath.endsWith('.json')) filePath = filePath + '.json'
await api.writeFile(filePath, configFileContent) await api.writeFile(filePath, configFileContent)
api.setAppParameter('configFilePath', filePath) api.setAppParameter('configFilePath', filePath)
setState(prevState => { setConfigFilePath(filePath)
return { ...prevState, configFilePath: filePath }
})
compileTabLogic.setConfigFilePath(filePath) compileTabLogic.setConfigFilePath(filePath)
setShowFilePathInput(false) setShowFilePathInput(false)
} }
@ -220,9 +254,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
if (await api.fileExists(configFilePathInput.current.value)) { if (await api.fileExists(configFilePathInput.current.value)) {
api.setAppParameter('configFilePath', configFilePathInput.current.value) api.setAppParameter('configFilePath', configFilePathInput.current.value)
setState(prevState => { setConfigFilePath(configFilePathInput.current.value)
return { ...prevState, configFilePath: configFilePathInput.current.value }
})
compileTabLogic.setConfigFilePath(configFilePathInput.current.value) compileTabLogic.setConfigFilePath(configFilePathInput.current.value)
setShowFilePathInput(false) setShowFilePathInput(false)
@ -394,6 +426,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
compileIcon.current.classList.remove('remixui_spinningIcon') compileIcon.current.classList.remove('remixui_spinningIcon')
compileIcon.current.classList.remove('remixui_bouncingIcon') compileIcon.current.classList.remove('remixui_bouncingIcon')
if (!state.autoCompile || (state.autoCompile && state.matomoAutocompileOnce)) { if (!state.autoCompile || (state.autoCompile && state.matomoAutocompileOnce)) {
if (state.useFileConfiguration)
_paq.push(['trackEvent', 'compiler', 'compiled_with_config_file'])
_paq.push(['trackEvent', 'compiler', 'compiled_with_version', _retrieveVersion()]) _paq.push(['trackEvent', 'compiler', 'compiled_with_version', _retrieveVersion()])
if (state.autoCompile && state.matomoAutocompileOnce) { if (state.autoCompile && state.matomoAutocompileOnce) {
setState(prevState => { setState(prevState => {
@ -750,36 +785,38 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div> </div>
<div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio"> <div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
<input className="custom-control-input" type="radio" name="configradio" value="file" onChange={toggleConfigType} checked={state.useFileConfiguration} id="scFileConfig" /> <input className="custom-control-input" type="radio" name="configradio" value="file" onChange={toggleConfigType} checked={state.useFileConfiguration} id="scFileConfig" />
<label className="form-check-label custom-control-label" htmlFor="scFileConfig">Use configuration file</label> <label className="form-check-label custom-control-label" htmlFor="scFileConfig" data-id="scFileConfiguration">Use configuration file</label>
</div> </div>
<div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}> <div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}>
{ (!showFilePathInput && state.useFileConfiguration) && <span { (!showFilePathInput && state.useFileConfiguration) && <span
title="Click to open the config file." title="Click to open the config file."
onClick={openFile} onClick={configFilePath === '' ? () => {} : openFile}
className="py-2 text-primary remixui_compilerConfigPath" className="py-2 remixui_compilerConfigPath"
>{state.configFilePath}</span> } >{configFilePath === '' ? 'No file selected.' : configFilePath}</span> }
{ (!showFilePathInput&& !state.useFileConfiguration) && <span className="py-2 text-secondary">{state.configFilePath}</span> } { (!showFilePathInput && !state.useFileConfiguration) && <span className="py-2 text-secondary">{configFilePath}</span> }
<input <input
ref={configFilePathInput} ref={configFilePathInput}
className={`py-0 my-0 form-control ${showFilePathInput ? "d-flex" : "d-none"}`} className={`py-0 my-0 form-control ${showFilePathInput ? "d-flex" : "d-none"}`}
placeholder={"Enter the new path"} placeholder={"Enter the new path"}
title="If the file you entered does not exist you will be able to create one in the next step." title="If the file you entered does not exist you will be able to create one in the next step."
disabled={!state.useFileConfiguration} disabled={!state.useFileConfiguration}
data-id="scConfigFilePathInput"
onKeyPress={event => { onKeyPress={event => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
handleConfigPathChange() handleConfigPathChange()
} }
}} }}
/> />
{ !showFilePathInput && <button disabled={!state.useFileConfiguration} className="btn-secondary" onClick={() => {setShowFilePathInput(true)}}>Change</button> } { !showFilePathInput && <button disabled={!state.useFileConfiguration} data-id="scConfigChangeFilePath" className="btn-secondary" onClick={() => {setShowFilePathInput(true)}}>Change</button> }
</div> </div>
</div> </div>
<div className="px-4"> <div className="px-4">
<button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block d-block w-100 text-break remixui_disabled mb-1 mt-3" onClick={compile} disabled={disableCompileButton}> <button id="compileBtn" data-id="compilerContainerCompileBtn" className="btn btn-primary btn-block d-block w-100 text-break remixui_disabled mb-1 mt-3" onClick={compile} disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton}>
<OverlayTrigger overlay={ <OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile"> <Tooltip id="overlay-tooltip-compile">
<div className="text-left"> <div className="text-left">
<div><b>Ctrl+S</b> for compiling</div> { !(configFilePath === '' && state.useFileConfiguration) && <div><b>Ctrl+S</b> for compiling</div> }
{ (configFilePath === '' && state.useFileConfiguration) && <div> No config file selected</div> }
</div> </div>
</Tooltip> </Tooltip>
}> }>
@ -790,11 +827,12 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</OverlayTrigger> </OverlayTrigger>
</button> </button>
<div className='d-flex align-items-center'> <div className='d-flex align-items-center'>
<button id="compileAndRunBtn" data-id="compilerContainerCompileAndRunBtn" className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-3" onClick={compileAndRun} disabled={disableCompileButton}> <button id="compileAndRunBtn" data-id="compilerContainerCompileAndRunBtn" className="btn btn-secondary btn-block d-block w-100 text-break remixui_solidityCompileAndRunButton d-inline-block remixui_disabled mb-1 mt-3" onClick={compileAndRun} disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton}>
<OverlayTrigger overlay={ <OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile-run"> <Tooltip id="overlay-tooltip-compile-run">
<div className="text-left"> <div className="text-left">
<div><b>Ctrl+Shift+S</b> for compiling and script execution</div> { !(configFilePath === '' && state.useFileConfiguration) && <div><b>Ctrl+Shift+S</b> for compiling and script execution</div> }
{ (configFilePath === '' && state.useFileConfiguration) && <div> No config file selected</div> }
</div> </div>
</Tooltip> </Tooltip>
}> }>
@ -806,18 +844,18 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<OverlayTrigger overlay={ <OverlayTrigger overlay={
<Tooltip id="overlay-tooltip-compile-run-doc"> <Tooltip id="overlay-tooltip-compile-run-doc">
<div className="text-left p-2"> <div className="text-left p-2">
<div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div> <div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div>
<pre> <pre>
<code> <code>
/**<br /> /**<br />
* @title ContractName<br /> * @title ContractName<br />
* @dev ContractDescription<br /> * @dev ContractDescription<br />
* @custom:dev-run-script file_path<br /> * @custom:dev-run-script file_path<br />
*/<br /> */<br />
contract ContractName {'{}'}<br /> contract ContractName {'{}'}<br />
</code> </code>
</pre> </pre>
Click to know more Click to know more
</div> </div>
</Tooltip> </Tooltip>
}> }>

@ -24,7 +24,7 @@ export class CompileTabLogic {
constructor (public api: ICompilerApi, public contentImport) { constructor (public api: ICompilerApi, public contentImport) {
this.event = new EventEmitter() this.event = new EventEmitter()
this.compiler = new Compiler((url, cb) => api.resolveContentAndSave(url).then((result) => cb(null, result)).catch((error) => cb(error.message))) this.compiler = new Compiler((url, cb) => api.resolveContentAndSave(url).then((result) => cb(null, result)).catch((error) => cb(error.message)))
this.evmVersions = ['default', 'london', 'istanbul', 'petersburg', 'constantinople', 'byzantium', 'spuriousDragon', 'tangerineWhistle', 'homestead'] this.evmVersions = ['default', 'berlin', 'london', 'istanbul', 'petersburg', 'constantinople', 'byzantium', 'spuriousDragon', 'tangerineWhistle', 'homestead']
} }
init () { init () {
@ -106,9 +106,11 @@ export class CompileTabLogic {
const sources = { [target]: { content } } const sources = { [target]: { content } }
this.event.emit('removeAnnotations') this.event.emit('removeAnnotations')
this.event.emit('startingCompilation') this.event.emit('startingCompilation')
this.api.readFile(this.configFilePath).then( contentConfig => { if (this.configFilePath) {
this.compiler.set('configFileContent', contentConfig) this.api.readFile(this.configFilePath).then( contentConfig => {
}) this.compiler.set('configFileContent', contentConfig)
})
}
// setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation') // setTimeout fix the animation on chrome... (animation triggered by 'staringCompilation')
setTimeout(() => { this.compiler.compile(sources, target); resolve(true) }, 100) setTimeout(() => { this.compiler.compile(sources, target); resolve(true) }, 100)
}).catch((error) => { }).catch((error) => {

@ -15,6 +15,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
isTruffleProject: false, isTruffleProject: false,
workspaceName: '', workspaceName: '',
currentFile, currentFile,
configFilePath: 'compiler_config.json',
loading: false, loading: false,
compileTabLogic: null, compileTabLogic: null,
compiler: null, compiler: null,
@ -71,6 +72,13 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
return { ...prevState, currentFile, isHardhatProject: isHardhat, workspaceName: workspaceName, isTruffleProject: isTruffle } return { ...prevState, currentFile, isHardhatProject: isHardhat, workspaceName: workspaceName, isTruffleProject: isTruffle }
}) })
} }
api.onFileRemoved = (path: string) => {
if (path === state.configFilePath)
setState(prevState => {
return { ...prevState, configFilePath: '' }
})
}
api.onNoFileSelected = () => { api.onNoFileSelected = () => {
setState(prevState => { setState(prevState => {
@ -103,6 +111,13 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
setBadgeStatus({ ...badgeStatus, [currentFile]: data }) setBadgeStatus({ ...badgeStatus, [currentFile]: data })
} }
const setConfigFilePath = (path: string) => {
setState(prevState => {
return { ...prevState, configFilePath: path }
})
}
const toast = (message: string) => { const toast = (message: string) => {
setState(prevState => { setState(prevState => {
return { ...prevState, toasterMsg: message } return { ...prevState, toasterMsg: message }
@ -162,6 +177,8 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
compiledFileName={currentFile} compiledFileName={currentFile}
updateCurrentVersion={updateCurrentVersion} updateCurrentVersion={updateCurrentVersion}
configurationSettings={configurationSettings} configurationSettings={configurationSettings}
configFilePath={state.configFilePath}
setConfigFilePath={setConfigFilePath}
/> />
{ contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> } { contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> }
{ compileErrors[currentFile] && { compileErrors[currentFile] &&

@ -16,7 +16,9 @@ export interface CompilerContainerProps {
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,
compiledFileName: string, compiledFileName: string,
updateCurrentVersion: any, updateCurrentVersion: any,
configurationSettings: ConfigurationSettings configurationSettings: ConfigurationSettings,
configFilePath: string,
setConfigFilePath: (path: string) => void
} }
export interface ContractSelectionProps { export interface ContractSelectionProps {
api: ICompilerApi, api: ICompilerApi,

@ -13,6 +13,7 @@
padding-left : 6px; padding-left : 6px;
padding-right : 6px; padding-right : 6px;
padding-top : 6px; padding-top : 6px;
overflow-y : auto;
} }
.remixui_fileExplorerTree { .remixui_fileExplorerTree {
cursor : default; cursor : default;

@ -228,8 +228,8 @@ export function Workspace () {
</div> </div>
</header> </header>
</div> </div>
<div className='remixui_fileExplorerTree'> <div className='h-100 remixui_fileExplorerTree'>
<div> <div className='h-100'>
<div className='pl-2 remixui_treeview' data-id='filePanelFileExplorerTree'> <div className='pl-2 remixui_treeview' data-id='filePanelFileExplorerTree'>
{ (global.fs.mode === 'browser') && (currentWorkspace !== NO_WORKSPACE) && { (global.fs.mode === 'browser') && (currentWorkspace !== NO_WORKSPACE) &&
<FileExplorer <FileExplorer

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-url-resolver", "name": "@remix-project/remix-url-resolver",
"version": "0.0.34", "version": "0.0.35",
"description": "Solidity import url resolver engine", "description": "Solidity import url resolver engine",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -41,5 +41,5 @@
"typescript": "^3.1.6" "typescript": "^3.1.6"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2be5108c51f2ac9226e4598c512ec6d3c63f5c78" "gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
} }

@ -1,23 +1,29 @@
{ {
"name": "@remix-project/remix-ws-templates", "name": "@remix-project/remix-ws-templates",
"version": "1.0.0", "version": "1.0.1",
"description": "Create a Remix IDE workspace using different templates", "description": "Create a Remix IDE workspace using different templates",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/ethereum/remix-project.git" "url": "git+https://github.com/ethereum/remix-project.git"
}, },
"author": "Aniket-Engg", "author": "Aniket-Engg",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/ethereum/remix-project/issues" "url": "https://github.com/ethereum/remix-project/issues"
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-ws-templates#readme" "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-ws-templates#readme",
} "typings": "./src/index.d.ts",
"dependencies": {
"ethers": "^5.4.2",
"web3": "^1.5.1"
},
"gitHead": "3f311aaf25f5796f70711006bb783ee4087ffc71"
}

@ -1,24 +1,27 @@
import { ethers } from 'ethers' import { ethers } from 'ethers'
export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath)) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object // 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let contract const contract = await factory.deploy(...args)
if (from) {
contract = await factory.connect(from).deploy(...args);
} else {
contract = await factory.deploy(...args);
}
// The contract is NOT deployed yet; we must wait until it is mined // The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed() await contract.deployed()

@ -1,8 +1,17 @@
import Web3 from 'web3' import Web3 from 'web3'
import { Contract, ContractSendMethod, Options } from 'web3-eth-contract'
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {string} from account used to send the transaction
* @param {number} gas gas limit
* @return {Options} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {
const web3 = new Web3(window.web3Provider) const web3 = new Web3(web3Provider)
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
@ -12,14 +21,14 @@ export const deploy = async (contractName: string, args: Array<any>, from?: stri
const accounts = await web3.eth.getAccounts() const accounts = await web3.eth.getAccounts()
let contract = new web3.eth.Contract(metadata.abi) const contract: Contract = new web3.eth.Contract(metadata.abi)
contract = contract.deploy({ const contractSend: ContractSendMethod = contract.deploy({
data: metadata.data.bytecode.object, data: metadata.data.bytecode.object,
arguments: args arguments: args
}) })
const newContractInstance = await contract.send({ const newContractInstance = await contractSend.send({
from: from || accounts[0], from: from || accounts[0],
gas: gas || 1500000 gas: gas || 1500000
}) })

@ -1,24 +1,27 @@
import { ethers } from 'ethers' import { ethers } from 'ethers'
export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath)) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object // 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let contract const contract = await factory.deploy(...args)
if (from) {
contract = await factory.connect(from).deploy(...args);
} else {
contract = await factory.deploy(...args);
}
// The contract is NOT deployed yet; we must wait until it is mined // The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed() await contract.deployed()

@ -1,8 +1,17 @@
import Web3 from 'web3' import Web3 from 'web3'
import { Contract, ContractSendMethod, Options } from 'web3-eth-contract'
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {string} from account used to send the transaction
* @param {number} gas gas limit
* @return {Options} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {
const web3 = new Web3(window.web3Provider) const web3 = new Web3(web3Provider)
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
@ -12,14 +21,14 @@ export const deploy = async (contractName: string, args: Array<any>, from?: stri
const accounts = await web3.eth.getAccounts() const accounts = await web3.eth.getAccounts()
let contract = new web3.eth.Contract(metadata.abi) const contract: Contract = new web3.eth.Contract(metadata.abi)
contract = contract.deploy({ const contractSend: ContractSendMethod = contract.deploy({
data: metadata.data.bytecode.object, data: metadata.data.bytecode.object,
arguments: args arguments: args
}) })
const newContractInstance = await contract.send({ const newContractInstance = await contractSend.send({
from: from || accounts[0], from: from || accounts[0],
gas: gas || 1500000 gas: gas || 1500000
}) })

@ -1,6 +1,13 @@
import { ethers } from 'ethers' import { ethers } from 'ethers'
export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
@ -9,16 +16,12 @@ export const deploy = async (contractName: string, args: Array<any>, from?: stri
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath)) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object // 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let contract const contract = await factory.deploy(...args)
if (from) {
contract = await factory.connect(from).deploy(...args);
} else {
contract = await factory.deploy(...args);
}
// The contract is NOT deployed yet; we must wait until it is mined // The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed() await contract.deployed()

@ -1,25 +1,34 @@
import Web3 from 'web3' import Web3 from 'web3'
import { Contract, ContractSendMethod, Options } from 'web3-eth-contract'
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {string} from account used to send the transaction
* @param {number} gas gas limit
* @return {Options} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {
const web3 = new Web3(window.web3Provider) const web3 = new Web3(web3Provider)
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path const artifactsPath = `browser/contracts/artifacts/${contractName}.json`
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath)) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
const accounts = await web3.eth.getAccounts() const accounts = await web3.eth.getAccounts()
let contract = new web3.eth.Contract(metadata.abi) const contract: Contract = new web3.eth.Contract(metadata.abi)
contract = contract.deploy({ const contractSend: ContractSendMethod = contract.deploy({
data: metadata.data.bytecode.object, data: metadata.data.bytecode.object,
arguments: args arguments: args
}) })
const newContractInstance = await contract.send({ const newContractInstance = await contractSend.send({
from: from || accounts[0], from: from || accounts[0],
gas: gas || 1500000 gas: gas || 1500000
}) })

@ -1,24 +1,27 @@
import { ethers } from 'ethers' import { ethers } from 'ethers'
export const deploy = async (contractName: string, args: Array<any>, from?: string): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath)) const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object // 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer); const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
let contract const contract = await factory.deploy(...args)
if (from) {
contract = await factory.connect(from).deploy(...args);
} else {
contract = await factory.deploy(...args);
}
// The contract is NOT deployed yet; we must wait until it is mined // The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed() await contract.deployed()

@ -1,8 +1,17 @@
import Web3 from 'web3' import Web3 from 'web3'
import { Contract, ContractSendMethod, Options } from 'web3-eth-contract'
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<any> => { /**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {string} from account used to send the transaction
* @param {number} gas gas limit
* @return {Options} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {
const web3 = new Web3(window.web3Provider) const web3 = new Web3(web3Provider)
console.log(`deploying ${contractName}`) console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact. // Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated // Make sure contract is compiled and artifacts are generated
@ -12,14 +21,14 @@ export const deploy = async (contractName: string, args: Array<any>, from?: stri
const accounts = await web3.eth.getAccounts() const accounts = await web3.eth.getAccounts()
let contract = new web3.eth.Contract(metadata.abi) const contract: Contract = new web3.eth.Contract(metadata.abi)
contract = contract.deploy({ const contractSend: ContractSendMethod = contract.deploy({
data: metadata.data.bytecode.object, data: metadata.data.bytecode.object,
arguments: args arguments: args
}) })
const newContractInstance = await contract.send({ const newContractInstance = await contractSend.send({
from: from || accounts[0], from: from || accounts[0],
gas: gas || 1500000 gas: gas || 1500000
}) })

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remixd", "name": "@remix-project/remixd",
"version": "0.6.1", "version": "0.6.2",
"description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)", "description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)",
"main": "index.js", "main": "index.js",
"types": "./index.d.ts", "types": "./index.d.ts",

@ -1,6 +1,6 @@
{ {
"name": "remix-project", "name": "remix-project",
"version": "0.24.0-dev", "version": "0.25.0-dev",
"license": "MIT", "license": "MIT",
"description": "Ethereum Remix Monorepo", "description": "Ethereum Remix Monorepo",
"keywords": [ "keywords": [
@ -60,7 +60,7 @@
"minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false", "minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false",
"build:production": "NODE_ENV=production nx build remix-ide --skip-nx-cache", "build:production": "NODE_ENV=production nx build remix-ide --skip-nx-cache",
"serve:production": "npx http-server ./dist/apps/remix-ide", "serve:production": "npx http-server ./dist/apps/remix-ide",
"select_test": "sh apps/remix-ide-e2e/src/select_tests.sh", "select_test": "bash apps/remix-ide-e2e/src/select_tests.sh",
"group_test": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/${npm_config_test}_group${npm_config_group}.test.js --env=${npm_config_env}", "group_test": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/${npm_config_test}_group${npm_config_group}.test.js --env=${npm_config_env}",
"nightwatch_parallel": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=chrome,firefox", "nightwatch_parallel": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=chrome,firefox",
"nightwatch_local_firefox": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=firefox", "nightwatch_local_firefox": "yarn run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js --env=firefox",
@ -198,6 +198,7 @@
"react-draggable": "^4.4.4", "react-draggable": "^4.4.4",
"react-tabs": "^3.2.2", "react-tabs": "^3.2.2",
"regenerator-runtime": "0.13.7", "regenerator-runtime": "0.13.7",
"rss-parser": "^3.12.0",
"selenium": "^2.20.0", "selenium": "^2.20.0",
"signale": "^1.4.0", "signale": "^1.4.0",
"string-similarity": "^4.0.4", "string-similarity": "^4.0.4",

Loading…
Cancel
Save