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

editorcontextDummy
filip mertens 2 years ago
commit c0843e5fae
  1. 4
      .prettierignore
  2. 3
      .prettierrc
  3. 8
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  4. 6
      apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts
  5. 8
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  6. 2
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  7. 3
      apps/remix-ide-e2e/src/tests/providers.test.ts
  8. 136
      apps/remix-ide-e2e/src/tests/recorder.test.ts
  9. 2
      apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
  10. 6
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  11. 12
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  12. 21
      apps/remix-ide/bin/remix-ide
  13. 8
      apps/remix-ide/src/app.js
  14. 8
      apps/remix-ide/src/app/plugins/storage.ts
  15. 14
      apps/remix-ide/src/app/tabs/abstract-provider.tsx
  16. 41
      apps/remix-ide/src/app/tabs/external-http-provider.tsx
  17. 68
      apps/remix-ide/src/app/tabs/runTab/model/recorder.js
  18. 2
      apps/remix-ide/src/app/tabs/theme-module.js
  19. 14
      apps/remix-ide/src/app/udapp/run-tab.js
  20. 18
      apps/remix-ide/src/blockchain/blockchain.js
  21. 3
      apps/remix-ide/src/blockchain/execution-context.js
  22. 2
      apps/remix-ide/src/remixAppManager.js
  23. 3
      libs/remix-core-plugin/src/index.ts
  24. 9
      libs/remix-core-plugin/src/lib/helpers/fetch-etherscan.ts
  25. 8
      libs/remix-lib/src/execution/forkAt.ts
  26. 2
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  27. 33
      libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx
  28. 4
      libs/remix-ui/app/src/lib/remix-app/context/provider.tsx
  29. 6
      libs/remix-ui/app/src/lib/remix-app/interface/index.ts
  30. 1
      libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts
  31. 1
      libs/remix-ui/app/src/lib/remix-app/state/modals.ts
  32. 2
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  33. 14
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  34. 4
      libs/remix-ui/helper/src/lib/components/web3Dialog.tsx
  35. 5
      libs/remix-ui/helper/src/lib/helper-components.tsx
  36. 10
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
  37. 7
      libs/remix-ui/modal-dialog/src/lib/types/index.ts
  38. 25
      libs/remix-ui/run-tab/src/lib/actions/account.ts
  39. 2
      libs/remix-ui/run-tab/src/lib/actions/index.ts
  40. 7
      libs/remix-ui/run-tab/src/lib/actions/recorder.ts
  41. 4
      libs/remix-ui/run-tab/src/lib/components/account.tsx
  42. 8
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  43. 4
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  44. 2
      libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx
  45. 2
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  46. 49
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  47. 2
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  48. 2
      libs/remix-ui/run-tab/src/lib/components/value.tsx
  49. 1
      libs/remix-ui/run-tab/src/lib/css/run-tab.css
  50. 20
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts
  51. 9
      libs/remix-ui/run-tab/src/lib/types/blockchain.d.ts
  52. 2
      libs/remix-ui/run-tab/src/lib/types/index.ts
  53. 2
      libs/remix-ui/run-tab/src/lib/types/recorder.d.ts
  54. 2
      libs/remix-ui/search/src/lib/reducers/Reducer.ts
  55. 4
      libs/remix-ui/settings/src/lib/constants.ts
  56. 26
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  57. 6
      libs/remix-ui/solidity-compiler/src/lib/logic/compileTabLogic.ts
  58. 7
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  59. 1
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  60. 2
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  61. 2
      libs/remix-ui/terminal/src/lib/components/RenderCall.tsx
  62. 2
      libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx
  63. 2
      libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx
  64. 95
      libs/remix-ui/workspace/src/lib/actions/index.ts
  65. 3
      libs/remixd/package.json
  66. 6
      libs/remixd/src/bin/remixd.ts
  67. 25
      libs/remixd/src/scripts/installSlither.ts
  68. 3
      package.json
  69. 65
      tslint.json
  70. 185
      yarn.lock

@ -1,4 +0,0 @@
# Add files here to ignore them from prettier formatting
/dist
/coverage

@ -1,3 +0,0 @@
{
"singleQuote": true
}

@ -83,10 +83,10 @@ module.exports = {
browser
.openFile('Untitled.sol')
.clickLaunchIcon('udapp')
.click('*[data-id="settingsWeb3Mode"]')
.waitForElementPresent('[data-id="envNotification-modal-footer-ok-react"]')
.click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]')
.waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(function () {
const modal = document.querySelector('[data-id="envNotification-modal-footer-ok-react"]') as any
const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any
modal.click()
})
@ -96,7 +96,7 @@ module.exports = {
return env.value
}, [], function (result) {
browser.assert.ok(result.value === 'web3', 'Web3 Provider not selected')
browser.assert.ok(result.value === 'External Http Provider', 'Web3 Provider not selected')
})
.clickLaunchIcon('solidity')
.clickLaunchIcon('udapp')

@ -78,10 +78,10 @@ module.exports = {
browser
.openFile('Untitled.sol')
.clickLaunchIcon('udapp')
.click('*[data-id="settingsWeb3Mode"]')
.waitForElementPresent('[data-id="envNotification-modal-footer-ok-react"]')
.click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]')
.waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(function () {
const modal = document.querySelector('[data-id="envNotification-modal-footer-ok-react"]') as any
const modal = document.querySelector('[data-id="basic-http-provider-modal-footer-ok-react"]') as any
modal.click()
})

@ -214,10 +214,10 @@ module.exports = {
.setSolidityCompilerVersion('soljson-v0.8.7+commit.e28d00a7.js')
.addFile('useDebugNodes.sol', sources[5]['useDebugNodes.sol']) // compile contract
.clickLaunchIcon('udapp')
.click('*[data-id="settingsWeb3Mode"]') // select web3 provider with debug nodes URL
.clearValue('*[data-id="modalDialogCustomPromptText"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', 'https://remix-rinkeby.ethdevops.io')
.modalFooterOKClick()
.click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]') // select web3 provider with debug nodes URL
.clearValue('*[data-id="modalDialogCustomPromp"]')
.setValue('*[data-id="modalDialogCustomPromp"]', 'https://remix-rinkeby.ethdevops.io')
.modalFooterOKClick('basic-http-provider')
.waitForElementPresent('*[title="Deploy - transact (not payable)"]', 65000) // wait for the compilation to succeed
.clickLaunchIcon('debugger')
.clearValue('*[data-id="debuggerTransactionInput"]')

@ -188,7 +188,7 @@ module.exports = {
.frameParent()
.useCss()
.clickLaunchIcon('udapp')
.waitForElementContainsText('#selectExEnvOptions option:checked', 'JavaScript VM (Berlin)')
.waitForElementContainsText('#selectExEnvOptions option:checked', 'Remix VM (Berlin)')
.clickLaunchIcon('localPlugin')
.useXpath()
// @ts-ignore

@ -44,9 +44,6 @@ module.exports = {
.waitForElementContainsText('*[data-id="foundry-providerModalDialogModalBody-react"]', 'Error while connecting to the provider')
.modalFooterOKClick('foundry-provider')
.waitForElementNotVisible('*[data-id="foundry-providerModalDialogModalBody-react"]')
.waitForElementVisible('*[data-id="PermissionHandler-modal-footer-ok-react"]')
.click('*[data-id="PermissionHandler-modal-footer-ok-react"]')
.waitForElementNotVisible('*[data-id="PermissionHandler-modal-footer-ok-react"]')
.pause(1000)
},

@ -42,7 +42,7 @@ module.exports = {
.createContract(['12'])
.clickInstance(0)
.clickFunction('set - transact (not payable)', { types: 'uint256 _p', values: '34' })
.click('i.savetransaction')
.click('.savetransaction')
.waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]')
.execute(function () {
const modalOk = document.querySelector('[data-id="udappNotify-modal-footer-ok-react"]') as any
@ -77,7 +77,7 @@ module.exports = {
.selectContract('t2est')
.pause(1000)
.createContract([])
.click('i.savetransaction')
.click('.savetransaction')
.waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]')
.execute(function () {
const modalOk = document.querySelector('[data-id="udappNotify-modal-footer-ok-react"]') as any
@ -95,6 +95,50 @@ module.exports = {
status: 'true Transaction mined and execution succeed',
'decoded input': { 'uint256 _po': '10' }
})
},
'Run with live "mode"': function (browser: NightwatchBrowser) {
let addressRef: string
browser.addFile('scenario_live_mode.json', { content: JSON.stringify(liveModeScenario, null, '\t') })
.addFile('scenario_live_mode_storage.sol', { content: testStorageForLiveMode })
.clickLaunchIcon('solidity')
.click('*[data-id="compilerContainerCompileBtn"]')
.openFile('scenario_live_mode.json')
.clickLaunchIcon('udapp')
.click('*[data-id="deployAndRunClearInstances"]')
.click('*[data-id="runtabLivemodeInput"]')
.click('.runtransaction')
.pause(1000)
.clickInstance(0)
.getAddressAtPosition(0, (address) => {
addressRef = address
})
.clickFunction('retrieve - call')
.perform((done) => {
browser.verifyCallReturnValue(addressRef, ['', '0:uint256: 350'])
.perform(() => done())
})
// change the init state and recompile the same contract.
.openFile('scenario_live_mode_storage.sol')
.setEditorValue(testStorageForLiveMode.replace('number = 350', 'number = 300'))
.pause(5000)
.clickLaunchIcon('solidity')
.click('*[data-id="compilerContainerCompileBtn"]')
.openFile('scenario_live_mode.json')
.clickLaunchIcon('udapp')
.click('*[data-id="deployAndRunClearInstances"]')
.click('.runtransaction')
.pause(5000)
.clickInstance(0)
.getAddressAtPosition(0, (address) => {
addressRef = address
})
.clickFunction('retrieve - call')
.perform((done) => {
browser.verifyCallReturnValue(addressRef, ['', '0:uint256: 300'])
.perform(() => done())
})
.end()
}
}
@ -364,3 +408,91 @@ const scenario = {
]
}
}
const liveModeScenario = {
"accounts": {
"account{0}": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"
},
"linkReferences": {},
"transactions": [
{
"timestamp": 1656329164297,
"record": {
"value": "0",
"parameters": [],
"abi": "0x8b8c9c14c8e1442e90dd6ff82bb9889ccfe5a54d88ef30776f11047ecce5fedb",
"contractName": "Storage",
"bytecode": "608060405234801561001057600080fd5b5060c88061001f6000396000f3fe6080604052348015600f57600080fd5b5060043610604e577c010000000000000000000000000000000000000000000000000000000060003504632e64cec1811460535780636057361d146068575b600080fd5b60005460405190815260200160405180910390f35b60786073366004607a565b600055565b005b600060208284031215608b57600080fd5b503591905056fea264697066735822122091f1bc250ccda7caf2b0d9f67b0314d92233fdb5952b72cece72bd2a5d43cfc264736f6c63430008070033",
"linkReferences": {},
"name": "",
"inputs": "()",
"type": "constructor",
"from": "account{0}"
}
}
],
"abis": {
"0x8b8c9c14c8e1442e90dd6ff82bb9889ccfe5a54d88ef30776f11047ecce5fedb": [
{
"inputs": [
{
"internalType": "uint256",
"name": "num",
"type": "uint256"
}
],
"name": "store",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "retrieve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]
}
}
const testStorageForLiveMode = `// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract Storage {
uint256 number;
constructor () {
number = 350;
}
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}`

@ -198,7 +198,7 @@ module.exports = {
.assert.containsText('*[data-id="solidityLocals"]', 'to: 0x6C3CCC7FBA111707D5A1AAF2758E9D4F4AC5E7B1')
},
'Call web3.eth.getAccounts() using Injected web3 (Metamask)': '' + function (browser: NightwatchBrowser) {
'Call web3.eth.getAccounts() using Injected Provider (Metamask)': '' + function (browser: NightwatchBrowser) {
browser
.executeScript('web3.eth.getAccounts()')
.pause(2000)

@ -40,7 +40,7 @@ module.exports = {
.waitForElementContainsText('*[data-id="terminalJournal"]', 'contract Ballot {', 60000)
},
'Call web3.eth.getAccounts() using JavaScript VM #group2': function (browser: NightwatchBrowser) {
'Call web3.eth.getAccounts() using Remix VM #group2': function (browser: NightwatchBrowser) {
browser
.executeScript('web3.eth.getAccounts()')
.waitForElementContainsText('*[data-id="terminalJournal"]', '["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2","0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db","0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB","0x617F2E2fD72FD9D5503197092aC168c91465E7f2","0x17F6AD8Ef982297579C203069C1DbfFE4348c372","0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678","0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7","0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C","0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC","0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c","0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C","0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB","0x583031D1113aD414F02576BD6afaBfb302140225","0xdD870fA1b7C4700F2BD7f44238821C26f7392148"]')
@ -50,8 +50,8 @@ module.exports = {
browser
.click('*[data-id="terminalClearConsole"]') // clear the terminal
.clickLaunchIcon('udapp')
.click('*[data-id="settingsWeb3Mode"]')
.modalFooterOKClick('envNotification')
.click('*[data-id="settingsSelectEnvOptions"] *[data-id="External Http Provider"]')
.modalFooterOKClick('basic-http-provider')
.executeScript('web3.eth.getAccounts()')
.waitForElementContainsText('*[data-id="terminalJournal"]', '["', 60000) // we check if an array is present, don't need to check for the content
.waitForElementContainsText('*[data-id="terminalJournal"]', '"]', 60000)

@ -38,7 +38,7 @@ module.exports = {
.clickLaunchIcon('filePanel')
.click('*[data-id="workspaceCreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_remix_default' })
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok')
@ -94,7 +94,7 @@ module.exports = {
browser
.click('*[data-id="workspaceCreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_blank' })
.click('select[id="wstemplate"]')
@ -115,7 +115,7 @@ module.exports = {
browser
.click('*[data-id="workspaceCreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc20' })
.click('select[id="wstemplate"]')
@ -163,7 +163,7 @@ module.exports = {
browser
.click('*[data-id="workspaceCreate"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc721' })
.click('select[id="wstemplate"]')
@ -213,7 +213,7 @@ module.exports = {
browser
.click('*[data-id="workspaceCreate"]') // create workspace_name
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
.click('*[data-id="modalDialogCustomPromptTextCreate"]')
.clearValue('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name')
@ -225,7 +225,7 @@ module.exports = {
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.sol"]')
.click('*[data-id="workspaceCreate"]') // create workspace_name_1
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > span')
.waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button')
.click('*[data-id="modalDialogCustomPromptTextCreate"]')
.clearValue('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_name_1')

@ -1,21 +0,0 @@
#!/usr/bin/env node
var path = require('path')
var httpServer = require('http-server')
var remixd = require('remixd')
var server = httpServer.createServer({
root: path.join(__dirname, '/../')
})
var folder = process.argv.length > 2 ? process.argv[2] : process.cwd()
server.listen(8080, '127.0.0.1', function () {})
var router = new remixd.Router(65520, remixd.services.sharedFolder, { remixIdeUrl: 'http://localhost:8080' }, (webSocket) => {
remixd.services.sharedFolder.setWebSocket(webSocket)
remixd.services.sharedFolder.setupNotifications(folder)
remixd.services.sharedFolder.sharedFolder(folder, false)
})
router.start()
console.log('\x1b[33m%s\x1b[0m', 'Starting Remix IDE at http://localhost:8080 and sharing ' + folder)

@ -30,6 +30,7 @@ import { Blockchain } from './blockchain/blockchain.js'
import { HardhatProvider } from './app/tabs/hardhat-provider'
import { GanacheProvider } from './app/tabs/ganache-provider'
import { FoundryProvider } from './app/tabs/foundry-provider'
import { ExternalHttpProvider } from './app/tabs/external-http-provider'
const isElectron = require('is-electron')
@ -181,6 +182,7 @@ class AppComponent {
const hardhatProvider = new HardhatProvider(blockchain)
const ganacheProvider = new GanacheProvider(blockchain)
const foundryProvider = new FoundryProvider(blockchain)
const externalHttpProvider = new ExternalHttpProvider(blockchain)
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
Registry.getInstance().put({
@ -242,6 +244,7 @@ class AppComponent {
hardhatProvider,
ganacheProvider,
foundryProvider,
externalHttpProvider,
this.walkthroughService,
search
])
@ -327,7 +330,8 @@ class AppComponent {
filePanel.slitherHandle,
linkLibraries,
deployLibraries,
openZeppelinProxy
openZeppelinProxy,
run.recorder
])
this.layout.panels = {
@ -361,7 +365,7 @@ class AppComponent {
await this.appManager.activatePlugin(['settings', 'config'])
await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'codeParser', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler'])
await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough','storage', 'search','compileAndRun'])
await this.appManager.activatePlugin(['walkthrough','storage', 'search','compileAndRun', 'recorder'])
this.appManager.on(
'filePanel',

@ -15,14 +15,14 @@ export class StoragePlugin extends Plugin {
async getStorage() {
let storage = null
if ('storage' in navigator && 'estimate' in navigator.storage && (window as any).remixFileSystem.name !== 'localstorage') {
storage = navigator.storage.estimate()
storage = await navigator.storage.estimate()
} else {
storage ={
usage: parseFloat(this.calculateLocalStorage()) * 1000,
quota: 5000000,
}
}
const _paq = window._paq = window._paq || []
const _paq = (window as any)._paq = (window as any)._paq || []
_paq.push(['trackEvent', 'Storage', 'used', this.formatString(storage)]);
return storage
}
@ -32,8 +32,8 @@ export class StoragePlugin extends Plugin {
}
calculateLocalStorage() {
var _lsTotal = 0
var _xLen; var _x
let _lsTotal = 0
let _xLen; let _x
for (_x in localStorage) {
// eslint-disable-next-line no-prototype-builtins
if (!localStorage.hasOwnProperty(_x)) {

@ -58,6 +58,20 @@ export abstract class AbstractProvider extends Plugin {
modalType: ModalTypes.prompt,
okLabel: 'OK',
cancelLabel: 'Cancel',
validationFn: (value) => {
if (!value) return { valid: false, message: "value is empty" }
if (value.startsWith('https://') || value.startsWith('http://')) {
return {
valid: true,
message: ''
}
} else {
return {
valid: false,
message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
}
}
},
okFn: (value: string) => {
setTimeout(() => resolve(value), 0)
},

@ -0,0 +1,41 @@
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { AbstractProvider } from './abstract-provider'
const profile = {
name: 'basic-http-provider',
displayName: 'External Http Provider',
kind: 'provider',
description: '',
methods: ['sendAsync'],
version: packageJson.version
}
export class ExternalHttpProvider extends AbstractProvider {
constructor (blockchain) {
super(profile, blockchain, 'http://127.0.0.1:8545')
}
body (): JSX.Element {
const thePath = '<path/to/local/folder/for/test/chain>'
return (
<>
<div className="">
Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see <a href="https://geth.ethereum.org/docs/rpc/server" target="_blank" rel="noreferrer">Geth Docs on rpc server</a>)
<div className="border p-1">geth --http --http.corsdomain https://remix.ethereum.org</div>
<br />
To run Remix & a local Geth test node, use this command: (see <a href="https://geth.ethereum.org/getting-started/dev-mode" target="_blank" rel="noreferrer">Geth Docs on Dev mode</a>)
<div className="border p-1">geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console</div>
<br />
<br />
<b>WARNING:</b> It is not safe to use the --http.corsdomain flag with a wildcard: <b>--http.corsdomain *</b>
<br />
<br />For more info: <a href="https://remix-ide.readthedocs.io/en/latest/run.html#more-about-web3-provider" target="_blank" rel="noreferrer">Remix Docs on Web3 Provider</a>
<br />
<br />
External HTTP Provider Endpoint
</div>
</>
)
}
}

@ -1,16 +1,28 @@
var async = require('async')
var ethutil = require('ethereumjs-util')
var remixLib = require('@remix-project/remix-lib')
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../.././../../package.json'
var EventManager = remixLib.EventManager
var format = remixLib.execution.txFormat
var txHelper = remixLib.execution.txHelper
const helper = require('../../../../lib/helper')
const _paq = window._paq = window._paq || [] //eslint-disable-line
const profile = {
name: 'recorder',
displayName: 'Recorder',
description: '',
version: packageJson.version,
methods: [ ]
}
/**
* Record transaction as long as the user create them.
*/
class Recorder {
class Recorder extends Plugin {
constructor (blockchain) {
super(profile)
this.event = new EventManager()
this.blockchain = blockchain
this.data = { _listen: true, _replay: false, journal: [], _createdContracts: {}, _createdContractsReverse: {}, _usedAccounts: {}, _abis: {}, _contractABIReferences: {}, _linkReferences: {} }
@ -21,7 +33,13 @@ class Recorder {
// convert to and from to tokens
if (this.data._listen) {
var record = { value, parameters: payLoad.funArgs }
var record = {
value,
inputs: txHelper.serializeInputs(payLoad.funAbi),
parameters: payLoad.funArgs,
name: payLoad.funAbi.name,
type: payLoad.funAbi.type
}
if (!to) {
var abi = payLoad.contractABI
var keccak = ethutil.bufferToHex(ethutil.keccakFromString(JSON.stringify(abi)))
@ -43,10 +61,7 @@ class Recorder {
var creationTimestamp = this.data._createdContracts[to]
record.to = `created{${creationTimestamp}}`
record.abi = this.data._contractABIReferences[creationTimestamp]
}
record.name = payLoad.funAbi.name
record.inputs = txHelper.serializeInputs(payLoad.funAbi)
record.type = payLoad.funAbi.type
}
for (var p in record.parameters) {
var thisarg = record.parameters[p]
var thistimestamp = this.data._createdContracts[thisarg]
@ -169,16 +184,30 @@ class Recorder {
/**
* run the list of records
*
* @param {Object} records
* @param {Object} accounts
* @param {Object} options
* @param {Object} abis
* @param {Object} linkReferences
* @param {Function} confirmationCb
* @param {Function} continueCb
* @param {Function} promptCb
* @param {Function} alertCb
* @param {Function} logCallBack
* @param {Function} liveMode
* @param {Function} newContractFn
*
*/
run (records, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, newContractFn) {
run (records, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, liveMode, newContractFn) {
this.setListen(false)
logCallBack(`Running ${records.length} transaction(s) ...`)
async.eachOfSeries(records, (tx, index, cb) => {
const liveMsg = liveMode ? ' in live mode' : ''
logCallBack(`Running ${records.length} transaction(s)${liveMsg} ...`)
async.eachOfSeries(records, async (tx, index, cb) => {
if (liveMode && tx.record.type === 'constructor') {
// resolve the bytecode using the contract name, this ensure getting the last compiled one.
const data = await this.call('compilerArtefacts', 'getArtefactsByContractName', tx.record.contractName)
tx.record.bytecode = data.artefact.evm.bytecode.object
}
var record = this.resolveAddress(tx.record, accounts, options)
var abi = abis[tx.record.abi]
if (!abi) {
@ -257,8 +286,10 @@ class Recorder {
}, () => { this.setListen(true) })
}
runScenario (json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) {
runScenario (liveMode, json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) {
_paq.push(['trackEvent', 'run', 'recorder', 'start'])
if (!json) {
_paq.push(['trackEvent', 'run', 'recorder', 'wrong-json'])
return cb('a json content must be provided')
}
if (typeof json === 'string') {
@ -269,12 +300,17 @@ class Recorder {
}
}
let txArray
let accounts
let options
let abis
let linkReferences
try {
var txArray = json.transactions || []
var accounts = json.accounts || []
var options = json.options || {}
var abis = json.abis || {}
var linkReferences = json.linkReferences || {}
txArray = json.transactions || []
accounts = json.accounts || []
options = json.options || {}
abis = json.abis || {}
linkReferences = json.linkReferences || {}
} catch (e) {
return cb('Invalid Scenario File. Please try again')
}
@ -283,7 +319,7 @@ class Recorder {
return
}
this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, (abi, address, contractName) => {
this.run(txArray, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, liveMode, (abi, address, contractName) => {
cb(null, abi, address, contractName)
})
}

@ -37,7 +37,7 @@ export class ThemeModule extends Plugin {
themes.map((theme) => {
this.themes[theme.name.toLocaleLowerCase()] = {
...theme,
url: window.location.origin + window.location.pathname + theme.url
url: window.location.origin + ( window.location.pathname.startsWith('/address/') || window.location.pathname.endsWith('.sol') ? '/' : window.location.pathname ) + theme.url
}
})
this._paq = _paq

@ -155,6 +155,20 @@ export class RunTab extends ViewPlugin {
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'External Http Provider',
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('basic-http-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
}
writeFile (fileName, content) {

@ -464,7 +464,7 @@ export class Blockchain extends Plugin {
}
/**
* This function send a tx only to javascript VM or testnet, will return an error for the mainnet
* This function send a tx only to Remix VM or testnet, will return an error for the mainnet
* SHOULD BE TAKEN CAREFULLY!
*
* @param {Object} tx - transaction.
@ -530,6 +530,7 @@ export class Blockchain extends Plugin {
if (this.transactionContextAPI.getAddress) {
return this.transactionContextAPI.getAddress(function (err, address) {
if (err) return reject(err)
if (!address) return reject('"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.')
return resolve(address)
})
}
@ -548,9 +549,18 @@ export class Blockchain extends Plugin {
const runTransaction = async () => {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
const fromAddress = await getAccount()
const value = await queryValue()
const gasLimit = await getGasLimit()
let fromAddress
let value
let gasLimit
try {
fromAddress = await getAccount()
value = await queryValue()
gasLimit = await getGasLimit()
} catch (e) {
reject(e)
return
}
const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp }
const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences }

@ -164,9 +164,6 @@ export class ExecutionContext {
}
}
if (context === 'web3') {
confirmCb(cb)
}
if (this.customNetWorks[context]) {
var network = this.customNetWorks[context]
this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => {

@ -19,7 +19,7 @@ const sensitiveCalls = {
}
export function isNative(name) {
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider']
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', 'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider']
return nativePlugins.includes(name) || requiredModules.includes(name)
}

@ -7,4 +7,5 @@ export { EditorContextListener } from './lib/editor-context-listener'
export { GistHandler } from './lib/gist-handler'
export * from './types/contract'
export { LinkLibraries, DeployLibraries } from './lib/link-libraries'
export { OpenZeppelinProxy } from './lib/openzeppelin-proxy'
export { OpenZeppelinProxy } from './lib/openzeppelin-proxy'
export { fetchContractFromEtherscan } from './lib/helpers/fetch-etherscan'

@ -1,8 +1,11 @@
export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath) => {
export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath, key?) => {
let data
const compilationTargets = {}
let etherscanKey
const etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token')
if (!key) etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token')
else etherscanKey = key
if (etherscanKey) {
const endpoint = network.id == 1 ? 'api.etherscan.io' : 'api-' + network.name + '.etherscan.io'
data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey)
@ -10,7 +13,7 @@ export const fetchContractFromEtherscan = async (plugin, network, contractAddres
// etherscan api doc https://docs.etherscan.io/api-endpoints/contracts
if (data.message === 'OK' && data.status === "1") {
if (data.result.length) {
if (data.result[0].SourceCode === '') throw new Error('contract not verified in Etherscan')
if (data.result[0].SourceCode === '') throw new Error(`contract not verified on Etherscan ${network.name} network`)
if (data.result[0].SourceCode.startsWith('{')) {
data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/,'{').replace(/}}$/,'}'))
}

@ -50,6 +50,14 @@ const forks = {
{
number: 12965000,
name: 'london'
},
{
number: 13773000,
name: 'arrowGlacier'
},
{
number: 15050000,
name: 'grayGlacier'
}
],
3: [

@ -85,7 +85,7 @@ export class TxRunnerWeb3 {
runInNode (from, to, data, value, gasLimit, useCall, timestamp, confirmCb, gasEstimationForceSend, promptCb, callback) {
const tx = { from: from, to: to, data: data, value: value }
if (!from) return callback('the value of "from" is not defined. Please make sure an account is selected.')
if (useCall) {
tx['gas'] = gasLimit
if (this._api && this._api.isVM()) tx['timestamp'] = timestamp

@ -1,5 +1,5 @@
import React, { useEffect, useRef, useState } from 'react'
import { ModalDialog, ModalDialogProps } from '@remix-ui/modal-dialog'
import { ModalDialog, ModalDialogProps, ValidationResult } from '@remix-ui/modal-dialog'
import { ModalTypes } from '../../types'
interface ModalWrapperProps extends ModalDialogProps {
@ -29,12 +29,23 @@ const ModalWrapper = (props: ModalWrapperProps) => {
(props.cancelFn) ? props.cancelFn() : props.resolve(false)
}
const createModalMessage = (defaultValue: string) => {
const onInputChanged = (event) => {
if (props.validationFn) {
const validation = props.validationFn(event.target.value)
setState(prevState => {
return { ...prevState, message: createModalMessage(props.defaultValue, validation), validation }
})
}
}
const createModalMessage = (defaultValue: string, validation: ValidationResult) => {
return (
<>
{props.message}
<input type={props.modalType === ModalTypes.password ? 'password' : 'text'} defaultValue={defaultValue} data-id="modalDialogCustomPromp" ref={ref} className="form-control" /></>
)
<input onChange={onInputChanged} type={props.modalType === ModalTypes.password ? 'password' : 'text'} defaultValue={defaultValue} data-id="modalDialogCustomPromp" ref={ref} className="form-control" />
{!validation.valid && <span className='text-warning'>{validation.message}</span>}
</>
)
}
useEffect(() => {
@ -47,13 +58,13 @@ const ModalWrapper = (props: ModalWrapperProps) => {
...props,
okFn: onFinishPrompt,
cancelFn: onCancelFn,
message: createModalMessage(props.defaultValue)
message: createModalMessage(props.defaultValue, { valid: true })
})
break
default:
setState({
...props,
okFn: (onOkFn),
okFn: onOkFn,
cancelFn: onCancelFn
})
break
@ -67,8 +78,16 @@ const ModalWrapper = (props: ModalWrapperProps) => {
}
}, [props])
// reset the message and input if any, so when the modal is shown again it doesn't show the previous value.
const handleHide = () => {
setState(prevState => {
return { ...prevState, message: '' }
})
props.handleHide()
}
return (
<ModalDialog id={props.id} {...state} handleHide={props.handleHide} />
<ModalDialog id={props.id} {...state} handleHide={handleHide} />
)
}
export default ModalWrapper

@ -16,11 +16,11 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
}
const modal = (modalData: AppModal) => {
const { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData
const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData
return new Promise((resolve, reject) => {
dispatch({
type: modalActionTypes.setModal,
payload: { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data }
payload: { id, title, message, okLabel, validationFn, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data }
})
})
}

@ -1,10 +1,16 @@
import { ModalTypes } from '../types'
export type ValidationResult = {
valid: boolean,
message?: string
}
export interface AppModal {
id: string
timestamp?: number
hide?: boolean
title: string
validationFn?: (value: string) => ValidationResult
// eslint-disable-next-line no-undef
message: string | JSX.Element
okLabel: string

@ -11,6 +11,7 @@ export const modalReducer = (state: ModalState = ModalInitialState, action: Moda
id: action.payload.id || timestamp.toString(),
hide: false,
title: action.payload.title,
validationFn: action.payload.validationFn,
message: action.payload.message,
okLabel: action.payload.okLabel,
okFn: action.payload.okFn,

@ -8,6 +8,7 @@ export const ModalInitialState: ModalState = {
hide: true,
title: '',
message: '',
validationFn: () => { return {valid: true, message: ''} },
okLabel: '',
okFn: () => { },
cancelLabel: '',

@ -331,7 +331,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return { ...prevState, opt: { ...prevState.opt, debugWithGeneratedSources: checked } }
})
}} type="checkbox" title="Debug with generated sources" />
<label data-id="debugGeneratedSourcesLabel" className="form-check-label custom-control-label" htmlFor="debugGeneratedSourcesInput">Use generated sources (from Solidity v0.7.2)</label>
<label data-id="debugGeneratedSourcesLabel" className="form-check-label custom-control-label" htmlFor="debugGeneratedSourcesInput">Use generated sources (Solidity {'>='} v0.7.2)</label>
</div>
{ state.isLocalNodeUsed && <div className="mt-2 mb-2 debuggerConfig custom-control custom-checkbox">
<input className="custom-control-input" id="debugWithLocalNodeInput" onChange={({ target: { checked } }) => {

@ -519,6 +519,20 @@ export const EditorUI = (props: EditorUIProps) => {
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => {
editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 })
})
const editorService = editor._codeEditorService;
const openEditorBase = editorService.openCodeEditor.bind(editorService);
editorService.openCodeEditor = async (input, source) => {
const result = await openEditorBase(input, source)
if (input && input.resource && input.resource.path) {
try {
await props.plugin.call('fileManager', 'open', input.resource.path)
} catch (e) {
console.log(e)
}
}
return result
}
}
function handleEditorWillMount(monaco: Monaco) {

@ -24,10 +24,10 @@ export function Web3ProviderDialog (props: web3ProviderDialogProps) {
<br />
<b>WARNING:</b> It is not safe to use the --http.corsdomain flag with a wildcard: <b>--http.corsdomain *</b>
<br />
<br />For more info: <a href="https://remix-ide.readthedocs.io/en/latest/run.html#more-about-web3-provider" target="_blank" rel="noreferrer">Remix Docs on Web3 Provider</a>
<br />For more info: <a href="https://remix-ide.readthedocs.io/en/latest/run.html#more-about-web3-provider" target="_blank" rel="noreferrer">Remix Docs on Remix Provider</a>
<br />
<br />
Web3 Provider Endpoint
External HTTP Provider Endpoint
</div>
<input
onInput={handleInputEndpoint}

@ -1,5 +1,4 @@
import React from 'react'
import { Web3ProviderDialog } from './components/web3Dialog'
export const fileChangedToastMsg = (from: string, path: string) => (
<div><i className="fas fa-exclamation-triangle text-danger mr-1"></i>
@ -54,10 +53,6 @@ export const sourceVerificationNotAvailableToastMsg = () => (
</div>
)
export const web3Dialog = (externalEndpoint: string, setWeb3Endpoint: (value: string) => void) => (
<Web3ProviderDialog externalEndpoint={externalEndpoint} setWeb3Endpoint={setWeb3Endpoint} />
)
export const envChangeNotification = (env: { context: string, fork: string }, from: string) => (
<div>
<i className="fas fa-exclamation-triangle text-danger mr-1"></i>

@ -96,18 +96,20 @@ export const ModalDialog = (props: ModalDialogProps) => {
</div>
<div className="modal-footer" data-id={`${props.id}ModalDialogModalFooter-react`}>
{/* todo add autofocus ^^ */}
{ props.okLabel && <span
{ props.okLabel && <button
data-id={`${props.id}-modal-footer-ok-react`}
className={'modal-ok btn btn-sm ' + (state.toggleBtn ? 'btn-dark' : 'btn-light')}
disabled={props.validation && !props.validation.valid}
onClick={() => {
if (props.validation && !props.validation.valid) return
if (props.okFn) props.okFn()
handleHide()
}}
>
{props.okLabel ? props.okLabel : 'OK'}
</span>
</button>
}
{ props.cancelLabel && <span
{ props.cancelLabel && <button
data-id={`${props.id}-modal-footer-cancel-react`}
className={'modal-cancel btn btn-sm ' + (state.toggleBtn ? 'btn-light' : 'btn-dark')}
data-dismiss="modal"
@ -117,7 +119,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
}}
>
{props.cancelLabel ? props.cancelLabel : 'Cancel'}
</span>
</button>
}
</div>
</div>

@ -1,8 +1,15 @@
export type ValidationResult = {
valid: boolean,
message?: string
}
/* eslint-disable no-undef */
export interface ModalDialogProps {
id: string
timestamp?: number,
title?: string,
validation?: ValidationResult
validationFn?: (value: string) => ValidationResult
message?: string | JSX.Element,
okLabel?: string,
okFn?: (value?:any) => void,

@ -1,4 +1,4 @@
import { shortenAddress, web3Dialog } from "@remix-ui/helper"
import { shortenAddress } from "@remix-ui/helper"
import { RunTab } from "../types/run-tab"
import { clearInstances, setAccount, setExecEnv } from "./actions"
import { displayNotification, displayPopUp, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, setExternalEndpoint, setMatchPassphrase, setPassphrase } from "./payload"
@ -74,28 +74,7 @@ const _getProviderDropdownValue = (plugin: RunTab): string => {
}
export const setExecutionContext = (plugin: RunTab, dispatch: React.Dispatch<any>, executionContext: { context: string, fork: string }) => {
const displayContent = web3Dialog(plugin.REACT_API.externalEndpoint, (endpoint: string) => {
dispatch(setExternalEndpoint(endpoint))
})
plugin.blockchain.changeExecutionContext(executionContext, () => {
plugin.call('notification', 'modal', {
id: 'envNotification',
title: 'External node request',
message: displayContent,
okLabel: 'OK',
cancelLabel: 'Cancel',
okFn: () => {
plugin.blockchain.setProviderFromEndpoint(plugin.REACT_API.externalEndpoint, executionContext, (alertMsg) => {
if (alertMsg) plugin.call('notification', 'toast', alertMsg)
setFinalContext(plugin, dispatch)
})
},
cancelFn: () => {
setFinalContext(plugin, dispatch)
}
})
}, (alertMsg) => {
plugin.blockchain.changeExecutionContext(executionContext, null, (alertMsg) => {
plugin.call('notification', 'toast', alertMsg)
}, () => { setFinalContext(plugin, dispatch) })
}

@ -54,7 +54,7 @@ export const getExecutionContext = () => getContext(plugin)
export const executeTransactions = (instanceIndex: number, lookupOnly: boolean, funcABI: FuncABI, inputsValues: string, contractName: string, contractABI, contract, address, logMsg:string, mainnetPrompt: MainnetPrompt, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, funcIndex?: number) => runTransactions(plugin, dispatch, instanceIndex, lookupOnly, funcABI, inputsValues, contractName, contractABI, contract, address, logMsg, mainnetPrompt, gasEstimationPrompt, passphrasePrompt, funcIndex)
export const loadFromAddress = (contract: ContractData, address: string) => loadAddress(plugin, dispatch, contract, address)
export const storeNewScenario = async (prompt: (msg: string, defaultValue: string) => JSX.Element) => storeScenario(plugin, dispatch, prompt)
export const runScenario = (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => runCurrentScenario(plugin, dispatch, gasEstimationPrompt, passphrasePrompt, confirmDialogContent)
export const runScenario = (liveMode: boolean, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => runCurrentScenario(liveMode, plugin, dispatch, gasEstimationPrompt, passphrasePrompt, confirmDialogContent)
export const setScenarioPath = (path: string) => updateScenarioPath(dispatch, path)
export const getFuncABIValues = (funcABI: FuncABI) => getFuncABIInputs(plugin, funcABI)
export const setNetworkName = (networkName: string) => setNetworkNameFromProvider(dispatch, networkName)

@ -36,12 +36,13 @@ export const storeScenario = async (plugin: RunTab, dispatch: React.Dispatch<any
)
}
const runScenario = (plugin: RunTab, dispatch: React.Dispatch<any>, file: string, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => {
const runScenario = (liveMode: boolean, plugin: RunTab, dispatch: React.Dispatch<any>, file: string, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => {
if (!file) return dispatch(displayNotification('Alert', 'Unable to run scenerio, no specified scenario file', 'OK', null))
plugin.fileManager.readFile(file).then((json) => {
// TODO: there is still a UI dependency to remove here, it's still too coupled at this point to remove easily
plugin.recorder.runScenario(
liveMode,
json,
(error, continueTxExecution, cancelCb) => {
continueHandler(dispatch, gasEstimationPrompt, error, continueTxExecution, cancelCb)
@ -64,9 +65,9 @@ const runScenario = (plugin: RunTab, dispatch: React.Dispatch<any>, file: string
}).catch((error) => dispatch(displayNotification('Alert', error, 'OK', null)))
}
export const runCurrentScenario = (plugin: RunTab, dispatch: React.Dispatch<any>, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => {
export const runCurrentScenario = (liveMode: boolean, plugin: RunTab, dispatch: React.Dispatch<any>, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => {
const file = plugin.config.get('currentFile')
if (!file) return dispatch(displayNotification('Alert', 'A scenario file has to be selected', 'Ok', null))
runScenario(plugin, dispatch, file, gasEstimationPrompt, passphrasePrompt, confirmDialogContent)
runScenario(liveMode, plugin, dispatch, file, gasEstimationPrompt, passphrasePrompt, confirmDialogContent)
}

@ -160,8 +160,8 @@ export function AccountUI (props: AccountProps) {
accounts.map((value, index) => <option value={value} key={index}>{ loadedAccounts[value] }</option>)
}
</select>
<div style={{ marginLeft: -5 }}><CopyToClipboard content={selectedAccount} direction='top' /></div>
<i id="remixRunSignMsg" data-id="settingsRemixRunSignMsg" className="mx-1 fas fa-edit udapp_icon" aria-hidden="true" onClick={signMessage} title="Sign a message using this account key"></i>
<div style={{ marginLeft: -5 }}><CopyToClipboard tip='Copy account to clipboard' content={selectedAccount} direction='top' /></div>
<i id="remixRunSignMsg" data-id="settingsRemixRunSignMsg" className="mx-1 fas fa-edit udapp_icon" aria-hidden="true" onClick={signMessage} title="Sign a message using this account"></i>
</div>
</div>
)

@ -119,12 +119,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
if (enable) {
setAtAddressOptions({
disabled: false,
title: 'Interact with the given contract.'
title: 'Interact with the deployed contract - requires the .abi file or compiled .sol file to be selected in the editor (with the same compiler configuration)'
})
} else {
setAtAddressOptions({
disabled: true,
title: loadedAddress ? 'Compile *.sol file or select *.abi file.' : '⚠ Compile *.sol file or select *.abi file & then enter the address of deployed contract.'
title: loadedAddress ? 'Compile a *.sol file or select a *.abi file.' : 'To interact with a deployed contract, enter its address and compile its source *.sol file (with the same compiler settings) or select its .abi file in the editor. '
})
}
}
@ -133,12 +133,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
if (enable) {
setContractOptions({
disabled: false,
title: 'Select contract for Deploy or At Address.'
title: 'Select a compiled contract to deploy or to use with At Address.'
})
} else {
setContractOptions({
disabled: true,
title: loadType === 'sol' ? 'Select and compile *.sol file to deploy or access a contract.' : '⚠ Selected *.abi file allows accessing contracts, select and compile *.sol file to deploy and access one.'
title: loadType === 'sol' ? 'Select and compile *.sol file to deploy or access a contract.' : 'When there is a compiled .sol file, the choice of contracts to deploy or to use with AtAddress is made here.'
})
}
}

@ -8,7 +8,7 @@ export function EnvironmentUI (props: EnvironmentProps) {
const fork = provider.fork // can be undefined if connected to an external source (web3 provider / injected)
let context = provider.value
context = context.startsWith('vm') ? 'vm' : context // context has to be 'vm', 'web3' or 'injected'
context = context.startsWith('vm') ? 'vm' : context
props.setExecutionContext({ context, fork })
}
@ -29,7 +29,7 @@ export function EnvironmentUI (props: EnvironmentProps) {
)
}
</select>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#run-setup" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info" title="check out docs to setup Environment"></i></a>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info" title="Click for docs about Environment"></i></a>
</div>
</div>
)

@ -10,7 +10,7 @@ export function GasPriceUI (props: GasPriceProps) {
return (
<div className="udapp_crow">
<label className="udapp_settingsLabel">Gas limit</label>
<input type="number" className="form-control udapp_gasNval udapp_col2" id="gasLimit" value={props.gasLimit} onChange={handleGasLimit} />
<input type="number" className="form-control udapp_gasNval udapp_col2" title="The default gas limit is 3M. Adjust as needed." id="gasLimit" value={props.gasLimit} onChange={handleGasLimit} />
</div>
)
}

@ -12,7 +12,7 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
return (
<div className="udapp_instanceContainer mt-3 border-0 list-group-item">
<label className="udapp_deployedContracts d-flex justify-content-between align-items-center pl-2 mb-1"
<label className="udapp_deployedContracts d-flex justify-content-between align-items-center pl-2 mb-2"
title="Autogenerated generic user interfaces for interaction with deployed contracts">
Deployed Contracts
{ instanceList.length > 0

@ -1,15 +1,18 @@
// eslint-disable-next-line no-use-before-define
import React, {useState} from 'react'
import React, {useRef, useState} from 'react'
import { RecorderProps } from '../types'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
export function RecorderUI (props: RecorderProps) {
const inputLive = useRef<HTMLInputElement>()
const [toggleExpander, setToggleExpander] = useState<boolean>(false)
const triggerRecordButton = () => {
props.storeScenario(props.scenarioPrompt)
}
const handleClickRunButton = () => {
props.runCurrentScenario(props.gasEstimationPrompt, props.passphrasePrompt, props.mainnetPrompt)
const liveMode = inputLive.current ? inputLive.current.checked : false
props.runCurrentScenario(liveMode, props.gasEstimationPrompt, props.passphrasePrompt, props.mainnetPrompt)
}
const toggleClass = () => {
@ -22,6 +25,14 @@ export function RecorderUI (props: RecorderProps) {
<div className="d-flex">
<label className="mt-1 udapp_recorderSectionLabel">Transactions recorded</label>
<div className="ml-2 mb-2 badge badge-pill badge-primary" title="The number of recorded transactions">{props.count}</div>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>Save transactions (deployed contracts and function executions) and replay them in another environment. <br/> e.g Transactions created in Javascript VM can be replayed in the Injected Web3.
</span>
</Tooltip>
}>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle'} aria-hidden="true"></i>
</OverlayTrigger>
</div>
<div>
<span data-id='udappRecorderTitleExpander' onClick={toggleClass}>
@ -29,17 +40,29 @@ export function RecorderUI (props: RecorderProps) {
</span>
</div>
</div>
<div className={`border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
<div className="p-2 mt-2">
All transactions (deployed contracts and function executions) can be saved and replayed in
another environment. e.g Transactions created in Javascript VM can be replayed in the Injected Web3.
</div>
<div className="mb-2 udapp_transactionActions">
<i className="fas fa-save savetransaction udapp_recorder udapp_icon"
onClick={triggerRecordButton} title="Save Transactions" aria-hidden="true">
</i>
<i className="fas fa-play runtransaction udapp_runTxs udapp_icon" title="Run Transactions" data-id="runtransaction" aria-hidden="true" onClick={handleClickRunButton}></i>
</div>
<div className={`flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
<div className="mb-1 mt-1 fmt-2 custom-control custom-checkbox mb-1">
<input ref={inputLive} type="checkbox" id="livemode-recorder" className="custom-control-input custom-select" name="input-livemode"/>
<label className="form-check-label custom-control-label" data-id="runtabLivemodeInput" htmlFor="livemode-recorder">Use live mode (Run transactions against latest compiled contracts).</label>
</div>
<div className="mb-1 mt-1 udapp_transactionActions">
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="tooltip-save-recorder">
<span>Save {props.count} transaction(s) as scenario file.
</span>
</Tooltip>
}>
<button className="btn btn-sm btn-info savetransaction udapp_recorder" onClick={triggerRecordButton}>Save</button>
</OverlayTrigger>
<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="tooltip-run-recorder">
<span>Run transaction(s) from the current scenario file.
</span>
</Tooltip>
}>
<button className="btn btn-sm btn-info runtransaction udapp_runTxs" data-id="runtransaction" onClick={handleClickRunButton}>Run</button>
</OverlayTrigger>
</div>
</div>
</div>
)

@ -204,7 +204,7 @@ export function UniversalDappUI (props: UdappProps) {
return (
<div className={`instance udapp_instance udapp_run-instance border-dark ${toggleExpander ? 'udapp_hidesub' : 'bg-light'}`} id={`instance${address}`} data-shared="universalDappUiInstance">
<div className="udapp_title alert alert-secondary">
<div className="udapp_title pb-0 alert alert-secondary">
<span data-id={`universalDappUiTitleExpander${props.index}`} className="btn udapp_titleExpander" onClick={toggleClass}>
<i className={`fas ${toggleExpander ? 'fa-angle-right' : 'fa-angle-down'}`} aria-hidden="true"></i>
</span>

@ -58,7 +58,7 @@ export function ValueUI (props: ValueProps) {
className="form-control udapp_gasNval udapp_col2"
id="value"
data-id="dandrValue"
title="Enter the value and choose the unit"
title="Enter an amount and choose its unit"
onKeyPress={validateInputKey}
onChange={validateValue}
value={props.sendValue}

@ -286,7 +286,6 @@
.udapp_instance {
display: block;
flex-direction: column;
margin-bottom: 12px;
background: none;
border-radius: 2px;
}

@ -2,6 +2,9 @@ import { CompilerAbstract } from '@remix-project/remix-solidity-ts'
import { ContractData } from '@remix-project/core-plugin'
import { DeployMode, DeployOption, DeployOptions } from '../types'
import { ADD_DEPLOY_OPTION, ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_DEPLOY_OPTION, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_TX_FEE_CONTENT } from '../constants'
import Web3 from 'web3'
declare const window: any
interface Action {
type: string
payload: any
@ -115,30 +118,23 @@ export const runTabInitialState: RunTabState = {
providerList: [{
id: 'vm-mode-london',
dataId: 'settingsVMLondonMode',
title: 'Execution environment does not connect to any node, everything is local and in memory only.',
title: 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.',
value: 'vm-london',
fork: 'london',
content: 'JavaScript VM (London)'
content: 'Remix VM (London)'
}, {
id: 'vm-mode-berlin',
dataId: 'settingsVMBerlinMode',
title: 'Execution environment does not connect to any node, everything is local and in memory only.',
title: 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.',
value: 'vm-berlin',
fork: 'berlin',
content: 'JavaScript VM (Berlin)'
content: 'Remix VM (Berlin)'
}, {
id: 'injected-mode',
dataId: 'settingsInjectedMode',
title: 'Execution environment has been provided by Metamask or similar provider.',
value: 'injected',
content: 'Injected Web3'
}, {
id: 'web3-mode',
dataId: 'settingsWeb3Mode',
title: `Execution environment connects to node at localhost (or via IPC if available), transactions will be sent to the network and can cause loss of money or worse!
If this page is served via https and you access your node via http, it might not work. In this case, try cloning the repository and serving it via http.`,
value: 'web3',
content: 'Web3 Provider'
content: `Injected Provider${(window && window.ethereum && window.ethereum.isMetaMask) ? ' - Metamask' : ''}`
}],
isRequesting: false,
isSuccessful: false,

@ -1,3 +1,6 @@
import { Plugin } from "@remixproject/engine/lib/abstract";
import { ExecutionContext } from "./execution-context";
import { EventEmitter } from "events";
export class Blockchain extends Plugin<any, any> {
constructor(config: any);
event: any;
@ -70,7 +73,7 @@ export class Blockchain extends Plugin<any, any> {
getBalanceInEther(address: any, cb: any): void;
pendingTransactionsCount(): number;
/**
* This function send a tx only to javascript VM or testnet, will return an error for the mainnet
* This function send a tx only to Remix VM or testnet, will return an error for the mainnet
* SHOULD BE TAKEN CAREFULLY!
*
* @param {Object} tx - transaction.
@ -78,6 +81,4 @@ export class Blockchain extends Plugin<any, any> {
sendTransaction(tx: any): any;
runTx(args: any, confirmationCb: any, continueCb: any, promptCb: any, cb: any): void;
}
import { Plugin } from "@remixproject/engine/lib/abstract";
import { ExecutionContext } from "./execution-context";
import { EventEmitter } from "events";

@ -166,7 +166,7 @@ export interface ContractDropdownProps {
export interface RecorderProps {
storeScenario: (prompt: (msg: string, defaultValue: string) => JSX.Element) => void,
runCurrentScenario: (gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => void,
runCurrentScenario: (liveMode: boolean, gasEstimationPrompt: (msg: string) => JSX.Element, passphrasePrompt: (msg: string) => JSX.Element, confirmDialogContent: MainnetPrompt) => void,
mainnetPrompt: MainnetPrompt,
gasEstimationPrompt: (msg: string) => JSX.Element,
passphrasePrompt: (msg: string) => JSX.Element,

@ -9,7 +9,7 @@ export class Recorder {
getAll: () => void;
clearAll: () => void;
run: (records, accounts, options, abis, linkReferences, confirmationCb, continueCb, promptCb, alertCb, logCallBack, newContractFn) => void
runScenario: (json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) => void
runScenario: (liveMode, json, continueCb, promptCb, alertCb, confirmationCb, logCallBack, cb) => void
}
import { Blockchain } from "./blockchain";

@ -46,7 +46,7 @@ export const SearchReducer = (state: SearchState = SearchingInitialState, action
run: true
}
case 'SET_UNDO_ENABLED':
if(state.undoBuffer[`${action.payload.workspace}/${action.payload.path}`]){
if(action.payload.workspace && state.undoBuffer[`${action.payload.workspace}/${action.payload.path}`]){
state.undoBuffer[`${action.payload.workspace}/${action.payload.path}`].enabled = (action.payload.content === state.undoBuffer[`${action.payload.workspace}/${action.payload.path}`].newContent)
state.undoBuffer[`${action.payload.workspace}/${action.payload.path}`].visible = (action.payload.content !== state.undoBuffer[`${action.payload.workspace}/${action.payload.path}`].oldContent)
}

@ -11,9 +11,9 @@ export const etherscanTokenTitle = 'EtherScan Access Token'
export const etherscanTokenLink = 'https://etherscan.io/myapikey'
export const etherscanAccessTokenText = 'Manage the api key used to interact with Etherscan.'
export const etherscanAccessTokenText2 = 'Go to Etherscan api key page (link below) to create a new api key and save it in Remix.'
export const ethereunVMText = 'Always use Javascript VM at load'
export const ethereunVMText = 'Always use Remix VM at load'
export const wordWrapText = 'Word wrap in editor'
export const enablePersonalModeText = ' Enable Personal Mode for web3 provider. Transaction sent over Web3 will use the web3.personal API.\n'
export const enablePersonalModeText = ' Enable Personal Mode for Remix Provider. Transaction sent over Web3 will use the web3.personal API.\n'
export const useAutoCompleteText = 'Enable code completion in editor.'
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'

@ -33,6 +33,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
configurationSettings,
isHardhatProject,
isTruffleProject,
isFoundryProject,
workspaceName,
configFilePath,
setConfigFilePath,
@ -71,7 +72,10 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
api.setAppParameter('configFilePath', defaultPath)
if (state.useFileConfiguration) {
api.fileExists(defaultPath).then((exists) => {
if (!exists && state.useFileConfiguration) createNewConfigFile()
if (!exists && state.useFileConfiguration) {
configFilePathInput.current.value = defaultPath
createNewConfigFile()
}
})
}
setShowFilePathInput(false)
@ -90,8 +94,9 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
useEffect(() => {
const listener = (event) => {
if (configFilePathInput.current !== event.target) {
if (configFilePathInput.current !== event.target && event.target.innerText !== "Create") {
setShowFilePathInput(false)
configFilePathInput.current.value = ""
return;
}
};
@ -241,7 +246,16 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
if (filePath === '') filePath = defaultPath
if (!filePath.endsWith('.json')) filePath = filePath + '.json'
await api.writeFile(filePath, configFileContent)
let compilerConfig = configFileContent
if (isFoundryProject && !compilerConfig.includes('remappings')) {
const config = JSON.parse(compilerConfig)
config.settings.remappings = [
'ds-test/=lib/forge-std/lib/ds-test/src/',
'forge-std/=lib/forge-std/src/'
]
compilerConfig = JSON.stringify(config, null, '\t')
}
await api.writeFile(filePath, compilerConfig)
api.setAppParameter('configFilePath', filePath)
setConfigFilePath(filePath)
compileTabLogic.setConfigFilePath(filePath)
@ -754,7 +768,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<div className={`flex-column 'd-flex'}`}>
<div className="mb-2 ml-4">
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">Language</label>
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Available since v0.5.7">
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Language specification available from Compiler >= v0.5.7">
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select>
@ -789,7 +803,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</div>
<div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}>
{ (!showFilePathInput && state.useFileConfiguration) && <span
title="Click to open the config file."
title="Click to open the config file"
onClick={configFilePath === '' ? () => {} : openFile}
className="py-2 remixui_compilerConfigPath"
>{configFilePath === '' ? 'No file selected.' : configFilePath}</span> }
@ -861,7 +875,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
}>
<a href="https://remix-ide.readthedocs.io/en/latest/running_js_scripts.html#compile-a-contract-and-run-a-script-on-the-fly" target="_blank" ><i className="pl-2 ml-2 mt-3 mb-1 fas fa-info text-dark"></i></a>
</OverlayTrigger>
<CopyToClipboard tip="Copy tag to use in contract NatSpec" getContent={() => '@custom:dev-run-script file_path'} direction='top'>
<CopyToClipboard tip="Click to copy the custom NatSpec tag" getContent={() => '@custom:dev-run-script file_path'} direction='top'>
<button className="btn remixui_copyButton ml-2 mt-3 mb-1 text-dark">
<i className="remixui_copyIcon far fa-copy" aria-hidden="true"></i>
</button>

@ -131,6 +131,12 @@ export class CompileTabLogic {
} else return false
}
async isFoundryProject () {
if (this.api.getFileManagerMode() === 'localhost') {
return await this.api.fileExists('foundry.toml')
} else return false
}
runCompiler (externalCompType) {
try {
if (this.api.getFileManagerMode() === 'localhost') {

@ -13,6 +13,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
const [state, setState] = useState({
isHardhatProject: false,
isTruffleProject: false,
isFoundryProject: false,
workspaceName: '',
currentFile,
configFilePath: 'compiler_config.json',
@ -67,9 +68,10 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
api.onSetWorkspace = async (isLocalhost: boolean, workspaceName: string) => {
const isHardhat = isLocalhost && await compileTabLogic.isHardhatProject()
const isTruffle = await compileTabLogic.isTruffleProject()
const isTruffle = isLocalhost && await compileTabLogic.isTruffleProject()
const isFoundry = isLocalhost && await compileTabLogic.isFoundryProject()
setState(prevState => {
return { ...prevState, currentFile, isHardhatProject: isHardhat, workspaceName: workspaceName, isTruffleProject: isTruffle }
return { ...prevState, currentFile, isHardhatProject: isHardhat, workspaceName: workspaceName, isTruffleProject: isTruffle, isFoundryProject: isFoundry }
})
}
@ -171,6 +173,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
isHardhatProject={state.isHardhatProject}
workspaceName={state.workspaceName}
isTruffleProject={state.isTruffleProject}
isFoundryProject={state.isFoundryProject}
compileTabLogic={compileTabLogic}
tooltip={toast}
modal={modal}

@ -11,6 +11,7 @@ export interface CompilerContainerProps {
compileTabLogic: CompileTabLogic,
isHardhatProject: boolean,
isTruffleProject: boolean,
isFoundryProject: boolean,
workspaceName: string,
tooltip: (message: string | JSX.Element) => void,
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,

@ -677,7 +677,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
<button
className="btn border w-50"
data-id="testTabGenerateTestFile"
title="Generate sample test file."
title="Generate a sample test file"
disabled={disableGenerateButton}
onClick={async () => {
await testTabLogic.generateTestFile((err:any) => { if (err) setToasterMsg(err)}) // eslint-disable-line @typescript-eslint/no-explicit-any

@ -15,7 +15,7 @@ const RenderCall = ({ tx, resolvedData, logs, index, plugin, showTableHash, txDe
const debug = (event, tx) => {
event.stopPropagation()
if (tx.isCall && tx.envMode !== 'vm') {
modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in Remix VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
} else {
plugin.event.trigger('debuggingRequested', [tx.hash])
}

@ -10,7 +10,7 @@ const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugi
const debug = (event, tx) => {
event.stopPropagation()
if (tx.isCall && tx.envMode !== 'vm') {
modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in Remix VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
} else {
plugin.event.trigger('debuggingRequested', [tx.hash])
}

@ -7,7 +7,7 @@ const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash,
const debug = (event, tx) => {
event.stopPropagation()
if (tx.isCall && tx.envMode !== 'vm') {
modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in JavaScript VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
modal('VM mode', 'Cannot debug this call. Debugging calls is only possible in Remix VM mode.', 'Ok', true, () => {}, 'Cancel', () => {})
} else {
plugin.event.trigger('debuggingRequested', [tx.hash])
}

@ -6,7 +6,9 @@ import { displayNotification, displayPopUp, fetchDirectoryError, fetchDirectoryR
import { listenOnPluginEvents, listenOnProviderEvents } from './events'
import { createWorkspaceTemplate, getWorkspaces, loadWorkspacePreset, setPlugin } from './workspace'
import { QueryParams } from '@remix-project/remix-lib'
import { fetchContractFromEtherscan } from '@remix-project/core-plugin' // eslint-disable-line
import JSZip from 'jszip'
import axios, { AxiosResponse } from 'axios'
export * from './events'
export * from './workspace'
@ -22,6 +24,21 @@ export type UrlParametersType = {
url: string
}
const basicWorkspaceInit = async (workspaces, workspaceProvider) => {
if (workspaces.length === 0) {
await createWorkspaceTemplate('default_workspace', 'remixDefault')
plugin.setWorkspace({ name: 'default_workspace', isLocalhost: false })
dispatch(setCurrentWorkspace('default_workspace'))
await loadWorkspacePreset('remixDefault')
} else {
if (workspaces.length > 0) {
workspaceProvider.setWorkspace(workspaces[workspaces.length - 1])
plugin.setWorkspace({ name: workspaces[workspaces.length - 1], isLocalhost: false })
dispatch(setCurrentWorkspace(workspaces[workspaces.length - 1]))
}
}
}
export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<any>) => {
if (filePanelPlugin) {
plugin = filePanelPlugin
@ -31,7 +48,6 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
const localhostProvider = filePanelPlugin.fileProviders.localhost
const params = queryParams.get() as UrlParametersType
const workspaces = await getWorkspaces() || []
dispatch(setWorkspaces(workspaces))
if (params.gist) {
await createWorkspaceTemplate('gist-sample', 'gist-template')
@ -44,19 +60,72 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
dispatch(setCurrentWorkspace('code-sample'))
const filePath = await loadWorkspacePreset('code-template')
plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(filePath))
} else {
if (workspaces.length === 0) {
await createWorkspaceTemplate('default_workspace', 'remixDefault')
plugin.setWorkspace({ name: 'default_workspace', isLocalhost: false })
dispatch(setCurrentWorkspace('default_workspace'))
await loadWorkspacePreset('remixDefault')
} else {
if (workspaces.length > 0) {
workspaceProvider.setWorkspace(workspaces[workspaces.length - 1])
plugin.setWorkspace({ name: workspaces[workspaces.length - 1], isLocalhost: false })
dispatch(setCurrentWorkspace(workspaces[workspaces.length - 1]))
} else if (window.location.pathname && window.location.pathname !== '/') {
let route = window.location.pathname
if (route.startsWith('/address/0x') && route.length === 51) {
const contractAddress = route.split('/')[2]
plugin.call('notification', 'toast', `Looking for contract(s) verified on different networks of Etherscan for contract address ${contractAddress} .....`)
let data
let count = 0
try {
let etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token')
if (!etherscanKey) etherscanKey = '2HKUX5ZVASZIKWJM8MIQVCRUVZ6JAWT531'
const networks = [
{id: 1, name: 'mainnet'},
{id: 3, name: 'ropsten'},
{id: 4, name: 'rinkeby'},
{id: 42, name: 'kovan'},
{id: 5, name: 'goerli'}
]
let found = false
const foundOnNetworks = []
for (const network of networks) {
const target = `/${network.name}/${contractAddress}`
try {
data = await fetchContractFromEtherscan(plugin, network, contractAddress, target, etherscanKey)
} catch (error) {
if ((error.message.startsWith('contract not verified on Etherscan') || error.message.startsWith('unable to retrieve contract data')) && network.id !== 5)
continue
else {
if (!found) await basicWorkspaceInit(workspaces, workspaceProvider)
break
}
}
found = true
foundOnNetworks.push(network.name)
await createWorkspaceTemplate('etherscan-code-sample', 'code-template')
plugin.setWorkspace({ name: 'etherscan-code-sample', isLocalhost: false })
dispatch(setCurrentWorkspace('etherscan-code-sample'))
let filePath
count = count + (Object.keys(data.compilationTargets)).length
for (filePath in data.compilationTargets)
await workspaceProvider.set(filePath, data.compilationTargets[filePath]['content'])
plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(filePath))
}
plugin.call('notification', 'toast', `Added ${count} verified contract${count === 1 ? '': 's'} from ${foundOnNetworks.join(',')} network${foundOnNetworks.length === 1 ? '': 's'} of Etherscan for contract address ${contractAddress} !!`)
} catch (error) {
await basicWorkspaceInit(workspaces, workspaceProvider)
}
}
} else if (route.endsWith('.sol')) {
if (route.includes('blob')) route = route.replace('/blob', '')
let response: AxiosResponse
try {
response = await axios.get(`https://raw.githubusercontent.com${route}`)
} catch (error) {
plugin.call('notification', 'toast', `cound not find ${route} on GitHub`)
await basicWorkspaceInit(workspaces, workspaceProvider)
}
if (response && response.status === 200) {
const content = response.data
await createWorkspaceTemplate('github-code-sample', 'code-template')
plugin.setWorkspace({ name: 'github-code-sample', isLocalhost: false })
dispatch(setCurrentWorkspace('github-code-sample'))
await workspaceProvider.set(route, content)
plugin.on('editor', 'editorMounted', async () => await plugin.fileManager.openFile(route))
} else await basicWorkspaceInit(workspaces, workspaceProvider)
} else await basicWorkspaceInit(workspaces, workspaceProvider)
} else {
await basicWorkspaceInit(workspaces, workspaceProvider)
}
listenOnPluginEvents(plugin)

@ -13,7 +13,8 @@
"npip": "npip",
"lint": "eslint ./src --ext .ts",
"build": "tsc -p ./ && chmod +x ./src/bin/remixd.js",
"dev": "nodemon"
"dev": "nodemon",
"postinstall": "node src/scripts/installSlither.js"
},
"publishConfig": {
"access": "public"

@ -63,6 +63,7 @@ function errorHandler (error: any, service: string) {
.description('Establish a two-way websocket connection between the local computer and Remix IDE for a folder')
.option('-u, --remix-ide <url>', 'URL of remix instance allowed to connect')
.option('-s, --shared-folder <path>', 'Folder to share with Remix IDE (Default: CWD)')
.option('-i, --install <name>', 'Module name to install locally (Supported: ["slither"])')
.option('-r, --read-only', 'Treat shared folder as read-only (experimental)')
.on('--help', function () {
console.log('\nExample:\n\n remixd -s ./shared_project -u http://localhost:8080')
@ -71,6 +72,11 @@ function errorHandler (error: any, service: string) {
await warnLatestVersion()
if(program.install && !program.readOnly) {
if (program.install.toLowerCase() === 'slither') require('./../scripts/installSlither')
process.exit(0)
}
if (!program.remixIde) {
console.log('\x1b[33m%s\x1b[0m', '[WARN] You can only connect to remixd from one of the supported origins.')
} else {

@ -0,0 +1,25 @@
const { execSync } = require('child_process') // eslint-disable-line
try {
const solcVersion = '0.8.15'
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: requires Python3.6+ (pip3) to be installed on your system`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc-select will be installed along with Slither to set different solc compiler versions.`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: checking pip3 availability ...`)
const pip3OP = execSync('pip3 --version')
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: pip3 found: ${pip3OP.toString()}`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: installing slither...`)
const slitherOP = execSync('pip3 install slither-analyzer')
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: slither installation output: ${slitherOP.toString()}`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: installing solc-select...`)
const solcSelectOP = execSync('pip3 install solc-select')
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc-select installation output: ${solcSelectOP.toString()}`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: installing solc ${solcVersion}...`)
const solcInstallOP = execSync(`solc-select install ${solcVersion}`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc installation output: ${solcInstallOP.toString()}`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: setting solc version to ${solcVersion}...`)
const solcUseOP = execSync(`solc-select use ${solcVersion}`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: solc setting installation output: ${solcUseOP.toString()}`)
console.log('\x1b[32m%s\x1b[0m', `[Slither Installation]: Slither is ready to use!`)
} catch (err) {
console.log('\x1b[31m%s\x1b[0m', `[Slither Installation]: Error occured: ${err}`)
}

@ -172,6 +172,7 @@
"core-js": "^3.6.5",
"deep-equal": "^1.0.1",
"document-register-element": "1.13.1",
"eslint-config-prettier": "^8.5.0",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
@ -275,7 +276,6 @@
"csslint": "^1.0.2",
"dotenv": "^8.2.0",
"eslint": "6.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "2.20.2",
"eslint-plugin-jsx-a11y": "6.4.1",
@ -313,7 +313,6 @@
"npm-run-all": "^4.0.2",
"nyc": "^13.3.0",
"onchange": "^3.2.1",
"prettier": "1.19.1",
"request": "^2.83.0",
"rimraf": "^2.6.1",
"selenium-standalone": "^8.0.4",

@ -1,65 +0,0 @@
{
"rulesDirectory": ["node_modules/@nrwl/workspace/src/tslint"],
"linterOptions": {
"exclude": ["**/*"]
},
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"deprecation": {
"severity": "warn"
},
"forin": true,
"import-blacklist": [true, "rxjs/Rx"],
"interface-over-type-literal": true,
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [true, "ignore-params"],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"prefer-const": true,
"radix": true,
"triple-equals": [true, "allow-null-check"],
"unified-signatures": true,
"variable-name": false,
"nx-enforce-module-boundaries": [
true,
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{ "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] }
]
}
]
}
}

@ -5463,22 +5463,17 @@ async-settle@^1.0.0:
dependencies:
async-done "^1.2.2"
async@0.9.x:
version "0.9.2"
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
integrity sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==
async@^2.4.0, async@^2.5.0, async@^2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
version "2.6.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==
dependencies:
lodash "^4.17.14"
async@^3.1.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.2.tgz#2eb7671034bb2194d45d30e31e24ec7e7f9670cd"
integrity sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==
async@^3.1.0, async@^3.2.3:
version "3.2.4"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
asynckit@^0.4.0:
version "0.4.0"
@ -6510,6 +6505,11 @@ boom@2.x.x:
dependencies:
hoek "2.x.x"
bootstrap@^5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.1.3.tgz#ba081b0c130f810fa70900acbc1c6d3c28fa8f34"
integrity sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==
borc@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/borc/-/borc-2.1.2.tgz#6ce75e7da5ce711b963755117dd1b187f6f8cf19"
@ -6566,6 +6566,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
brace@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/brace/-/brace-0.8.0.tgz#e826c6d5054cae5f607ad7b1c81236dd2cf01978"
@ -7345,7 +7352,7 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0, chalk@^4.1.0:
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@ -7940,7 +7947,7 @@ compression@^1.7.4:
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
concat-stream@^1.5.0, concat-stream@^1.5.2:
version "1.6.0"
@ -8813,7 +8820,7 @@ decimal.js@^10.2.1:
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==
decompress-response@^3.2.0, decompress-response@^3.3.0:
version "3.3.0"
@ -9421,11 +9428,11 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
ejs@^3.1.5, ejs@^3.1.6:
version "3.1.6"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a"
integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==
version "3.1.8"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b"
integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==
dependencies:
jake "^10.6.1"
jake "^10.8.5"
electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.857:
version "1.3.866"
@ -9743,12 +9750,10 @@ escodegen@^2.0.0:
optionalDependencies:
source-map "~0.6.1"
eslint-config-prettier@^6.11.0:
version "6.15.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9"
integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==
dependencies:
get-stdin "^6.0.0"
eslint-config-prettier@^8.5.0:
version "8.5.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1"
integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==
eslint-config-standard@^14.1.1:
version "14.1.1"
@ -10201,11 +10206,9 @@ events@^2.0.0:
integrity sha512-3Zmiobend8P9DjmKAty0Era4jV8oJ0yGYe2nJJAxgymF9+N8F2m0hhZiMoWtcfepExzNKZumFU3ksdQbInGWCg==
eventsource@^1.0.7:
version "1.1.0"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf"
integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==
dependencies:
original "^1.0.0"
version "1.1.2"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.2.tgz#bc75ae1c60209e7cb1541231980460343eaea7c2"
integrity sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
@ -10656,11 +10659,11 @@ file-uri-to-path@2:
integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==
filelist@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b"
integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==
version "1.0.4"
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
dependencies:
minimatch "^3.0.4"
minimatch "^5.0.1"
filename-regex@^2.0.0:
version "2.0.1"
@ -10703,7 +10706,7 @@ fill-range@^7.0.1:
filter-obj@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs=
integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
finalhandler@~1.1.2:
version "1.1.2"
@ -11248,7 +11251,16 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
get-intrinsic@^1.0.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.3"
get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
@ -11293,11 +11305,6 @@ get-stdin@^5.0.0:
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=
get-stdin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@ -11917,6 +11924,11 @@ has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2:
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has-to-string-tag-x@^1.2.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
@ -13299,11 +13311,11 @@ is-shared-array-buffer@^1.0.1:
integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
is-ssh@^1.3.0:
version "1.3.3"
resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.3.tgz#7f133285ccd7f2c2c7fc897b771b53d95a2b2c7e"
integrity sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==
version "1.4.0"
resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2"
integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==
dependencies:
protocols "^1.1.0"
protocols "^2.0.1"
is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
@ -13676,13 +13688,13 @@ it-to-stream@^0.1.1:
p-fifo "^1.0.0"
readable-stream "^3.6.0"
jake@^10.6.1:
version "10.8.2"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b"
integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==
jake@^10.8.5:
version "10.8.5"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==
dependencies:
async "0.9.x"
chalk "^2.4.2"
async "^3.2.3"
chalk "^4.0.2"
filelist "^1.0.1"
minimatch "^3.0.4"
@ -15820,7 +15832,7 @@ minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
minimatch@*, minimatch@3.0.4, minimatch@^3.0.4:
minimatch@*, minimatch@3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@ -15834,6 +15846,20 @@ minimatch@^3.0.0, minimatch@^3.0.2:
dependencies:
brace-expansion "^1.0.0"
minimatch@^3.0.4:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minimatch@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
dependencies:
brace-expansion "^2.0.1"
minimist-options@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
@ -17295,11 +17321,16 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"
object-inspect@^1.11.0, object-inspect@^1.9.0, object-inspect@~1.11.0:
object-inspect@^1.11.0, object-inspect@~1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==
object-inspect@^1.9.0:
version "1.12.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
object-is@^1.0.1:
version "1.1.5"
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
@ -17571,13 +17602,6 @@ ordered-read-streams@^1.0.0:
dependencies:
readable-stream "^2.0.1"
original@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
dependencies:
url-parse "^1.4.3"
os-browserify@^0.3.0, os-browserify@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
@ -18044,10 +18068,10 @@ parse-passwd@^1.0.0:
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
parse-path@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.3.tgz#82d81ec3e071dcc4ab49aa9f2c9c0b8966bb22bf"
integrity sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==
parse-path@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.4.tgz#4bf424e6b743fb080831f03b536af9fc43f0ffea"
integrity sha512-Z2lWUis7jlmXC1jeOG9giRO2+FsuyNipeQ43HAjqAZjwSe3SEf+q/84FGPHoso3kyntbxa4c4i77t3m6fGf8cw==
dependencies:
is-ssh "^1.3.0"
protocols "^1.4.0"
@ -18055,13 +18079,13 @@ parse-path@^4.0.0:
query-string "^6.13.8"
parse-url@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.0.tgz#f5dd262a7de9ec00914939220410b66cff09107d"
integrity sha512-cYyojeX7yIIwuJzledIHeLUBVJ6COVLeT4eF+2P6aKVzwvgKQPndCBv3+yQ7pcWjqToYwaligxzSYNNmGoMAvw==
version "6.0.2"
resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-6.0.2.tgz#4a30b057bfc452af64512dfb1a7755c103db3ea1"
integrity sha512-uCSjOvD3T+6B/sPWhR+QowAZcU/o4bjPrVBQBGFxcDF6J6FraCGIaDBsdoQawiaaAVdHvtqBe3w3vKlfBKySOQ==
dependencies:
is-ssh "^1.3.0"
normalize-url "^6.1.0"
parse-path "^4.0.0"
parse-path "^4.0.4"
protocols "^1.4.0"
parse5@4.0.0:
@ -18719,11 +18743,6 @@ preserve@^0.2.0:
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
prettier@1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
pretty-format@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
@ -18856,11 +18875,16 @@ protocol-buffers-schema@^3.3.1:
resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03"
integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==
protocols@^1.1.0, protocols@^1.4.0:
protocols@^1.4.0:
version "1.4.8"
resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8"
integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg==
protocols@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86"
integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==
protoduck@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f"
@ -19007,13 +19031,20 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@^6.4.0, qs@^6.9.4:
qs@^6.4.0:
version "6.10.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a"
integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==
dependencies:
side-channel "^1.0.4"
qs@^6.9.4:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
@ -21321,7 +21352,7 @@ strict-uri-encode@^1.0.0:
strict-uri-encode@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
string-hash@^1.1.1:
version "1.1.3"
@ -22909,7 +22940,7 @@ url-parse-lax@^3.0.0:
dependencies:
prepend-http "^2.0.0"
url-parse@^1.4.3, url-parse@^1.5.3:
url-parse@^1.5.3:
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==

Loading…
Cancel
Save