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

pull/3176/head
filip mertens 2 years ago
commit b634cf8dc8
  1. 6
      .circleci/config.yml
  2. 19
      apps/debugger/src/app/debugger-api.ts
  3. 4
      apps/remix-ide-e2e/src/commands/waitForElementContainsText.ts
  4. 1
      apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx
  5. 10
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  6. 1
      apps/remix-ide-e2e/src/tests/libraryDeployment.test.ts
  7. 4
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  8. 2
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  9. 2
      apps/remix-ide/src/app/tabs/locales/en-US.js
  10. 1
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  11. 6
      apps/remix-ide/src/blockchain/blockchain.js
  12. 8
      libs/remix-analyzer/package.json
  13. 6
      libs/remix-astwalker/package.json
  14. 3
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  15. 28
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  16. 6
      libs/remix-core-plugin/src/lib/constants/uups.ts
  17. 13
      libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts
  18. 10
      libs/remix-debug/package.json
  19. 10
      libs/remix-debug/src/Ethdebugger.ts
  20. 48
      libs/remix-debug/src/debugger/debugger.ts
  21. 2
      libs/remix-debug/src/solidity-decoder/decodeInfo.ts
  22. 242
      libs/remix-debug/src/solidity-decoder/internalCallTree.ts
  23. 4
      libs/remix-debug/src/solidity-decoder/localDecoder.ts
  24. 19
      libs/remix-debug/src/solidity-decoder/solidityProxy.ts
  25. 2
      libs/remix-debug/src/solidity-decoder/types/StringType.ts
  26. 12
      libs/remix-debug/src/source/offsetToLineColumnConverter.ts
  27. 23
      libs/remix-debug/src/source/sourceLocationTracker.ts
  28. 2
      libs/remix-debug/test/decoder/localDecoder.ts
  29. 86
      libs/remix-debug/test/decoder/localsTests/int.ts
  30. 4
      libs/remix-lib/package.json
  31. 23
      libs/remix-lib/src/util.ts
  32. 6
      libs/remix-lib/test/util.ts
  33. 6
      libs/remix-simulator/package.json
  34. 6
      libs/remix-solidity/package.json
  35. 2
      libs/remix-solidity/src/compiler/compiler-utils.ts
  36. 14
      libs/remix-tests/package.json
  37. 4
      libs/remix-tests/tests/testRunner.cli.spec.ts
  38. 0
      libs/remix-tests/tests/testRunner.spec.__
  39. 6
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  40. 5
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  41. 6
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  42. 34
      libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx
  43. 13
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  44. 2
      libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts
  45. 10
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx
  46. 6
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger-head.tsx
  47. 4
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/vm-debugger.tsx
  48. 24
      libs/remix-ui/debugger-ui/src/reducers/assembly-items.ts
  49. 2
      libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx
  50. 3
      libs/remix-ui/helper/src/lib/components/custom-tooltip.tsx
  51. 3
      libs/remix-ui/helper/src/types/customtooltip.ts
  52. 14
      libs/remix-ui/home-tab/src/lib/components/customButtonGroupAsArrows.tsx
  53. 8
      libs/remix-ui/home-tab/src/lib/components/customNavButtons.tsx
  54. 20
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  55. 6
      libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx
  56. 30
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  57. 6
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  58. 24
      libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx
  59. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  60. 3
      libs/remix-ui/publish-to-storage/src/lib/publishToIPFS.tsx
  61. 36
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  62. 4
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  63. 18
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  64. 2
      libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx
  65. 2
      libs/remix-ui/run-tab/src/lib/components/network.tsx
  66. 6
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  67. 2
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  68. 4
      libs/remix-ui/run-tab/src/lib/css/run-tab.css
  69. 18
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  70. 2
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  71. 4
      libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx
  72. 2
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  73. 7
      libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx
  74. 9
      libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx
  75. 6
      libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx
  76. 2
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  77. 6
      libs/remix-url-resolver/package.json
  78. 4
      libs/remix-ws-templates/package.json
  79. 2
      libs/remixd/package.json
  80. 2
      package.json
  81. 21
      yarn.lock

@ -64,6 +64,12 @@ jobs:
- v1-deps-{{ checksum "yarn.lock" }} - v1-deps-{{ checksum "yarn.lock" }}
- run: yarn - run: yarn
- run: cd dist/libs/remix-tests && yarn - run: cd dist/libs/remix-tests && yarn
- run: cd dist/libs/remix-tests && yarn add @remix-project/remix-url-resolver ../../libs/remix-url-resolver
- run: cd dist/libs/remix-tests && yarn add @remix-project/remix-lib ../../libs/remix-lib
- run: cd dist/libs/remix-tests && yarn add @remix-project/remix-solidity ../../libs/remix-solidity
- run: cd dist/libs/remix-tests && yarn add @remix-project/remix-simulator ../../libs/remix-simulator
- run: cd dist/libs/remix-tests && ./bin/remix-tests ./../../../libs/remix-tests/tests/examples_0/assert_ok_test.sol
- run: node dist/libs/remix-tests/bin/remix-tests ./libs/remix-tests/tests/examples_0/assert_ok_test.sol
- run: yarn run test:libs - run: yarn run test:libs
remix-ide-browser: remix-ide-browser:

@ -1,7 +1,7 @@
import Web3 from 'web3' import Web3 from 'web3'
import remixDebug, { TransactionDebugger as Debugger } from '@remix-project/remix-debug' import remixDebug, { TransactionDebugger as Debugger } from '@remix-project/remix-debug'
import { CompilerAbstract } from '@remix-project/remix-solidity' import { CompilerAbstract } from '@remix-project/remix-solidity'
import { lineText } from '@remix-ui/editor'
export const DebuggerApiMixin = (Base) => class extends Base { export const DebuggerApiMixin = (Base) => class extends Base {
@ -39,10 +39,25 @@ export const DebuggerApiMixin = (Base) => class extends Base {
async discardHighlight () { async discardHighlight () {
await this.call('editor', 'discardHighlight') await this.call('editor', 'discardHighlight')
await this.call('editor', 'discardLineTexts' as any)
} }
async highlight (lineColumnPos, path) { async highlight (lineColumnPos, path, rawLocation, stepDetail, lineGasCost) {
await this.call('editor', 'highlight', lineColumnPos, path, '', { focus: true }) await this.call('editor', 'highlight', lineColumnPos, path, '', { focus: true })
const label = `${stepDetail.op} costs ${stepDetail.gasCost} gas - this line costs ${lineGasCost} gas - ${stepDetail.gas} gas left`
const linetext: lineText = {
content: label,
position: lineColumnPos,
hide: false,
className: 'text-muted small',
afterContentClassName: 'text-muted small fas fa-gas-pump pl-4',
from: 'debugger',
hoverMessage: [{
value: label,
},
],
}
await this.call('editor', 'addLineText' as any, linetext, path)
} }
async getFile (path) { async getFile (path) {

@ -4,8 +4,10 @@ import EventEmitter from 'events'
class WaitForElementContainsText extends EventEmitter { class WaitForElementContainsText extends EventEmitter {
command (this: NightwatchBrowser, id: string, value: string, timeout = 10000): NightwatchBrowser { command (this: NightwatchBrowser, id: string, value: string, timeout = 10000): NightwatchBrowser {
let waitId // eslint-disable-line let waitId // eslint-disable-line
let currentValue
const runid = setInterval(() => { const runid = setInterval(() => {
this.api.getText(id, (result) => { this.api.getText(id, (result) => {
currentValue = result.value
if (typeof result.value === 'string' && result.value.indexOf(value) !== -1) { if (typeof result.value === 'string' && result.value.indexOf(value) !== -1) {
clearInterval(runid) clearInterval(runid)
clearTimeout(waitId) clearTimeout(waitId)
@ -17,7 +19,7 @@ class WaitForElementContainsText extends EventEmitter {
waitId = setTimeout(() => { waitId = setTimeout(() => {
clearInterval(runid) clearInterval(runid)
this.api.assert.fail(`TimeoutError: An error occurred while running .waitForElementContainsText() command on ${id} after ${timeout} milliseconds`) this.api.assert.fail(`TimeoutError: An error occurred while running .waitForElementContainsText() command on ${id} after ${timeout} milliseconds. expected: ${value} - got: ${currentValue}`)
}, timeout) }, timeout)
return this return this
} }

@ -110,6 +110,7 @@ function App () {
placeholder="Enter payload here..." placeholder="Enter payload here..."
value={payload} value={payload}
onChange={handleChange} onChange={handleChange}
data-id="payload-input"
/> />
{profiles.map((profile: Profile) => { {profiles.map((profile: Profile) => {
const methods = profile.methods.map((method: string) => { const methods = profile.methods.map((method: string) => {

@ -97,11 +97,6 @@ module.exports = {
locateStrategy: 'xpath', locateStrategy: 'xpath',
selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"545")]', selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"545")]',
}) })
.goToVMTraceStep(10)
.waitForElementVisible({
locateStrategy: 'xpath',
selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"10")]',
})
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`constructor (string memory name_, string memory symbol_) { browser.assert.ok(content.indexOf(`constructor (string memory name_, string memory symbol_) {
_name = name_; _name = name_;
@ -109,6 +104,11 @@ module.exports = {
}`) !== -1, }`) !== -1,
'current displayed content is not from the ERC20 source code') 'current displayed content is not from the ERC20 source code')
}) })
.goToVMTraceStep(10)
.waitForElementVisible({
locateStrategy: 'xpath',
selector: '//*[@data-id="treeViewLivm trace step" and contains(.,"10")]',
})
}, },
'Should display correct source highlighting while debugging a contract which has ABIEncoderV2 #group2': function (browser: NightwatchBrowser) { 'Should display correct source highlighting while debugging a contract which has ABIEncoderV2 #group2': function (browser: NightwatchBrowser) {

@ -21,6 +21,7 @@ module.exports = {
let addressRef: string let addressRef: string
browser.verifyContracts(['test']) browser.verifyContracts(['test'])
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.click('#selectExEnv')
.selectContract('test') .selectContract('test')
.createContract('') .createContract('')
.getAddressAtPosition(0, (address) => { .getAddressAtPosition(0, (address) => {

@ -50,7 +50,7 @@ const debugValues = async function (browser: NightwatchBrowser, field: string, e
const setPayload = async (browser: NightwatchBrowser, payload: any) => { const setPayload = async (browser: NightwatchBrowser, payload: any) => {
return new Promise((resolve) => { return new Promise((resolve) => {
if (typeof payload !== 'string') payload = JSON.stringify(payload) if (typeof payload !== 'string') payload = JSON.stringify(payload)
browser.clearValue('//*[@id="payload"]').setValue('//*[@id="payload"]', payload, (result) => { browser.clearValue('//*[@id="payload"]').pause(500).setValue('//*[@id="payload"]', payload, (result) => {
resolve(result) resolve(result)
}) })
}) })
@ -419,7 +419,7 @@ module.exports = {
.addFile('test_modal.js', { content: testModalToasterApi }) .addFile('test_modal.js', { content: testModalToasterApi })
.executeScriptInTerminal('remix.execute(\'test_modal.js\')') .executeScriptInTerminal('remix.execute(\'test_modal.js\')')
.useCss() .useCss()
.waitForElementVisible('*[data-id="test_id_1_ModalDialogModalBody-react"]', 60000) .waitForElementVisible('*[data-id="test_id_1_ModalDialogModalBody-react"]', 65000)
.assert.containsText('*[data-id="test_id_1_ModalDialogModalBody-react"]', 'message 1') .assert.containsText('*[data-id="test_id_1_ModalDialogModalBody-react"]', 'message 1')
.modalFooterOKClick('test_id_1_') .modalFooterOKClick('test_id_1_')
// check the script runner notifications // check the script runner notifications

@ -290,7 +290,7 @@ module.exports = {
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000)
.waitForElementVisible('*[data-id="dropdownPanelSolidityLocals"]').pause(1000) .waitForElementVisible('*[data-id="dropdownPanelSolidityLocals"]').pause(1000)
.waitForElementContainsText('*[data-id="solidityLocals"]', 'no locals', 60000) .waitForElementContainsText('*[data-id="solidityLocals"]', 'No data available', 60000)
.goToVMTraceStep(316) .goToVMTraceStep(316)
.waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'checkWinningProposalFailed()', 60000)
.waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000) .waitForElementContainsText('*[data-id="functionPanel"]', 'vote(proposal)', 60000)

@ -60,7 +60,7 @@ export default {
'filePanel.compileForNahmii': 'Compile for Nahmii', 'filePanel.compileForNahmii': 'Compile for Nahmii',
'filePanel.createNewFile': 'Create New File', 'filePanel.createNewFile': 'Create New File',
'filePanel.createNewFolder': 'Create New Folder', 'filePanel.createNewFolder': 'Create New Folder',
'filePanel.publishToGist': 'Publish all the current workspace files (only root) to a github gist', 'filePanel.publishToGist': 'Publish all the current workspace files to a github gist',
'filePanel.uploadFile': 'Load a local file into current workspace', 'filePanel.uploadFile': 'Load a local file into current workspace',
'filePanel.updateGist': 'Update the current [gist] explorer', 'filePanel.updateGist': 'Update the current [gist] explorer',

@ -4018,6 +4018,7 @@ input[type="submit"].btn-block {
.card-body { .card-body {
flex: 1 1 auto; flex: 1 1 auto;
padding: 1.25rem; padding: 1.25rem;
color: #b2b8cd;
} }
.card-title { .card-title {
color: #DFE1EA; color: #DFE1EA;

@ -11,7 +11,7 @@ import InjectedProvider from './providers/injected.js'
import NodeProvider from './providers/node.js' import NodeProvider from './providers/node.js'
import { execution, EventManager, helpers } from '@remix-project/remix-lib' import { execution, EventManager, helpers } from '@remix-project/remix-lib'
import { etherScanLink } from './helper' import { etherScanLink } from './helper'
import { logBuilder, cancelUpgradeMsg, cancelProxyMsg } from "@remix-ui/helper" import { logBuilder, cancelUpgradeMsg, cancelProxyMsg, addressToString } from "@remix-ui/helper"
const { txFormat, txExecution, typeConversion, txListener: Txlistener, TxRunner, TxRunnerWeb3, txHelper } = execution const { txFormat, txExecution, typeConversion, txListener: Txlistener, TxRunner, TxRunnerWeb3, txHelper } = execution
const { txResultHelper: resultToRemixTx } = helpers const { txResultHelper: resultToRemixTx } = helpers
const packageJson = require('../../../../package.json') const packageJson = require('../../../../package.json')
@ -180,7 +180,7 @@ export class Blockchain extends Plugin {
if (networkInfo.name === 'VM') this.config.set('vm/proxy', address) if (networkInfo.name === 'VM') this.config.set('vm/proxy', address)
else this.config.set(`${networkInfo.name}/${networkInfo.currentFork}/${networkInfo.id}/proxy`, address) else this.config.set(`${networkInfo.name}/${networkInfo.currentFork}/${networkInfo.id}/proxy`, address)
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful']) _paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful'])
return this.call('udapp', 'resolveContractAndAddInstance', implementationContractObject, address) this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name)
} }
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
@ -223,7 +223,7 @@ export class Blockchain extends Plugin {
return this.call('terminal', 'logHtml', log) return this.call('terminal', 'logHtml', log)
} }
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful']) _paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful'])
return this.call('udapp', 'resolveContractAndAddInstance', newImplementationContractObject, proxyAddress) this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name)
} }
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb) this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-analyzer", "name": "@remix-project/remix-analyzer",
"version": "0.5.28", "version": "0.5.29",
"description": "Tool to perform static analysis on Solidity smart contracts", "description": "Tool to perform static analysis on Solidity smart contracts",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -22,8 +22,8 @@
"@ethereumjs/block": "^3.5.1", "@ethereumjs/block": "^3.5.1",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-astwalker": "^0.0.49", "@remix-project/remix-astwalker": "^0.0.50",
"@remix-project/remix-lib": "^0.5.19", "@remix-project/remix-lib": "^0.5.20",
"async": "^2.6.2", "async": "^2.6.2",
"ethereumjs-util": "^7.0.10", "ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2", "ethers": "^5.4.2",
@ -52,5 +52,5 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2cec195f5a3678b17745155536a1714d9b1a5694" "gitHead": "569f7d53f6f218d99aa8281929b541ab7e00058f"
} }

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

@ -198,8 +198,7 @@ export class CompilerArtefacts extends Plugin {
return this.compilersArtefactsPerFile[file] return this.compilersArtefactsPerFile[file]
} }
// compilerData is a CompilerAbstract object addResolvedContract (address: string, compilerData: CompilerAbstract) {
addResolvedContract (address, compilerData) {
this.compilersArtefacts[address] = compilerData this.compilersArtefacts[address] = compilerData
} }

@ -4,6 +4,7 @@ import { util } from '@remix-project/remix-lib'
import { toChecksumAddress } from 'ethereumjs-util' import { toChecksumAddress } from 'ethereumjs-util'
import { fetchContractFromEtherscan } from './helpers/fetch-etherscan' import { fetchContractFromEtherscan } from './helpers/fetch-etherscan'
import { fetchContractFromSourcify } from './helpers/fetch-sourcify' import { fetchContractFromSourcify } from './helpers/fetch-sourcify'
import { UUPSDeployedByteCode, UUPSCompilerVersion } from './constants/uups'
const profile = { const profile = {
name: 'fetchAndCompile', name: 'fetchAndCompile',
@ -48,6 +49,33 @@ export class FetchAndCompile extends Plugin {
if (resolved) return resolved if (resolved) return resolved
if (this.unresolvedAddresses.includes(contractAddress)) return localCompilation() if (this.unresolvedAddresses.includes(contractAddress)) return localCompilation()
if (codeAtAddress === '0x' + UUPSDeployedByteCode) { // proxy
const settings = {
version: UUPSCompilerVersion,
language: 'Solidity',
evmVersion: null,
optimize: false,
runs: 0
}
const compilationTargets = {
'proxy.sol': { content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/proxy/ERC1967/ERC1967Proxy.sol";' }
}
const compData = await compile(
compilationTargets,
settings,
async (url, cb) => {
// we first try to resolve the content from the compilation target using a more appropiate path
const path = `${targetPath}/${url}`
if (compilationTargets[path] && compilationTargets[path].content) {
return cb(null, compilationTargets[path].content)
} else {
await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message))
}
})
await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compData)
return compData
}
// sometimes when doing an internal call, the only available artifact is the Solidity interface. // sometimes when doing an internal call, the only available artifact is the Solidity interface.
// resolving addresses of internal call would allow to step over the source code, even if the declaration was made using an Interface. // resolving addresses of internal call would allow to step over the source code, even if the declaration was made using an Interface.

File diff suppressed because one or more lines are too long

@ -13,9 +13,11 @@ const profile = {
export class OffsetToLineColumnConverter extends Plugin { export class OffsetToLineColumnConverter extends Plugin {
lineBreakPositionsByContent: Record<number, Array<number>> lineBreakPositionsByContent: Record<number, Array<number>>
sourceMappingDecoder: any sourceMappingDecoder: any
offsetConvertion: any
constructor () { constructor () {
super(profile) super(profile)
this.lineBreakPositionsByContent = {} this.lineBreakPositionsByContent = {}
this.offsetConvertion = {}
this.sourceMappingDecoder = sourceMappingDecoder this.sourceMappingDecoder = sourceMappingDecoder
} }
@ -45,7 +47,15 @@ export class OffsetToLineColumnConverter extends Plugin {
} }
} }
} }
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
const token = `${rawLocation.start}:${rawLocation.length}:${file}`
if (this.offsetConvertion[token]) {
return this.offsetConvertion[token]
} else {
const convertion = this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
this.offsetConvertion[token] = convertion
return convertion
}
} }
/** /**
@ -64,6 +74,7 @@ export class OffsetToLineColumnConverter extends Plugin {
*/ */
clear () { clear () {
this.lineBreakPositionsByContent = {} this.lineBreakPositionsByContent = {}
this.offsetConvertion = {}
} }
/** /**

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-debug", "name": "@remix-project/remix-debug",
"version": "0.5.19", "version": "0.5.20",
"description": "Tool to debug Ethereum transactions", "description": "Tool to debug Ethereum transactions",
"contributors": [ "contributors": [
{ {
@ -22,9 +22,9 @@
"@ethereumjs/common": "^2.5.0", "@ethereumjs/common": "^2.5.0",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-astwalker": "^0.0.49", "@remix-project/remix-astwalker": "^0.0.50",
"@remix-project/remix-lib": "^0.5.19", "@remix-project/remix-lib": "^0.5.20",
"@remix-project/remix-simulator": "^0.2.19", "@remix-project/remix-simulator": "^0.2.20",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.2", "async": "^2.6.2",
"color-support": "^1.1.3", "color-support": "^1.1.3",
@ -68,5 +68,5 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2cec195f5a3678b17745155536a1714d9b1a5694" "gitHead": "569f7d53f6f218d99aa8281929b541ab7e00058f"
} }

@ -32,9 +32,11 @@ export class Ethdebugger {
storageResolver storageResolver
callTree callTree
breakpointManager breakpointManager
offsetToLineColumnConverter
constructor (opts) { constructor (opts) {
this.compilationResult = opts.compilationResult || function (contractAddress) { return null } this.compilationResult = opts.compilationResult || function (contractAddress) { return null }
this.offsetToLineColumnConverter = opts.offsetToLineColumnConverter
this.web3 = opts.web3 this.web3 = opts.web3
this.opts = opts this.opts = opts
@ -49,7 +51,8 @@ export class Ethdebugger {
this.traceManager, this.traceManager,
this.solidityProxy, this.solidityProxy,
this.codeManager, this.codeManager,
{ ...opts, includeLocalVariables }) { ...opts, includeLocalVariables },
this.offsetToLineColumnConverter)
} }
setManagers () { setManagers () {
@ -63,7 +66,8 @@ export class Ethdebugger {
this.traceManager, this.traceManager,
this.solidityProxy, this.solidityProxy,
this.codeManager, this.codeManager,
{ ...this.opts, includeLocalVariables }) { ...this.opts, includeLocalVariables },
this.offsetToLineColumnConverter)
} }
resolveStep (index) { resolveStep (index) {
@ -71,7 +75,7 @@ export class Ethdebugger {
} }
setCompilationResult (compilationResult) { setCompilationResult (compilationResult) {
this.solidityProxy.reset((compilationResult && compilationResult.data) || {}) this.solidityProxy.reset((compilationResult && compilationResult.data) || {}, (compilationResult && compilationResult.source && compilationResult.source.sources) || {})
} }
async sourceLocationFromVMTraceIndex (address, stepIndex) { async sourceLocationFromVMTraceIndex (address, stepIndex) {

@ -14,6 +14,8 @@ export class Debugger {
breakPointManager breakPointManager
step_manager // eslint-disable-line camelcase step_manager // eslint-disable-line camelcase
vmDebuggerLogic vmDebuggerLogic
currentFile = -1
currentLine = -1
constructor (options) { constructor (options) {
this.event = new EventManager() this.event = new EventManager()
@ -26,7 +28,8 @@ export class Debugger {
this.debugger = new Ethdebugger({ this.debugger = new Ethdebugger({
web3: options.web3, web3: options.web3,
debugWithGeneratedSources: options.debugWithGeneratedSources, debugWithGeneratedSources: options.debugWithGeneratedSources,
compilationResult: this.compilationResult compilationResult: this.compilationResult,
offsetToLineColumnConverter: this.offsetToLineColumnConverter
}) })
const { traceManager, callTree, solidityProxy } = this.debugger const { traceManager, callTree, solidityProxy } = this.debugger
@ -73,35 +76,56 @@ export class Debugger {
const compilationResultForAddress = await this.compilationResult(address) const compilationResultForAddress = await this.compilationResult(address)
if (!compilationResultForAddress) { if (!compilationResultForAddress) {
this.event.trigger('newSourceLocation', [null]) this.event.trigger('newSourceLocation', [null])
this.currentFile = -1
this.currentLine = -1
this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null])
return return
} }
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then(async (rawLocation) => { this.debugger.callTree.getValidSourceLocationFromVMTraceIndexFromCache(address, index, compilationResultForAddress.data.contracts).then(async (rawLocationAndOpcode) => {
if (compilationResultForAddress && compilationResultForAddress.data) { if (compilationResultForAddress && compilationResultForAddress.data) {
const rawLocation = rawLocationAndOpcode.sourceLocation
const stepDetail = rawLocationAndOpcode.stepDetail
const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address)
const astSources = Object.assign({}, compilationResultForAddress.data.sources)
const sources = Object.assign({}, compilationResultForAddress.source.sources) const lineColumnPos = rawLocationAndOpcode.lineColumnPos
if (generatedSources) {
for (const genSource of generatedSources) { let lineGasCostObj = null
astSources[genSource.name] = { id: genSource.id, ast: genSource.ast } try {
sources[genSource.name] = { content: genSource.contents } lineGasCostObj = await this.debugger.callTree.getGasCostPerLine(rawLocation.file, lineColumnPos.start.line)
} } catch (e) {
console.log(e)
} }
const lineColumnPos = await this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources) this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address, stepDetail, (lineGasCostObj && lineGasCostObj.gasCost) || -1])
this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address])
this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [rawLocation]) this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [rawLocation])
if (this.currentFile !== rawLocation.file || this.currentLine !== lineColumnPos.start.line) {
const instructionIndexes = lineGasCostObj.indexes.map((index) => { // translate from vmtrace index to instruction index
return this.debugger.codeManager.getInstructionIndex(address, index)
})
this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [instructionIndexes, lineColumnPos.start.line ])
this.currentFile = rawLocation.file
this.currentLine = lineColumnPos.start.line
}
} else { } else {
this.event.trigger('newSourceLocation', [null]) this.event.trigger('newSourceLocation', [null])
this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null]) this.currentFile = -1
this.currentLine = -1
this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null])
} }
}).catch((_error) => { }).catch((_error) => {
this.event.trigger('newSourceLocation', [null]) this.event.trigger('newSourceLocation', [null])
this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null]) this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null])
this.currentFile = -1
this.currentLine = -1
this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null])
}) })
// }) // })
} catch (error) { } catch (error) {
this.event.trigger('newSourceLocation', [null]) this.event.trigger('newSourceLocation', [null])
this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null]) this.vmDebuggerLogic.event.trigger('sourceLocationChanged', [null])
this.currentFile = -1
this.currentLine = -1
this.vmDebuggerLogic.event.trigger('lineGasCostChanged', [null])
return console.log(error) return console.log(error)
} }
} }

@ -241,9 +241,7 @@ function getStructMembers (type, stateDefinitions, contractName, location) {
if (type.indexOf('.') === -1) { if (type.indexOf('.') === -1) {
type = contractName + '.' + type type = contractName + '.' + type
} }
if (!contractName) {
contractName = type.split('.')[0] contractName = type.split('.')[0]
}
const state = stateDefinitions[contractName] const state = stateDefinitions[contractName]
if (state) { if (state) {
for (const dec of state.stateDefinitions) { for (const dec of state.stateDefinitions) {

@ -7,6 +7,16 @@ import { parseType } from './decodeInfo'
import { isContractCreation, isCallInstruction, isCreateInstruction, isJumpDestInstruction } from '../trace/traceHelper' import { isContractCreation, isCallInstruction, isCreateInstruction, isJumpDestInstruction } from '../trace/traceHelper'
import { extractLocationFromAstVariable } from './types/util' import { extractLocationFromAstVariable } from './types/util'
export type StepDetail = {
depth: number,
gas: number | string,
gasCost: number,
memory: number[],
op: string,
pc: number,
stack: number[],
}
/** /**
* Tree representing internal jump into function. * Tree representing internal jump into function.
* Triggers `callTreeReady` event when tree is ready * Triggers `callTreeReady` event when tree is ready
@ -27,6 +37,15 @@ export class InternalCallTree {
functionDefinitionByFile functionDefinitionByFile
astWalker astWalker
reducedTrace reducedTrace
locationAndOpcodePerVMTraceIndex: {
[Key: number]: any
}
gasCostPerLine
offsetToLineColumnConverter
pendingConstructorExecutionAt: number
pendingConstructorId: number
pendingConstructor
constructorsStartExecution
/** /**
* constructor * constructor
@ -37,14 +56,16 @@ export class InternalCallTree {
* @param {Object} codeManager - code manager * @param {Object} codeManager - code manager
* @param {Object} opts - { includeLocalVariables, debugWithGeneratedSources } * @param {Object} opts - { includeLocalVariables, debugWithGeneratedSources }
*/ */
constructor (debuggerEvent, traceManager, solidityProxy, codeManager, opts) { constructor (debuggerEvent, traceManager, solidityProxy, codeManager, opts, offsetToLineColumnConverter?) {
this.includeLocalVariables = opts.includeLocalVariables this.includeLocalVariables = opts.includeLocalVariables
this.debugWithGeneratedSources = opts.debugWithGeneratedSources this.debugWithGeneratedSources = opts.debugWithGeneratedSources
this.event = new EventManager() this.event = new EventManager()
this.solidityProxy = solidityProxy this.solidityProxy = solidityProxy
this.traceManager = traceManager this.traceManager = traceManager
this.offsetToLineColumnConverter = offsetToLineColumnConverter
this.sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: opts.debugWithGeneratedSources }) this.sourceLocationTracker = new SourceLocationTracker(codeManager, { debugWithGeneratedSources: opts.debugWithGeneratedSources })
debuggerEvent.register('newTraceLoaded', (trace) => { debuggerEvent.register('newTraceLoaded', (trace) => {
const time = Date.now()
this.reset() this.reset()
if (!this.solidityProxy.loaded()) { if (!this.solidityProxy.loaded()) {
this.event.trigger('callTreeBuildFailed', ['compilation result not loaded. Cannot build internal call tree']) this.event.trigger('callTreeBuildFailed', ['compilation result not loaded. Cannot build internal call tree'])
@ -52,11 +73,17 @@ export class InternalCallTree {
// each recursive call to buildTree represent a new context (either call, delegatecall, internal function) // each recursive call to buildTree represent a new context (either call, delegatecall, internal function)
const calledAddress = traceManager.getCurrentCalledAddressAt(0) const calledAddress = traceManager.getCurrentCalledAddressAt(0)
const isCreation = isContractCreation(calledAddress) const isCreation = isContractCreation(calledAddress)
buildTree(this, 0, '', true, isCreation).then((result) => {
const scopeId = '1'
this.scopeStarts[0] = scopeId
this.scopes[scopeId] = { firstStep: 0, locals: {}, isCreation, gasCost: 0 }
buildTree(this, 0, scopeId, isCreation).then((result) => {
if (result.error) { if (result.error) {
this.event.trigger('callTreeBuildFailed', [result.error]) this.event.trigger('callTreeBuildFailed', [result.error])
} else { } else {
createReducedTrace(this, traceManager.trace.length - 1) createReducedTrace(this, traceManager.trace.length - 1)
console.log('call tree build lasts ', (Date.now() - time) / 1000)
this.event.trigger('callTreeReady', [this.scopes, this.scopeStarts]) this.event.trigger('callTreeReady', [this.scopes, this.scopeStarts])
} }
}, (reason) => { }, (reason) => {
@ -85,10 +112,16 @@ export class InternalCallTree {
this.functionCallStack = [] this.functionCallStack = []
this.functionDefinitionsByScope = {} this.functionDefinitionsByScope = {}
this.scopeStarts = {} this.scopeStarts = {}
this.gasCostPerLine = {}
this.variableDeclarationByFile = {} this.variableDeclarationByFile = {}
this.functionDefinitionByFile = {} this.functionDefinitionByFile = {}
this.astWalker = new AstWalker() this.astWalker = new AstWalker()
this.reducedTrace = [] this.reducedTrace = []
this.locationAndOpcodePerVMTraceIndex = {}
this.pendingConstructorExecutionAt = -1
this.pendingConstructorId = -1
this.constructorsStartExecution = {}
this.pendingConstructor = null
} }
/** /**
@ -123,6 +156,7 @@ export class InternalCallTree {
const scope = this.findScope(vmtraceIndex) const scope = this.findScope(vmtraceIndex)
if (!scope) return [] if (!scope) return []
let scopeId = this.scopeStarts[scope.firstStep] let scopeId = this.scopeStarts[scope.firstStep]
const scopeDetail = this.scopes[scopeId]
const functions = [] const functions = []
if (!scopeId) return functions if (!scopeId) return functions
let i = 0 let i = 0
@ -132,7 +166,7 @@ export class InternalCallTree {
if (i > 1000) throw new Error('retrieFunctionStack: recursion too deep') if (i > 1000) throw new Error('retrieFunctionStack: recursion too deep')
const functionDefinition = this.functionDefinitionsByScope[scopeId] const functionDefinition = this.functionDefinitionsByScope[scopeId]
if (functionDefinition !== undefined) { if (functionDefinition !== undefined) {
functions.push(functionDefinition) functions.push({ ...functionDefinition, ...scopeDetail })
} }
const parent = this.parentScope(scopeId) const parent = this.parentScope(scopeId)
if (!parent) break if (!parent) break
@ -141,31 +175,41 @@ export class InternalCallTree {
return functions return functions
} }
async extractSourceLocation (step) { async extractSourceLocation (step: number, address?: string) {
try { try {
const address = this.traceManager.getCurrentCalledAddressAt(step) if (!address) address = this.traceManager.getCurrentCalledAddressAt(step)
const location = await this.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, step, this.solidityProxy.contracts) return await this.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, step, this.solidityProxy.contracts)
return location
} catch (error) { } catch (error) {
throw new Error('InternalCallTree - Cannot retrieve sourcelocation for step ' + step + ' ' + error) throw new Error('InternalCallTree - Cannot retrieve sourcelocation for step ' + step + ' ' + error)
} }
} }
async extractValidSourceLocation (step) { async extractValidSourceLocation (step: number, address?: string) {
try { try {
const address = this.traceManager.getCurrentCalledAddressAt(step) if (!address) address = this.traceManager.getCurrentCalledAddressAt(step)
const location = await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, step, this.solidityProxy.contracts) return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, step, this.solidityProxy.contracts)
return location
} catch (error) { } catch (error) {
throw new Error('InternalCallTree - Cannot retrieve valid sourcelocation for step ' + step + ' ' + error) throw new Error('InternalCallTree - Cannot retrieve valid sourcelocation for step ' + step + ' ' + error)
} }
} }
async getValidSourceLocationFromVMTraceIndexFromCache (address: string, step: number, contracts: any) {
return await this.sourceLocationTracker.getValidSourceLocationFromVMTraceIndexFromCache(address, step, contracts, this.locationAndOpcodePerVMTraceIndex)
}
async getGasCostPerLine(file: number, line: number) {
if (this.gasCostPerLine[file] && this.gasCostPerLine[file][line]) {
return this.gasCostPerLine[file][line]
}
throw new Error('Could not find gas cost per line')
}
} }
async function buildTree (tree, step, scopeId, isExternalCall, isCreation) { async function buildTree (tree, step, scopeId, isCreation, functionDefinition?, contractObj?, sourceLocation?, validSourceLocation?) {
let subScope = 1 let subScope = 1
tree.scopeStarts[step] = scopeId if (functionDefinition) {
tree.scopes[scopeId] = { firstStep: step, locals: {}, isCreation } await registerFunctionParameters(tree, functionDefinition, step, scopeId, contractObj, validSourceLocation)
}
function callDepthChange (step, trace) { function callDepthChange (step, trace) {
if (step + 1 < trace.length) { if (step + 1 < trace.length) {
@ -183,30 +227,104 @@ async function buildTree (tree, step, scopeId, isExternalCall, isCreation) {
included.file === source.file) included.file === source.file)
} }
let currentSourceLocation = { start: -1, length: -1, file: -1 } let currentSourceLocation = sourceLocation || { start: -1, length: -1, file: -1, jump: '-' }
let previousSourceLocation = currentSourceLocation let previousSourceLocation = currentSourceLocation
let previousValidSourceLocation = validSourceLocation || currentSourceLocation
while (step < tree.traceManager.trace.length) { while (step < tree.traceManager.trace.length) {
let sourceLocation let sourceLocation
let newLocation = false let validSourceLocation
let address
try { try {
sourceLocation = await tree.extractSourceLocation(step) address = tree.traceManager.getCurrentCalledAddressAt(step)
sourceLocation = await tree.extractSourceLocation(step, address)
if (!includedSource(sourceLocation, currentSourceLocation)) { if (!includedSource(sourceLocation, currentSourceLocation)) {
tree.reducedTrace.push(step) tree.reducedTrace.push(step)
currentSourceLocation = sourceLocation currentSourceLocation = sourceLocation
newLocation = true
} }
const amountOfSources = tree.sourceLocationTracker.getTotalAmountOfSources(address, tree.solidityProxy.contracts)
if (tree.sourceLocationTracker.isInvalidSourceLocation(currentSourceLocation, amountOfSources)) { // file is -1 or greater than amount of sources
validSourceLocation = previousValidSourceLocation
} else
validSourceLocation = currentSourceLocation
} catch (e) { } catch (e) {
return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e } return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e }
} }
if (!sourceLocation) { if (!sourceLocation) {
return { outStep: step, error: 'InternalCallTree - No source Location. ' + step } return { outStep: step, error: 'InternalCallTree - No source Location. ' + step }
} }
const isCallInstrn = isCallInstruction(tree.traceManager.trace[step]) const stepDetail: StepDetail = tree.traceManager.trace[step]
const isCreateInstrn = isCreateInstruction(tree.traceManager.trace[step]) const nextStepDetail: StepDetail = tree.traceManager.trace[step + 1]
if (stepDetail && nextStepDetail) {
stepDetail.gasCost = parseInt(stepDetail.gas as string) - parseInt(nextStepDetail.gas as string)
}
// gas per line
let lineColumnPos
if (tree.offsetToLineColumnConverter) {
try {
const generatedSources = tree.sourceLocationTracker.getGeneratedSourcesFromAddress(address)
const astSources = Object.assign({}, tree.solidityProxy.sources)
const sources = Object.assign({}, tree.solidityProxy.sourcesCode)
if (generatedSources) {
for (const genSource of generatedSources) {
astSources[genSource.name] = { id: genSource.id, ast: genSource.ast }
sources[genSource.name] = { content: genSource.contents }
}
}
lineColumnPos = await tree.offsetToLineColumnConverter.offsetToLineColumn(validSourceLocation, validSourceLocation.file, sources, astSources)
if (!tree.gasCostPerLine[validSourceLocation.file]) tree.gasCostPerLine[validSourceLocation.file] = {}
if (!tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line]) {
tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line] = {
gasCost: 0,
indexes: []
}
}
tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line].gasCost += stepDetail.gasCost
tree.gasCostPerLine[validSourceLocation.file][lineColumnPos.start.line].indexes.push(step)
} catch (e) {
console.log(e)
}
}
tree.locationAndOpcodePerVMTraceIndex[step] = { sourceLocation, stepDetail, lineColumnPos }
tree.scopes[scopeId].gasCost += stepDetail.gasCost
const contractObj = await tree.solidityProxy.contractObjectAtAddress(address)
const generatedSources = getGeneratedSources(tree, scopeId, contractObj)
const functionDefinition = resolveFunctionDefinition(tree, sourceLocation, generatedSources)
const isInternalTxInstrn = isCallInstruction(stepDetail)
const isCreateInstrn = isCreateInstruction(stepDetail)
// we are checking if we are jumping in a new CALL or in an internal function // we are checking if we are jumping in a new CALL or in an internal function
if (isCallInstrn || sourceLocation.jump === 'i') {
const constructorExecutionStarts = tree.pendingConstructorExecutionAt > -1 && tree.pendingConstructorExecutionAt < validSourceLocation.start
if (functionDefinition && functionDefinition.kind === 'constructor' && tree.pendingConstructorExecutionAt === -1 && !tree.constructorsStartExecution[functionDefinition.id]) {
tree.pendingConstructorExecutionAt = validSourceLocation.start
tree.pendingConstructorId = functionDefinition.id
tree.pendingConstructor = functionDefinition
// from now on we'll be waiting for a change in the source location which will mark the beginning of the constructor execution.
// constructorsStartExecution allows to keep track on which constructor has already been executed.
}
const internalfunctionCall = functionDefinition && previousSourceLocation.jump === 'i'
if (constructorExecutionStarts || isInternalTxInstrn || internalfunctionCall) {
try { try {
const externalCallResult = await buildTree(tree, step + 1, scopeId === '' ? subScope.toString() : scopeId + '.' + subScope, isCallInstrn, isCreateInstrn) const newScopeId = scopeId === '' ? subScope.toString() : scopeId + '.' + subScope
tree.scopeStarts[step] = newScopeId
tree.scopes[newScopeId] = { firstStep: step, locals: {}, isCreation, gasCost: 0 }
// for the ctor we we are at the start of its trace, we have to replay this step in order to catch all the locals:
const nextStep = constructorExecutionStarts ? step : step + 1
if (constructorExecutionStarts) {
tree.constructorsStartExecution[tree.pendingConstructorId] = tree.pendingConstructorExecutionAt
tree.pendingConstructorExecutionAt = -1
tree.pendingConstructorId = -1
await registerFunctionParameters(tree, tree.pendingConstructor, step, newScopeId, contractObj, previousValidSourceLocation)
tree.pendingConstructor = null
}
const externalCallResult = await buildTree(tree, nextStep, newScopeId, isCreateInstrn, functionDefinition, contractObj, sourceLocation, validSourceLocation)
if (externalCallResult.error) { if (externalCallResult.error) {
return { outStep: step, error: 'InternalCallTree - ' + externalCallResult.error } return { outStep: step, error: 'InternalCallTree - ' + externalCallResult.error }
} else { } else {
@ -216,7 +334,7 @@ async function buildTree (tree, step, scopeId, isExternalCall, isCreation) {
} catch (e) { } catch (e) {
return { outStep: step, error: 'InternalCallTree - ' + e.message } return { outStep: step, error: 'InternalCallTree - ' + e.message }
} }
} else if ((isExternalCall && callDepthChange(step, tree.traceManager.trace)) || (!isExternalCall && sourceLocation.jump === 'o')) { } else if (callDepthChange(step, tree.traceManager.trace) || (sourceLocation.jump === 'o' && functionDefinition)) {
// if not, we might be returning from a CALL or internal function. This is what is checked here. // if not, we might be returning from a CALL or internal function. This is what is checked here.
tree.scopes[scopeId].lastStep = step tree.scopes[scopeId].lastStep = step
return { outStep: step + 1 } return { outStep: step + 1 }
@ -224,9 +342,10 @@ async function buildTree (tree, step, scopeId, isExternalCall, isCreation) {
// if not, we are in the current scope. // if not, we are in the current scope.
// We check in `includeVariableDeclaration` if there is a new local variable in scope for this specific `step` // We check in `includeVariableDeclaration` if there is a new local variable in scope for this specific `step`
if (tree.includeLocalVariables) { if (tree.includeLocalVariables) {
await includeVariableDeclaration(tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation) await includeVariableDeclaration(tree, step, sourceLocation, scopeId, contractObj, generatedSources)
} }
previousSourceLocation = sourceLocation previousSourceLocation = sourceLocation
previousValidSourceLocation = validSourceLocation
step++ step++
} }
} }
@ -245,10 +364,33 @@ function getGeneratedSources (tree, scopeId, contractObj) {
return null return null
} }
async function includeVariableDeclaration (tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation) { async function registerFunctionParameters (tree, functionDefinition, step, scopeId, contractObj, sourceLocation) {
const contractObj = await tree.solidityProxy.contractObjectAt(step) tree.functionCallStack.push(step)
const functionDefinitionAndInputs = { functionDefinition, inputs: [] }
// means: the previous location was a function definition && JUMPDEST
// => we are at the beginning of the function and input/output are setup
try {
const stack = tree.traceManager.getStackAt(step)
const states = tree.solidityProxy.extractStatesDefinitions()
if (functionDefinition.parameters) {
const inputs = functionDefinition.parameters
const outputs = functionDefinition.returnParameters
// input params
if (inputs && inputs.parameters) {
functionDefinitionAndInputs.inputs = addParams(inputs, tree, scopeId, states, contractObj, sourceLocation, stack.length, inputs.parameters.length, -1)
}
// output params
if (outputs) addParams(outputs, tree, scopeId, states, contractObj, sourceLocation, stack.length, 0, 1)
}
} catch (error) {
console.log(error)
}
tree.functionDefinitionsByScope[scopeId] = functionDefinitionAndInputs
}
async function includeVariableDeclaration (tree, step, sourceLocation, scopeId, contractObj, generatedSources) {
let states = null let states = null
const generatedSources = getGeneratedSources(tree, scopeId, contractObj)
const variableDeclarations = resolveVariableDeclaration(tree, sourceLocation, generatedSources) const variableDeclarations = resolveVariableDeclaration(tree, sourceLocation, generatedSources)
// using the vm trace step, the current source location and the ast, // using the vm trace step, the current source location and the ast,
// we check if the current vm trace step target a new ast node of type VariableDeclaration // we check if the current vm trace step target a new ast node of type VariableDeclaration
@ -278,49 +420,6 @@ async function includeVariableDeclaration (tree, step, sourceLocation, scopeId,
} }
} }
} }
// we check here if we are at the beginning inside a new function.
// if that is the case, we have to add to locals tree the inputs and output params
const functionDefinition = resolveFunctionDefinition(tree, previousSourceLocation, generatedSources)
if (!functionDefinition) return
const previousIsJumpDest2 = isJumpDestInstruction(tree.traceManager.trace[step - 2])
const previousIsJumpDest1 = isJumpDestInstruction(tree.traceManager.trace[step - 1])
const isConstructor = functionDefinition.kind === 'constructor'
if (newLocation && (previousIsJumpDest1 || previousIsJumpDest2 || isConstructor)) {
tree.functionCallStack.push(step)
const functionDefinitionAndInputs = { functionDefinition, inputs: [] }
// means: the previous location was a function definition && JUMPDEST
// => we are at the beginning of the function and input/output are setup
try {
const stack = tree.traceManager.getStackAt(step)
states = tree.solidityProxy.extractStatesDefinitions()
if (functionDefinition.parameters) {
const inputs = functionDefinition.parameters
const outputs = functionDefinition.returnParameters
// for (const element of functionDefinition.parameters) {
// if (element.nodeType === 'ParameterList') {
// if (!inputs) inputs = element
// else {
// outputs = element
// break
// }
// }
// }
// input params
if (inputs && inputs.parameters) {
functionDefinitionAndInputs.inputs = addParams(inputs, tree, scopeId, states, contractObj, previousSourceLocation, stack.length, inputs.parameters.length, -1)
}
// output params
if (outputs) addParams(outputs, tree, scopeId, states, contractObj, previousSourceLocation, stack.length, 0, 1)
}
} catch (error) {
console.log(error)
}
tree.functionDefinitionsByScope[scopeId] = functionDefinitionAndInputs
}
} }
// this extract all the variable declaration for a given ast and file // this extract all the variable declaration for a given ast and file
@ -388,7 +487,8 @@ function addParams (parameterList, tree, scopeId, states, contractObj, sourceLoc
type: parseType(param.typeDescriptions.typeString, states, contractName, location), type: parseType(param.typeDescriptions.typeString, states, contractName, location),
stackDepth: stackDepth, stackDepth: stackDepth,
sourceLocation: sourceLocation, sourceLocation: sourceLocation,
abi: contractObj.contract.abi abi: contractObj.contract.abi,
isParameter: true
} }
params.push(attributesName) params.push(attributesName)
} }

@ -11,7 +11,7 @@ export async function solidityLocals (vmtraceIndex, internalTreeCall, stack, mem
let anonymousIncr = 1 let anonymousIncr = 1
for (const local in scope.locals) { for (const local in scope.locals) {
const variable = scope.locals[local] const variable = scope.locals[local]
if (variable.stackDepth < stack.length && variable.sourceLocation.start <= currentSourceLocation.start) { if (variable.stackDepth < stack.length && (variable.sourceLocation.start <= currentSourceLocation.start || variable.isParameter)) {
let name = variable.name let name = variable.name
if (name.indexOf('$') !== -1) { if (name.indexOf('$') !== -1) {
name = '<' + anonymousIncr + '>' name = '<' + anonymousIncr + '>'
@ -21,7 +21,7 @@ export async function solidityLocals (vmtraceIndex, internalTreeCall, stack, mem
locals[name] = await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageResolver, calldata, cursor, variable) locals[name] = await variable.type.decodeFromStack(variable.stackDepth, stack, memory, storageResolver, calldata, cursor, variable)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
locals[name] = { error: '<decoding failed - ' + e.message + '>' } locals[name] = { error: '<decoding failed - ' + e.message + '>', type: variable && variable.type && variable.type.typeName || 'unknown' }
} }
} }
} }

@ -10,6 +10,8 @@ export class SolidityProxy {
getCode getCode
sources sources
contracts contracts
compilationResult
sourcesCode
constructor ({ getCurrentCalledAddressAt, getCode }) { constructor ({ getCurrentCalledAddressAt, getCode }) {
this.cache = new Cache() this.cache = new Cache()
@ -23,9 +25,10 @@ export class SolidityProxy {
* *
* @param {Object} compilationResult - result os a compilatiion (diectly returned by the compiler) * @param {Object} compilationResult - result os a compilatiion (diectly returned by the compiler)
*/ */
reset (compilationResult) { reset (compilationResult, sources?) {
this.sources = compilationResult.sources this.sources = compilationResult.sources // ast
this.contracts = compilationResult.contracts this.contracts = compilationResult.contracts
if (sources) this.sourcesCode = sources
this.cache.reset() this.cache.reset()
} }
@ -44,8 +47,18 @@ export class SolidityProxy {
* @param {Int} vmTraceIndex - index in the vm trave where to resolve the executed contract name * @param {Int} vmTraceIndex - index in the vm trave where to resolve the executed contract name
* @param {Function} cb - callback returns (error, contractName) * @param {Function} cb - callback returns (error, contractName)
*/ */
async contractObjectAt (vmTraceIndex) { async contractObjectAt (vmTraceIndex: number) {
const address = this.getCurrentCalledAddressAt(vmTraceIndex) const address = this.getCurrentCalledAddressAt(vmTraceIndex)
return this.contractObjectAtAddress(address)
}
/**
* retrieve the compiled contract name at the @arg vmTraceIndex (cached)
*
* @param {Int} vmTraceIndex - index in the vm trave where to resolve the executed contract name
* @param {Function} cb - callback returns (error, contractName)
*/
async contractObjectAtAddress (address: string) {
if (this.cache.contractObjectByAddress[address]) { if (this.cache.contractObjectByAddress[address]) {
return this.cache.contractObjectByAddress[address] return this.cache.contractObjectByAddress[address]
} }

@ -25,7 +25,7 @@ export class StringType extends DynamicByteArray {
return await super.decodeFromStack(stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails) return await super.decodeFromStack(stackDepth, stack, memory, storageResolver, calldata, cursor, variableDetails)
} catch (e) { } catch (e) {
console.log(e) console.log(e)
return { error: '<decoding failed - ' + e.message + '>' } return { error: '<decoding failed - ' + e.message + '>', type: this.typeName }
} }
} }

@ -4,9 +4,11 @@ import { getLinebreakPositions, convertOffsetToLineColumn } from './sourceMappin
export class OffsetToColumnConverter { export class OffsetToColumnConverter {
lineBreakPositionsByContent lineBreakPositionsByContent
sourceMappingDecoder sourceMappingDecoder
offsetConvertion
constructor (compilerEvent) { constructor (compilerEvent) {
this.lineBreakPositionsByContent = {} this.lineBreakPositionsByContent = {}
this.offsetConvertion = {}
if (compilerEvent) { if (compilerEvent) {
compilerEvent.register('compilationFinished', (success, data, source, input, version) => { compilerEvent.register('compilationFinished', (success, data, source, input, version) => {
this.clear() this.clear()
@ -26,10 +28,18 @@ export class OffsetToColumnConverter {
} }
} }
} }
return convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) const token = `${rawLocation.start}:${rawLocation.length}:${file}`
if (this.offsetConvertion[token]) {
return this.offsetConvertion[token]
} else {
const convertion = convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
this.offsetConvertion[token] = convertion
return convertion
}
} }
clear () { clear () {
this.lineBreakPositionsByContent = {} this.lineBreakPositionsByContent = {}
this.offsetConvertion = {}
} }
} }

@ -87,13 +87,34 @@ export class SourceLocationTracker {
(map.file > amountOfSources - 1) this indicates the current file index exceed the total number of files. (map.file > amountOfSources - 1) this indicates the current file index exceed the total number of files.
this happens when generated sources should not be considered. this happens when generated sources should not be considered.
*/ */
while (vmtraceStepIndex >= 0 && (map.file === -1 || map.file > amountOfSources - 1)) { while (vmtraceStepIndex >= 0 && this.isInvalidSourceLocation(map, amountOfSources)) {
map = await this.getSourceLocationFromVMTraceIndex(address, vmtraceStepIndex, contracts) map = await this.getSourceLocationFromVMTraceIndex(address, vmtraceStepIndex, contracts)
vmtraceStepIndex = vmtraceStepIndex - 1 vmtraceStepIndex = vmtraceStepIndex - 1
} }
return map return map
} }
isInvalidSourceLocation (sourceLocation, amountOfSources) {
return sourceLocation.file === -1 || sourceLocation.file > amountOfSources - 1
}
async getValidSourceLocationFromVMTraceIndexFromCache (address: string, vmtraceStepIndex: number, contracts: any, cache: Map<number, any>) {
const amountOfSources = this.getTotalAmountOfSources(address, contracts)
let map: any = { file: -1 }
/*
(map.file === -1) this indicates that it isn't associated with a known source code
(map.file > amountOfSources - 1) this indicates the current file index exceed the total number of files.
this happens when generated sources should not be considered.
*/
const originStep = cache[vmtraceStepIndex]
while (vmtraceStepIndex >= 0 && (map.file === -1 || map.file > amountOfSources - 1)) {
map = cache[vmtraceStepIndex].sourceLocation
vmtraceStepIndex = vmtraceStepIndex - 1
originStep.sourceLocation = map
}
return originStep
}
clearCache () { clearCache () {
this.sourceMapByAddress = {} this.sourceMapByAddress = {}
} }

@ -23,7 +23,7 @@ tape('solidity', function (t) {
async function test (st, privateKey) { async function test (st, privateKey) {
var output = compiler.compile(compilerInput(intLocal.contract)) var output = compiler.compile(compilerInput(intLocal.contract))
output = JSON.parse(output) output = JSON.parse(output)
await intLocalTest(st, privateKey, output.contracts['test.sol']['intLocal'].evm.bytecode.object, output) await intLocalTest(st, privateKey, output.contracts['test.sol']['intLocal'].evm.bytecode.object, output, intLocal.contract)
output = compiler.compile(compilerInput(miscLocal.contract)) output = compiler.compile(compilerInput(miscLocal.contract))
output = JSON.parse(output) output = JSON.parse(output)
await miscLocalTest(st, privateKey, output.contracts['test.sol']['miscLocal'].evm.bytecode.object, output) await miscLocalTest(st, privateKey, output.contracts['test.sol']['miscLocal'].evm.bytecode.object, output)

@ -7,9 +7,10 @@ import { contractCreationToken } from '../../../src/trace/traceHelper'
import { SolidityProxy } from '../../../src/solidity-decoder/solidityProxy' import { SolidityProxy } from '../../../src/solidity-decoder/solidityProxy'
import { InternalCallTree } from '../../../src/solidity-decoder/internalCallTree' import { InternalCallTree } from '../../../src/solidity-decoder/internalCallTree'
import { EventManager } from '../../../src/eventManager' import { EventManager } from '../../../src/eventManager'
import * as sourceMappingDecoder from '../../../src/source/sourceMappingDecoder'
import * as helper from './helper' import * as helper from './helper'
module.exports = function (st, privateKey, contractBytecode, compilationResult) { module.exports = function (st, privateKey, contractBytecode, compilationResult, contractCode) {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
let web3 = await (vmCall as any).getWeb3(); let web3 = await (vmCall as any).getWeb3();
(vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) { (vmCall as any).sendTx(web3, { nonce: 0, privateKey: privateKey }, null, 0, contractBytecode, function (error, hash) {
@ -27,22 +28,37 @@ module.exports = function (st, privateKey, contractBytecode, compilationResult)
var solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), getCode: codeManager.getCode.bind(codeManager) }) var solidityProxy = new SolidityProxy({ getCurrentCalledAddressAt: traceManager.getCurrentCalledAddressAt.bind(traceManager), getCode: codeManager.getCode.bind(codeManager) })
solidityProxy.reset(compilationResult) solidityProxy.reset(compilationResult)
var debuggerEvent = new EventManager() var debuggerEvent = new EventManager()
var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }) const offsetToLineColumnConverter = {
offsetToLineColumn: (rawLocation) => {
return new Promise((resolve) => {
const lineBreaks = sourceMappingDecoder.getLinebreakPositions(contractCode)
resolve(sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, lineBreaks))
})
}
}
var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true }, offsetToLineColumnConverter)
callTree.event.register('callTreeBuildFailed', (error) => { callTree.event.register('callTreeBuildFailed', (error) => {
st.fail(error) st.fail(error)
}) })
callTree.event.register('callTreeNotReady', (reason) => { callTree.event.register('callTreeNotReady', (reason) => {
st.fail(reason) st.fail(reason)
}) })
callTree.event.register('callTreeReady', (scopes, scopeStarts) => { callTree.event.register('callTreeReady', async (scopes, scopeStarts) => {
try { try {
let functions1 = callTree.retrieveFunctionsStack(102)
let functions2 = callTree.retrieveFunctionsStack(115) // test gas cost per line
st.equals((await callTree.getGasCostPerLine(0, 16)).gasCost, 11)
st.equals((await callTree.getGasCostPerLine(0, 32)).gasCost, 84)
let functions1 = callTree.retrieveFunctionsStack(103)
let functions2 = callTree.retrieveFunctionsStack(116)
let functions3 = callTree.retrieveFunctionsStack(13) let functions3 = callTree.retrieveFunctionsStack(13)
st.equals(functions1.length, 1) st.equals(functions1.length, 2)
st.equals(functions2.length, 2) st.equals(functions2.length, 3)
st.equals(functions3.length, 0) st.equals(functions3.length, 1)
st.equal(functions1[0].gasCost, 54)
st.equals(Object.keys(functions1[0])[0], 'functionDefinition') st.equals(Object.keys(functions1[0])[0], 'functionDefinition')
st.equals(Object.keys(functions1[0])[1], 'inputs') st.equals(Object.keys(functions1[0])[1], 'inputs')
@ -58,33 +74,33 @@ module.exports = function (st, privateKey, contractBytecode, compilationResult)
st.equals(functions2[0].functionDefinition.name, 'level12') st.equals(functions2[0].functionDefinition.name, 'level12')
st.equals(functions2[1].functionDefinition.name, 'level11') st.equals(functions2[1].functionDefinition.name, 'level11')
st.equals(scopeStarts[0], '') st.equals(scopeStarts[0], '1')
st.equals(scopeStarts[13], '1') st.equals(scopeStarts[10], '1.1')
st.equals(scopeStarts[102], '2') st.equals(scopeStarts[102], '1.1.1')
st.equals(scopeStarts[115], '2.1') st.equals(scopeStarts[115], '1.1.1.1')
st.equals(scopeStarts[136], '3') st.equals(scopeStarts[136], '1.1.2')
st.equals(scopeStarts[153], '4') st.equals(scopeStarts[153], '1.1.3')
st.equals(scopeStarts[166], '4.1') st.equals(scopeStarts[166], '1.1.3.1')
st.equals(scopes[''].locals['ui8'].type.typeName, 'uint8') st.equals(scopes['1.1'].locals['ui8'].type.typeName, 'uint8')
st.equals(scopes[''].locals['ui16'].type.typeName, 'uint16') st.equals(scopes['1.1'].locals['ui16'].type.typeName, 'uint16')
st.equals(scopes[''].locals['ui32'].type.typeName, 'uint32') st.equals(scopes['1.1'].locals['ui32'].type.typeName, 'uint32')
st.equals(scopes[''].locals['ui64'].type.typeName, 'uint64') st.equals(scopes['1.1'].locals['ui64'].type.typeName, 'uint64')
st.equals(scopes[''].locals['ui128'].type.typeName, 'uint128') st.equals(scopes['1.1'].locals['ui128'].type.typeName, 'uint128')
st.equals(scopes[''].locals['ui256'].type.typeName, 'uint256') st.equals(scopes['1.1'].locals['ui256'].type.typeName, 'uint256')
st.equals(scopes[''].locals['ui'].type.typeName, 'uint256') st.equals(scopes['1.1'].locals['ui'].type.typeName, 'uint256')
st.equals(scopes[''].locals['i8'].type.typeName, 'int8') st.equals(scopes['1.1'].locals['i8'].type.typeName, 'int8')
st.equals(scopes[''].locals['i16'].type.typeName, 'int16') st.equals(scopes['1.1'].locals['i16'].type.typeName, 'int16')
st.equals(scopes[''].locals['i32'].type.typeName, 'int32') st.equals(scopes['1.1'].locals['i32'].type.typeName, 'int32')
st.equals(scopes[''].locals['i64'].type.typeName, 'int64') st.equals(scopes['1.1'].locals['i64'].type.typeName, 'int64')
st.equals(scopes[''].locals['i128'].type.typeName, 'int128') st.equals(scopes['1.1'].locals['i128'].type.typeName, 'int128')
st.equals(scopes[''].locals['i256'].type.typeName, 'int256') st.equals(scopes['1.1'].locals['i256'].type.typeName, 'int256')
st.equals(scopes[''].locals['i'].type.typeName, 'int256') st.equals(scopes['1.1'].locals['i'].type.typeName, 'int256')
st.equals(scopes[''].locals['ishrink'].type.typeName, 'int32') st.equals(scopes['1.1'].locals['ishrink'].type.typeName, 'int32')
st.equals(scopes['2'].locals['ui8'].type.typeName, 'uint8') st.equals(scopes['1.1.1'].locals['ui8'].type.typeName, 'uint8')
st.equals(scopes['2.1'].locals['ui81'].type.typeName, 'uint8') st.equals(scopes['1.1.1.1'].locals['ui81'].type.typeName, 'uint8')
st.equals(scopes['3'].locals['ui81'].type.typeName, 'uint8') st.equals(scopes['1.1.2'].locals['ui81'].type.typeName, 'uint8')
st.equals(scopes['4'].locals['ui8'].type.typeName, 'uint8') st.equals(scopes['1.1.3'].locals['ui8'].type.typeName, 'uint8')
st.equals(scopes['4.1'].locals['ui81'].type.typeName, 'uint8') st.equals(scopes['1.1.3.1'].locals['ui81'].type.typeName, 'uint8')
} catch (e) { } catch (e) {
st.fail(e.message) st.fail(e.message)
} }

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

@ -239,6 +239,8 @@ export function compareByteCode (code1, code2) {
code2 = replaceLibReference(code2, pos) code2 = replaceLibReference(code2, pos)
code1 = replaceLibReference(code1, pos) code1 = replaceLibReference(code1, pos)
} }
code1 = removeImmutableReference(code1, code2)
code1 = extractinputParameters(code1) code1 = extractinputParameters(code1)
code1 = extractSwarmHash(code1) code1 = extractSwarmHash(code1)
code1 = extractcborMetadata(code1) code1 = extractcborMetadata(code1)
@ -276,6 +278,27 @@ function replaceLibReference (code, pos) {
return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40) return code.substring(0, pos) + '0000000000000000000000000000000000000000' + code.substring(pos + 40)
} }
function removeByIndex (code, index, length, emptyRef) {
if (!code) return code
return code.slice(0, index) + emptyRef + code.slice(index + length)
}
function removeImmutableReference (code1, code2) {
try {
const refOccurence = code2.match(/7f000000000000000000000000000000000000000000000000000000000000000073/g)
if (!refOccurence) return code1
let offset = 0
refOccurence.map((value) => {
offset = code2.indexOf(value, offset)
code1 = removeByIndex(code1, offset, value.length, '7f000000000000000000000000000000000000000000000000000000000000000073')
offset = offset + 1
})
} catch (e) {
console.log('error removeImmutableReference', e)
}
return code1
}
function findCallInternal (index, rootCall, callsPath) { function findCallInternal (index, rootCall, callsPath) {
const calls = Object.keys(rootCall.calls) const calls = Object.keys(rootCall.calls)
const ret = rootCall const ret = rootCall

File diff suppressed because one or more lines are too long

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

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

@ -52,7 +52,7 @@ export function canUseWorker (selectedVersion) {
} }
function browserSupportWorker () { function browserSupportWorker () {
return document.location.protocol !== 'file:' && Worker !== undefined return document ? document.location.protocol !== 'file:' && Worker !== undefined : false
} }
// returns a promise for minixhr // returns a promise for minixhr

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-tests", "name": "@remix-project/remix-tests",
"version": "0.2.19", "version": "0.2.20",
"description": "Tool to test Solidity smart contracts", "description": "Tool to test Solidity smart contracts",
"main": "src/index.js", "main": "src/index.js",
"types": "./src/index.d.ts", "types": "./src/index.d.ts",
@ -40,13 +40,13 @@
"@ethereumjs/common": "^2.5.0", "@ethereumjs/common": "^2.5.0",
"@ethereumjs/tx": "^3.3.2", "@ethereumjs/tx": "^3.3.2",
"@ethereumjs/vm": "^5.5.3", "@ethereumjs/vm": "^5.5.3",
"@remix-project/remix-lib": "^0.5.19", "@remix-project/remix-lib": "^0.5.20",
"@remix-project/remix-simulator": "^0.2.19", "@remix-project/remix-simulator": "^0.2.20",
"@remix-project/remix-solidity": "^0.5.5", "@remix-project/remix-solidity": "^0.5.6",
"@remix-project/remix-url-resolver": "^0.0.40", "@remix-project/remix-url-resolver": "^0.0.41",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.0", "async": "^2.6.0",
"axios": ">=0.21.1", "axios": "1.1.2",
"change-case": "^3.0.1", "change-case": "^3.0.1",
"color-support": "^1.1.3", "color-support": "^1.1.3",
"colors": "1.4.0", "colors": "1.4.0",
@ -81,5 +81,5 @@
"typescript": "^3.3.1" "typescript": "^3.3.1"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2cec195f5a3678b17745155536a1714d9b1a5694" "gitHead": "569f7d53f6f218d99aa8281929b541ab7e00058f"
} }

@ -11,6 +11,10 @@ describe('testRunner: remix-tests CLI', () => {
const dirContent = result.stdout.toString() const dirContent = result.stdout.toString()
// Install dependencies if 'node_modules' is not already present // Install dependencies if 'node_modules' is not already present
if(!dirContent.includes('node_modules')) { if(!dirContent.includes('node_modules')) {
execSync('yarn add @remix-project/remix-lib ../../libs/remix-lib', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') })
execSync('yarn add @remix-project/remix-url-resolver ../../libs/remix-url-resolver', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') })
execSync('yarn add @remix-project/remix-solidity ../../libs/remix-solidity', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') })
execSync('yarn add @remix-project/remix-simulator ../../libs/remix-simulator', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') })
execSync('yarn install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') }) execSync('yarn install', { cwd: resolve(__dirname + '/../../../dist/libs/remix-tests') })
} }
} }

@ -58,9 +58,10 @@ const DragBar = (props: IRemixDragBarUi) => {
return () => window.removeEventListener('resize', handleResize) return () => window.removeEventListener('resize', handleResize)
}, []) }, [])
function stopDrag(e: MouseEvent, data: any) { function stopDrag(data: any) {
setDragState(false) setDragState(false)
if (data.x < props.minWidth) { console.log("drag")
if (data.x < props.minWidth + offset) {
setDragBarPosX(offset) setDragBarPosX(offset)
props.setHideStatus(true) props.setHideStatus(true)
} else { } else {
@ -70,7 +71,6 @@ const DragBar = (props: IRemixDragBarUi) => {
setDragBarPosX(offset + props.refObject.current.offsetWidth) setDragBarPosX(offset + props.refObject.current.offsetWidth)
}, 300) }, 300)
} }
} }
function startDrag() { function startDrag() {

@ -1,6 +1,11 @@
import React, { useContext, useEffect, useState } from 'react' import React, { useContext, useEffect, useState } from 'react'
import { AppContext } from '../../context/context' import { AppContext } from '../../context/context'
import { useDialogDispatchers } from '../../context/provider' import { useDialogDispatchers } from '../../context/provider'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
const MatomoDialog = (props) => { const MatomoDialog = (props) => {

@ -1,4 +1,4 @@
import React, { useContext, useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import './style/remix-app.css' import './style/remix-app.css'
import { RemixUIMainPanel } from '@remix-ui/panel' import { RemixUIMainPanel } from '@remix-ui/panel'
import MatomoDialog from './components/modals/matomo' import MatomoDialog from './components/modals/matomo'
@ -8,7 +8,6 @@ import { AppProvider } from './context/provider'
import AppDialogs from './components/modals/dialogs' import AppDialogs from './components/modals/dialogs'
import DialogViewPlugin from './components/modals/dialogViewPlugin' import DialogViewPlugin from './components/modals/dialogViewPlugin'
import { AppContext } from './context/context' import { AppContext } from './context/context'
import { RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel'
import { IntlProvider } from 'react-intl' import { IntlProvider } from 'react-intl'
interface IRemixAppUi { interface IRemixAppUi {
@ -83,11 +82,10 @@ const RemixApp = (props: IRemixAppUi) => {
<AppProvider value={value}> <AppProvider value={value}>
<OriginWarning></OriginWarning> <OriginWarning></OriginWarning>
<MatomoDialog hide={!appReady}></MatomoDialog> <MatomoDialog hide={!appReady}></MatomoDialog>
<div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE"> <div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE">
<div id="icon-panel" data-id="remixIdeIconPanel" className="iconpanel bg-light">{props.app.menuicons.render()}</div> <div id="icon-panel" data-id="remixIdeIconPanel" className="iconpanel bg-light">{props.app.menuicons.render()}</div>
<div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>{props.app.sidePanel.render()}</div> <div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>{props.app.sidePanel.render()}</div>
<DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={250} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar> <DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={285} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar>
<div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'> <div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel'>
<RemixUIMainPanel Context={AppContext}></RemixUIMainPanel> <RemixUIMainPanel Context={AppContext}></RemixUIMainPanel>
</div> </div>

@ -34,9 +34,32 @@ export const RemixUiCheckbox = ({
title, title,
visibility, visibility,
display = 'flex', display = 'flex',
tooltipPlacement = 'right-start' tooltipPlacement = 'right'
}: RemixUiCheckboxProps) => { }: RemixUiCheckboxProps) => {
const childJSXWithTooltip = (
<CustomTooltip
tooltipText={title}
tooltipId={`${name}Tooltip`}
placement={tooltipPlacement}
>
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
<input
id={id}
type={inputType}
onChange={onChange}
style={{ verticalAlign: 'bottom' }}
name={name}
className="custom-control-input"
checked={checked}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label}
</label>
</div>
</CustomTooltip>
)
const childJSX = ( const childJSX = (
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}> <div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
<input <input
@ -55,14 +78,7 @@ export const RemixUiCheckbox = ({
</div> </div>
) )
return ( return (
<CustomTooltip title ? (childJSXWithTooltip) : (childJSX)
tooltipText={title}
tooltipId={`${name}Tooltip`}
placement={tooltipPlacement}
>
{childJSX}
</CustomTooltip>
) )
} }

@ -121,7 +121,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}) })
}) })
debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address) => { debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address, stepDetail, lineGasCost) => {
if (!lineColumnPos) { if (!lineColumnPos) {
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
setState(prevState => { setState(prevState => {
@ -158,7 +158,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return { ...prevState, sourceLocationStatus: '' } return { ...prevState, sourceLocationStatus: '' }
}) })
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
await debuggerModule.highlight(lineColumnPos, path) await debuggerModule.highlight(lineColumnPos, path, rawLocation, stepDetail, lineGasCost)
} }
} }
}) })
@ -266,13 +266,14 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
console.log(e.message) console.log(e.message)
} }
const localCache = {}
const debuggerInstance = new Debugger({ const debuggerInstance = new Debugger({
web3, web3,
offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter, offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter,
compilationResult: async (address) => { compilationResult: async (address) => {
try { try {
const ret = await debuggerModule.fetchContractAndCompile(address, currentReceipt) if (!localCache[address]) localCache[address] = await debuggerModule.fetchContractAndCompile(address, currentReceipt)
return ret return localCache[address]
} catch (e) { } catch (e) {
// debuggerModule.showMessage('Debugging error', 'Unable to fetch a transaction.') // debuggerModule.showMessage('Debugging error', 'Unable to fetch a transaction.')
console.error(e) console.error(e)
@ -395,8 +396,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
{ state.debugging && <StepManager stepManager={ stepManager } /> } { state.debugging && <StepManager stepManager={ stepManager } /> }
</div> </div>
<div className="debuggerPanels" ref={panelsRef}> <div className="debuggerPanels" ref={panelsRef}>
{ state.debugging && <VmDebuggerHead vmDebugger={ vmDebugger } /> } { state.debugging && <VmDebuggerHead debugging={state.debugging} vmDebugger={ vmDebugger } /> }
{ state.debugging && <VmDebugger vmDebugger={ vmDebugger } currentBlock={ state.currentBlock } currentReceipt={ state.currentReceipt } currentTransaction={ state.currentTransaction } /> } { state.debugging && <VmDebugger debugging={state.debugging} vmDebugger={ vmDebugger } currentBlock={ state.currentBlock } currentReceipt={ state.currentReceipt } currentTransaction={ state.currentTransaction } /> }
</div> </div>
</div> </div>
) )

@ -44,7 +44,7 @@ export interface IDebuggerApi {
onEditorContentChanged: (listener: onEditorContentChanged) => void onEditorContentChanged: (listener: onEditorContentChanged) => void
onEnvChanged: (listener: onEnvChangedListener) => void onEnvChanged: (listener: onEnvChangedListener) => void
discardHighlight: () => Promise<void> discardHighlight: () => Promise<void>
highlight: (lineColumnPos: LineColumnLocation, path: string) => Promise<void> highlight: (lineColumnPos: LineColumnLocation, path: string, rawLocation: any, stepDetail: any, highlight: any) => Promise<void>
fetchContractAndCompile: (address: string, currentReceipt: TransactionReceipt) => Promise<CompilerAbstract> fetchContractAndCompile: (address: string, currentReceipt: TransactionReceipt) => Promise<CompilerAbstract>
getFile: (path: string) => Promise<string> getFile: (path: string) => Promise<string>
setFile: (path: string, content: string) => Promise<void> setFile: (path: string, content: string) => Promise<void>

@ -1,3 +1,4 @@
import { stateDecoder } from 'dist/libs/remix-debug/src/solidity-decoder'
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import { initialState, reducer } from '../../reducers/assembly-items' import { initialState, reducer } from '../../reducers/assembly-items'
import './styles/assembly-items.css' import './styles/assembly-items.css'
@ -9,6 +10,7 @@ export const AssemblyItems = ({ registerEvent }) => {
const [nextSelectedItems, setNextSelectedItems] = useState([1]) const [nextSelectedItems, setNextSelectedItems] = useState([1])
const [returnInstructionIndexes, setReturnInstructionIndexes] = useState([]) const [returnInstructionIndexes, setReturnInstructionIndexes] = useState([])
const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState([]) const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState([])
const [opcodeTooltipText, setOpcodeTooltipText] = useState('')
const refs = useRef({}) const refs = useRef({})
const asmItemsRef = useRef(null) const asmItemsRef = useRef(null)
@ -16,6 +18,10 @@ export const AssemblyItems = ({ registerEvent }) => {
registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => { registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => {
dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes } }) dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes } })
}) })
registerEvent && registerEvent('lineGasCostChanged', (instructionsIndexes: number[], line: []) => {
dispatch({ type: 'FETCH_INDEXES_FOR_NEW_LINE', payload: { currentLineIndexes: instructionsIndexes || [], line } })
})
}, []) }, [])
useEffect(() => { useEffect(() => {
@ -129,7 +135,9 @@ export const AssemblyItems = ({ registerEvent }) => {
<div className="pl-2 my-1 small instructions" data-id="asmitems" id='asmitems' ref={asmItemsRef}> <div className="pl-2 my-1 small instructions" data-id="asmitems" id='asmitems' ref={asmItemsRef}>
{ {
assemblyItems.display.map((item, i) => { assemblyItems.display.map((item, i) => {
return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}><span>{item}</span></div> return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}>
<span>{item}</span>{assemblyItems.currentLineIndexes.includes(i) ? <span><i><b> - LINE {assemblyItems.line + 1}</b></i></span> : ' - '}
</div>
}) })
} }
</div> </div>

@ -5,7 +5,7 @@ import StepDetail from './step-detail' // eslint-disable-line
import SolidityState from './solidity-state' // eslint-disable-line import SolidityState from './solidity-state' // eslint-disable-line
import SolidityLocals from './solidity-locals' // eslint-disable-line import SolidityLocals from './solidity-locals' // eslint-disable-line
export const VmDebuggerHead = ({ vmDebugger: { registerEvent, triggerEvent } }) => { export const VmDebuggerHead = ({ vmDebugger: { registerEvent, triggerEvent }, debugging }) => {
const [functionPanel, setFunctionPanel] = useState(null) const [functionPanel, setFunctionPanel] = useState(null)
const [stepDetail, setStepDetail] = useState({ const [stepDetail, setStepDetail] = useState({
'vm trace step': '-', 'vm trace step': '-',
@ -31,7 +31,7 @@ export const VmDebuggerHead = ({ vmDebugger: { registerEvent, triggerEvent } })
const functions = [] const functions = []
for (const func of stack) { for (const func of stack) {
functions.push(func.functionDefinition.name + '(' + func.inputs.join(', ') + ')') functions.push((func.functionDefinition.name || func.functionDefinition.kind) + '(' + func.inputs.join(', ') + ')' + ' - ' + func.gasCost + ' gas')
} }
setFunctionPanel(() => functions) setFunctionPanel(() => functions)
}) })
@ -95,7 +95,7 @@ export const VmDebuggerHead = ({ vmDebugger: { registerEvent, triggerEvent } })
return { ...solidityLocals, message } return { ...solidityLocals, message }
}) })
}) })
}, [registerEvent]) }, [debugging])
return ( return (
<div id='vmheadView' className="mt-1 px-2 d-flex"> <div id='vmheadView' className="mt-1 px-2 d-flex">

@ -8,7 +8,7 @@ import ReturnValuesPanel from './dropdown-panel' // eslint-disable-line
import FullStoragesChangesPanel from './full-storages-changes' // eslint-disable-line import FullStoragesChangesPanel from './full-storages-changes' // eslint-disable-line
import GlobalVariables from './global-variables' // eslint-disable-line import GlobalVariables from './global-variables' // eslint-disable-line
export const VmDebugger = ({ vmDebugger: { registerEvent }, currentBlock, currentReceipt, currentTransaction }) => { export const VmDebugger = ({ vmDebugger: { registerEvent }, currentBlock, currentReceipt, currentTransaction, debugging }) => {
const [calldataPanel, setCalldataPanel] = useState(null) const [calldataPanel, setCalldataPanel] = useState(null)
const [memoryPanel, setMemoryPanel] = useState(null) const [memoryPanel, setMemoryPanel] = useState(null)
const [callStackPanel, setCallStackPanel] = useState(null) const [callStackPanel, setCallStackPanel] = useState(null)
@ -49,7 +49,7 @@ export const VmDebugger = ({ vmDebugger: { registerEvent }, currentBlock, curren
registerEvent && registerEvent('traceStorageUpdate', (calldata) => { registerEvent && registerEvent('traceStorageUpdate', (calldata) => {
setFullStoragesChangesPanel(() => calldata) setFullStoragesChangesPanel(() => calldata)
}) })
}, [registerEvent]) }, [debugging])
return ( return (
<div id='vmdebugger' className="d-flex"> <div id='vmdebugger' className="d-flex">

@ -13,6 +13,7 @@ export const initialState = {
}, },
display: [], display: [],
index: 0, index: 0,
initialIndex: 0,
nextIndexes: [-1], nextIndexes: [-1],
returnInstructionIndexes: [], returnInstructionIndexes: [],
outOfGasInstructionIndexes: [], outOfGasInstructionIndexes: [],
@ -20,7 +21,10 @@ export const initialState = {
bottom: 0, bottom: 0,
isRequesting: false, isRequesting: false,
isSuccessful: false, isSuccessful: false,
hasError: null hasError: null,
absoluteCurrentLineIndexes: [],
currentLineIndexes: [],
line: -1
} }
const reducedOpcode = (opCodes, payload) => { const reducedOpcode = (opCodes, payload) => {
@ -31,6 +35,7 @@ const reducedOpcode = (opCodes, payload) => {
return { return {
index: opCodes.index - bottom, index: opCodes.index - bottom,
nextIndexes: opCodes.nextIndexes.map(index => index - bottom), nextIndexes: opCodes.nextIndexes.map(index => index - bottom),
currentLineIndexes: (opCodes.absoluteCurrentLineIndexes && opCodes.absoluteCurrentLineIndexes.map(index => index - bottom)) || [],
display: opCodes.code.slice(bottom, top), display: opCodes.code.slice(bottom, top),
returnInstructionIndexes: payload.returnInstructionIndexes.map((index) => index.instructionIndex - bottom), returnInstructionIndexes: payload.returnInstructionIndexes.map((index) => index.instructionIndex - bottom),
outOfGasInstructionIndexes: payload.outOfGasInstructionIndexes.map((index) => index.instructionIndex - bottom) outOfGasInstructionIndexes: payload.outOfGasInstructionIndexes.map((index) => index.instructionIndex - bottom)
@ -49,20 +54,23 @@ export const reducer = (state = initialState, action: Action) => {
} }
case 'FETCH_OPCODES_SUCCESS': { case 'FETCH_OPCODES_SUCCESS': {
const opCodes = action.payload.address === state.opCodes.address ? { const opCodes = action.payload.address === state.opCodes.address ? {
...state.opCodes, index: action.payload.index, nextIndexes: action.payload.nextIndexes ...state.opCodes, index: action.payload.index, nextIndexes: action.payload.nextIndexes, absoluteCurrentLineIndexes: state.absoluteCurrentLineIndexes
} : deepEqual(action.payload.code, state.opCodes.code) ? state.opCodes : action.payload } : deepEqual(action.payload.code, state.opCodes.code) ? state.opCodes : action.payload
const reduced = reducedOpcode(opCodes, action.payload) const reduced = reducedOpcode(opCodes, action.payload)
return { return {
...state,
opCodes, opCodes,
display: reduced.display, display: reduced.display,
initialIndex: action.payload.index,
index: reduced.index, index: reduced.index,
nextIndexes: reduced.nextIndexes, nextIndexes: reduced.nextIndexes,
isRequesting: false, isRequesting: false,
isSuccessful: true, isSuccessful: true,
hasError: null, hasError: null,
returnInstructionIndexes: reduced.returnInstructionIndexes, returnInstructionIndexes: reduced.returnInstructionIndexes,
outOfGasInstructionIndexes: reduced.outOfGasInstructionIndexes outOfGasInstructionIndexes: reduced.outOfGasInstructionIndexes,
currentLineIndexes: reduced.currentLineIndexes
} }
} }
case 'FETCH_OPCODES_ERROR': { case 'FETCH_OPCODES_ERROR': {
@ -73,6 +81,16 @@ export const reducer = (state = initialState, action: Action) => {
hasError: action.payload hasError: action.payload
} }
} }
case 'FETCH_INDEXES_FOR_NEW_LINE': {
let bottom = state.initialIndex - 10
bottom = bottom < 0 ? 0 : bottom
return {
...state,
absoluteCurrentLineIndexes: action.payload.currentLineIndexes,
currentLineIndexes: action.payload.currentLineIndexes.map(index => index - bottom),
line: action.payload.line
}
}
default: default:
throw new Error() throw new Error()
} }

@ -13,7 +13,7 @@ export const CustomToggle = React.forwardRef(({ children, onClick, icon, classNa
className={className.replace('dropdown-toggle', '')} className={className.replace('dropdown-toggle', '')}
> >
<div className="d-flex"> <div className="d-flex">
<div className="mr-auto">{ children }</div> <div className="mr-auto text-nowrap">{ children }</div>
{ icon && <div className="pr-1"><i className={`${icon} pr-1`}></i></div> } { icon && <div className="pr-1"><i className={`${icon} pr-1`}></i></div> }
<div><i className="fad fa-sort-circle"></i></div> <div><i className="fad fa-sort-circle"></i></div>
</div> </div>

@ -4,7 +4,7 @@ import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { CustomTooltipType } from '../../types/customtooltip' import { CustomTooltipType } from '../../types/customtooltip'
export function CustomTooltip({ children, placement, tooltipId, tooltipClasses, tooltipText, tooltipTextClasses }: CustomTooltipType) { export function CustomTooltip({ children, placement, tooltipId, tooltipClasses, tooltipText, tooltipTextClasses, delay }: CustomTooltipType) {
return ( return (
<Fragment> <Fragment>
@ -15,6 +15,7 @@ export function CustomTooltip({ children, placement, tooltipId, tooltipClasses,
{typeof tooltipText === 'string' ? (<span className={tooltipTextClasses}>{tooltipText}</span>) : (tooltipText)} {typeof tooltipText === 'string' ? (<span className={tooltipTextClasses}>{tooltipText}</span>) : (tooltipText)}
</Tooltip> </Tooltip>
} }
delay={delay}
> >
{children} {children}
</OverlayTrigger> </OverlayTrigger>

@ -1,5 +1,5 @@
import { Placement } from 'react-bootstrap/esm/Overlay' import { Placement } from 'react-bootstrap/esm/Overlay'
import { OverlayTriggerRenderProps } from 'react-bootstrap/esm/OverlayTrigger' import { OverlayDelay, OverlayTriggerRenderProps } from 'react-bootstrap/esm/OverlayTrigger'
export type CustomTooltipType = { export type CustomTooltipType = {
children: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ((props: OverlayTriggerRenderProps) => React.ReactNode), children: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ((props: OverlayTriggerRenderProps) => React.ReactNode),
@ -8,4 +8,5 @@ export type CustomTooltipType = {
tooltipClasses?:string, tooltipClasses?:string,
tooltipText: string | JSX.Element, tooltipText: string | JSX.Element,
tooltipTextClasses?: string tooltipTextClasses?: string
delay?: OverlayDelay
} }

@ -1,14 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React from 'react'
function CustomButtonGroupAsArrows ({ next, previous }) {
return (
<div style={{ textAlign: "center" }}>
<h4>These buttons can be positioned anywhere you want on the screen</h4>
<button onClick={previous}>Prev</button>
<button onClick={next}>Next</button>
</div>
)
}
export default CustomButtonGroupAsArrows

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import React from 'react' import React from 'react'
const CustomNavButtons = ({ next, previous, goToSlide, ...rest }) => { const CustomNavButtons = ({ parent, next, previous, goToSlide, ...rest }) => {
const { carouselState: { currentSlide, totalItems, itemWidth, containerWidth } } = rest const { carouselState: { currentSlide, totalItems, containerWidth, transform } } = rest
return ( return (
<div className="mt-1 d-flex justify-content-end carousel-button-group"> <div className="mt-1 d-flex justify-content-end carousel-button-group">
<button <button
@ -14,11 +14,11 @@ const CustomNavButtons = ({ next, previous, goToSlide, ...rest }) => {
</button> </button>
<button <button
className={ className={
((totalItems - currentSlide) * itemWidth + 5) < containerWidth ? 'disable py-1 border btn' : 'py-1 border btn'} (Math.abs(transform) >= parent?.current?.containerRef?.current?.scrollWidth - containerWidth) ? 'disable py-1 border btn' : 'py-1 border btn'}
onClick={() => { onClick={() => {
if (currentSlide + 1 < totalItems) goToSlide(currentSlide + 1) if (currentSlide + 1 < totalItems) goToSlide(currentSlide + 1)
}} }}
disabled ={((totalItems - currentSlide) * itemWidth + 5) < containerWidth} disabled ={Math.abs(transform) >= parent?.current?.containerRef?.current?.scrollWidth - containerWidth}
> >
<i className="fas fa-angle-right"></i> <i className="fas fa-angle-right"></i>
</button> </button>

@ -3,17 +3,11 @@ import React, { useEffect, useState, useRef, useContext } from 'react'
import { ThemeContext, themes } from '../themeContext' import { ThemeContext, themes } from '../themeContext'
import Carousel from 'react-multi-carousel' import Carousel from 'react-multi-carousel'
import 'react-multi-carousel/lib/styles.css' import 'react-multi-carousel/lib/styles.css'
import CustomNavButtons from './customNavButtons'
const _paq = window._paq = window._paq || [] // eslint-disable-line const _paq = window._paq = window._paq || [] // eslint-disable-line
function HomeTabFeatured() { function HomeTabFeatured() {
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
useEffect(() => {
return () => {
}
}, [])
return ( return (
<div className="pt-3 pl-2" id="hTFeaturedeSection"> <div className="pt-3 pl-2" id="hTFeaturedeSection">
<label style={{ fontSize: "1.2rem" }}>Featured</label> <label style={{ fontSize: "1.2rem" }}>Featured</label>
@ -21,7 +15,6 @@ function HomeTabFeatured() {
<div className="w-100 d-flex flex-column" style={{ height: "200px" }}> <div className="w-100 d-flex flex-column" style={{ height: "200px" }}>
<ThemeContext.Provider value={themeFilter}> <ThemeContext.Provider value={themeFilter}>
<Carousel <Carousel
customButtonGroup={<CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} />}
arrows={false} arrows={false}
swipeable={false} swipeable={false}
draggable={true} draggable={true}
@ -30,17 +23,18 @@ function HomeTabFeatured() {
renderDotsOutside={true} renderDotsOutside={true}
ssr={true} // means to render carousel on server-side. ssr={true} // means to render carousel on server-side.
infinite={true} infinite={true}
partialVisible={false}
centerMode={false} centerMode={false}
autoPlay={true} autoPlay={true}
keyBoardControl={true} keyBoardControl={true}
containerClass="border carousel-container" containerClass="border w-full carousel-container"
sliderClass="px-2 h-100 justify-content-between" sliderClass="h-100 justify-content-between"
deviceType={"desktop"} deviceType={"desktop"}
itemClass="px-2 carousel-item-padding-10-px" itemClass=""
autoPlaySpeed={15000} autoPlaySpeed={15000}
dotListClass="position-relative mt-2" dotListClass="position-relative mt-2"
> >
<div className="d-flex"> <div className="mx-1 px-1 d-flex">
<img src={"assets/img/bgRemi.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img> <img src={"assets/img/bgRemi.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
<div className="h6 w-50 p-4" style={{ flex: "1" }}> <div className="h6 w-50 p-4" style={{ flex: "1" }}>
<h5>JUMP INTO WEB3</h5> <h5>JUMP INTO WEB3</h5>
@ -48,7 +42,7 @@ function HomeTabFeatured() {
<a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'jumpIntoWeb3'])} target="__blank" href="https://remix-project.org">More</a> <a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'jumpIntoWeb3'])} target="__blank" href="https://remix-project.org">More</a>
</div> </div>
</div> </div>
<div className="d-flex"> <div className="mx-1 px-1 d-flex">
<img src={"/assets/img/remixRewardUser.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img> <img src={"/assets/img/remixRewardUser.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
<div className="h6 p-4" style={{ flex: "1" }}> <div className="h6 p-4" style={{ flex: "1" }}>
<h5>REMIX REWARDS</h5> <h5>REMIX REWARDS</h5>
@ -59,7 +53,7 @@ function HomeTabFeatured() {
<a className="remixui_home_text" target="__blank" onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'remixRewards'])} href="https://rewards.remix.ethereum.eth.limo">More</a> <a className="remixui_home_text" target="__blank" onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'remixRewards'])} href="https://rewards.remix.ethereum.eth.limo">More</a>
</div> </div>
</div> </div>
<div className="d-flex"> <div className="mx-1 px-1 d-flex">
<img src={"/assets/img/remixRewardBetaTester.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img> <img src={"/assets/img/remixRewardBetaTester.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
<div className="h6 p-4" style={{ flex: "1" }}> <div className="h6 p-4" style={{ flex: "1" }}>
<h5>BETA TESTING</h5> <h5>BETA TESTING</h5>

@ -20,7 +20,7 @@ interface HomeTabFeaturedPluginsProps {
function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) { function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) {
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
const carouselRef = useRef(null) const carouselRef = useRef<any>({})
const carouselRefDiv = useRef(null) const carouselRefDiv = useRef(null)
useEffect(() => { useEffect(() => {
@ -47,7 +47,7 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) {
let nextSlide = 0 let nextSlide = 0
if (e.wheelDelta < 0) { if (e.wheelDelta < 0) {
nextSlide = carouselRef.current.state.currentSlide + 1; nextSlide = carouselRef.current.state.currentSlide + 1;
if ((carouselRef.current.state.totalItems - carouselRef.current.state.currentSlide) * carouselRef.current.state.itemWidth + 5 < carouselRef.current.state.containerWidth) return // 5 is approx margins if (Math.abs(carouselRef.current.state.transform) >= carouselRef.current.containerRef.current.scrollWidth - carouselRef.current.state.containerWidth) return
carouselRef.current.goToSlide(nextSlide) carouselRef.current.goToSlide(nextSlide)
} else { } else {
nextSlide = carouselRef.current.state.currentSlide - 1; nextSlide = carouselRef.current.state.currentSlide - 1;
@ -92,7 +92,7 @@ function HomeTabFeaturedPlugins ({plugin}: HomeTabFeaturedPluginsProps) {
ref={carouselRef} ref={carouselRef}
focusOnSelect={true} focusOnSelect={true}
customButtonGroup={ customButtonGroup={
<CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} /> <CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} parent={carouselRef} />
} }
arrows={false} arrows={false}
swipeable={false} swipeable={false}

@ -23,13 +23,13 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
const [state, setState] = useState<{ const [state, setState] = useState<{
searchInput: string, searchInput: string,
showModalDialog: boolean, showModalDialog: boolean,
modalInfo: { title: string, loadItem: string, examples: Array<string> }, modalInfo: { title: string, loadItem: string, examples: Array<string>, prefix?: string },
importSource: string, importSource: string,
toasterMsg: string toasterMsg: string
}>({ }>({
searchInput: '', searchInput: '',
showModalDialog: false, showModalDialog: false,
modalInfo: { title: '', loadItem: '', examples: [] }, modalInfo: { title: '', loadItem: '', examples: [], prefix: '' },
importSource: '', importSource: '',
toasterMsg: '' toasterMsg: ''
}) })
@ -42,8 +42,15 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
_paq.push(['trackEvent', 'hometab', 'filesSection', 'importFrom' + type]) _paq.push(['trackEvent', 'hometab', 'filesSection', 'importFrom' + type])
const contentImport = plugin.contentImport const contentImport = plugin.contentImport
const workspace = plugin.fileManager.getProvider('workspace') const workspace = plugin.fileManager.getProvider('workspace')
const startsWith = state.importSource.substring(0, 4)
if ((type === 'ipfs' || type === 'IPFS') && (startsWith !== 'ipfs' && startsWith !== "IPFS")) {
setState(prevState => {
return { ...prevState, importSource: startsWith + state.importSource}
})
}
contentImport.import( contentImport.import(
state.importSource, state.modalInfo.prefix + state.importSource,
(loadingMsg) => dispatch({ tooltip: loadingMsg }), (loadingMsg) => dispatch({ tooltip: loadingMsg }),
async (error, content, cleanUrl, type, url) => { async (error, content, cleanUrl, type, url) => {
if (error) { if (error) {
@ -93,9 +100,9 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
plugin.verticalIcons.select('filePanel') plugin.verticalIcons.select('filePanel')
} }
const showFullMessage = (title: string, loadItem: string, examples: Array<string>) => { const showFullMessage = (title: string, loadItem: string, examples: Array<string>, prefix = '') => {
setState(prevState => { setState(prevState => {
return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples } } return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples, prefix } }
}) })
} }
@ -126,6 +133,8 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
{ examples } { examples }
</div> </div>
</> } </> }
<div className="d-flex flex-row">
{ state.modalInfo.prefix && <span className='text-nowrap align-self-center mr-2'>ipfs://</span> }
<input <input
ref={inputValue} ref={inputValue}
type='text' type='text'
@ -141,6 +150,7 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
}} }}
/> />
</div> </div>
</div>
</ModalDialog> </ModalDialog>
<Toaster message={state.toasterMsg} /> <Toaster message={state.toasterMsg} />
<div className="justify-content-start mt-1 p-2 border-bottom d-flex flex-column" id="hTFileSection"> <div className="justify-content-start mt-1 p-2 border-bottom d-flex flex-column" id="hTFileSection">
@ -155,9 +165,15 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
<button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}><FormattedMessage id='home.connectToLocalhost' defaultMessage='Connect to Localhost' /></button> <button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}><FormattedMessage id='home.connectToLocalhost' defaultMessage='Connect to Localhost' /></button>
<label className="pt-2"><FormattedMessage id='home.loadFrom' defaultMessage='Load From' /></label> <label className="pt-2"><FormattedMessage id='home.loadFrom' defaultMessage='Load From' /></label>
<div className="d-flex"> <div className="d-flex">
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGitHubButton" onClick={() => showFullMessage('GitHub', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'])}>GitHub</button> <button
className="btn p-2 border mr-2"
data-id="landingPageImportFromGitHubButton"
onClick={() => showFullMessage('GitHub', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'])}
>
GitHub
</button>
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button> <button className="btn p-2 border mr-2" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button>
<button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs URL', ['ipfs://<ipfs-hash>'])}>IPFS</button> <button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs hash', ['ipfs://QmQQfBMkpDgmxKzYaoAtqfaybzfgGm9b2LWYyT56Chv6xH'], "ipfs://")}>IPFS</button>
<button className="btn p-2 border" onClick={() => showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])}>HTTPS</button> <button className="btn p-2 border" onClick={() => showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])}>HTTPS</button>
</div> </div>
</div> </div>

@ -17,7 +17,7 @@ interface HomeTabGetStartedProps {
function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) { function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) {
const themeFilter = useContext(ThemeContext) const themeFilter = useContext(ThemeContext)
const carouselRef = useRef(null) const carouselRef = useRef<any>({})
const carouselRefDiv = useRef(null) const carouselRefDiv = useRef(null)
useEffect(() => { useEffect(() => {
@ -44,7 +44,7 @@ function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) {
let nextSlide = 0 let nextSlide = 0
if (e.wheelDelta < 0) { if (e.wheelDelta < 0) {
nextSlide = carouselRef.current.state.currentSlide + 1; nextSlide = carouselRef.current.state.currentSlide + 1;
if ((carouselRef.current.state.totalItems - carouselRef.current.state.currentSlide) * carouselRef.current.state.itemWidth + 5 < carouselRef.current.state.containerWidth) return // 5 is approx margins if (Math.abs(carouselRef.current.state.transform) >= carouselRef.current.containerRef.current.scrollWidth - carouselRef.current.state.containerWidth) return
carouselRef.current.goToSlide(nextSlide) carouselRef.current.goToSlide(nextSlide)
} else { } else {
nextSlide = carouselRef.current.state.currentSlide - 1; nextSlide = carouselRef.current.state.currentSlide - 1;
@ -77,7 +77,7 @@ function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) {
ref={carouselRef} ref={carouselRef}
focusOnSelect={true} focusOnSelect={true}
customButtonGroup={ customButtonGroup={
<CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} /> <CustomNavButtons next={undefined} previous={undefined} goToSlide={undefined} parent={carouselRef} />
} }
arrows={false} arrows={false}
swipeable={false} swipeable={false}

@ -49,25 +49,27 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
</button> </button>
</div> </div>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<button className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Basics }})}> <label className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Basics }})}>
<label className="m-0 float-left" style={{fontSize: "1rem"}}>Remix Basics</label> <label className="card-title align-self-start m-0 float-left" style={{fontSize: "1rem"}}>Remix Basics</label>
{(state.visibleTutorial === VisibleTutorial.Basics) && <div className="pt-2 d-flex flex-column text-left"> {(state.visibleTutorial === VisibleTutorial.Basics) && <div className="pt-2 d-flex flex-column text-left">
<span>Introduction to Remix's interface and concepts used in Ethereum, as well as the basics of Solidity.</span> <span>Introduction to Remix's interface and concepts used in Ethereum, as well as the basics of Solidity.</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('basics')}>Get Started</button> <button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('basics')}>Get Started</button>
</div>} </div>}
</button> </label>
<button className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Intermediate }})}> <label className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Intermediate }})}>
<label className="m-0 float-left" style={{fontSize: "1rem"}}>Remix Intermediate</label> <label className="card-title align-self-start m-0 float-left" style={{fontSize: "1rem"}}>Remix Intermediate</label>
{(state.visibleTutorial === VisibleTutorial.Intermediate) && <div className="pt-2 d-flex flex-column text-left">Using the web3.js to interact with a contract. Using Recorder tool. {(state.visibleTutorial === VisibleTutorial.Intermediate) && <div className="pt-2 d-flex flex-column text-left">
<span>Using the web3.js to interact with a contract. Using Recorder tool.</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('useofweb3js')}>Get Started</button> <button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('useofweb3js')}>Get Started</button>
</div>} </div>}
</button> </label>
<button className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Advanced }})}> <label className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Advanced }})}>
<label className="m-0 float-left" style={{fontSize: "1rem"}}>Remix Advanced</label> <label className="card-title align-self-start m-0 float-left" style={{fontSize: "1rem"}}>Remix Advanced</label>
{(state.visibleTutorial === VisibleTutorial.Advanced) && <div className="pt-2 d-flex flex-column text-left">Learn the Proxy Pattern and working with Libraries in Remix. Learn to use the Debugger. {(state.visibleTutorial === VisibleTutorial.Advanced) && <div className="pt-2 d-flex flex-column text-left">
<span>Learn the Proxy Pattern and working with Libraries in Remix. Learn to use the Debugger.</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('deploylibraries')}>Get Started</button> <button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('deploylibraries')}>Get Started</button>
</div>} </div>}
</button> </label>
</div> </div>
</div> </div>
) )

@ -157,7 +157,7 @@ function HomeTabTitle() {
ref={searchInputRef} ref={searchInputRef}
type="text" type="text"
className="border form-control border-right-0" className="border form-control border-right-0"
id="searchInput" id="homeTabSearchInput"
placeholder="Search Documentation" placeholder="Search Documentation"
data-id="terminalInputSearch" data-id="terminalInputSearch"
/> />

@ -1,7 +1,5 @@
import IpfsHttpClient from 'ipfs-http-client' import IpfsHttpClient from 'ipfs-http-client'
let ipfsNodes = [] let ipfsNodes = []
export const publishToIPFS = async (contract, api) => { export const publishToIPFS = async (contract, api) => {
@ -93,7 +91,6 @@ export const publishToIPFS = async (contract, api) => {
try { try {
const result = await ipfsVerifiedPublish(metadataContent, '', api) const result = await ipfsVerifiedPublish(metadataContent, '', api)
try { try {
contract.metadataHash = result.url.match('dweb:/ipfs/(.+)')[1] contract.metadataHash = result.url.match('dweb:/ipfs/(.+)')[1]
} catch (e) { } catch (e) {

@ -17,12 +17,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
display: '', display: '',
content: '' content: ''
}) })
const [atAddressOptions, setAtAddressOptions] = useState<{ title: string, disabled: boolean }>({ const [atAddressOptions, setAtAddressOptions] = useState<{ title: string | JSX.Element, disabled: boolean }>({
title: 'address of contract', title: 'address of contract',
disabled: true disabled: true
}) })
const [loadedAddress, setLoadedAddress] = useState<string>('') const [loadedAddress, setLoadedAddress] = useState<string>('')
const [contractOptions, setContractOptions] = useState<{ title: string, disabled: boolean }>({ const [contractOptions, setContractOptions] = useState<{ title: string | JSX.Element, disabled: boolean }>({
title: 'Please compile *.sol file to deploy or access a contract', title: 'Please compile *.sol file to deploy or access a contract',
disabled: true disabled: true
}) })
@ -136,12 +136,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
if (enable) { if (enable) {
setAtAddressOptions({ setAtAddressOptions({
disabled: false, disabled: false,
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)' title: <span className="text-start">Interact with the deployed contract - requires the .abi file or <br /> compiled .sol file to be selected in the editor <br />(with the same compiler configuration)</span>
}) })
} else { } else {
setAtAddressOptions({ setAtAddressOptions({
disabled: true, disabled: true,
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. ' title: loadedAddress ? 'Compile a *.sol file or select a *.abi file.' : <span className="text-start">To interact with a deployed contract, either<br /> enter its address and compile its source *.sol file <br />(with the same compiler settings) or select its .abi file in the editor. </span>
}) })
} }
} }
@ -155,7 +155,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
} else { } else {
setContractOptions({ setContractOptions({
disabled: true, disabled: true,
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.' title: loadType === 'sol' ? 'Select and compile *.sol file to deploy or access a contract.' : <span className="text-start">When there is a compiled .sol file, choose the <br /> contract to deploy or to use with AtAddress.'</span>
}) })
} }
} }
@ -236,7 +236,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
const checkSumWarning = () => { const checkSumWarning = () => {
return ( return (
<span> <span className="text-start">
It seems you are not using a checksumed address. It seems you are not using a checksumed address.
<br />A checksummed address is an address that contains uppercase letters, as specified in <a href="https://eips.ethereum.org/EIPS/eip-55" target="_blank" rel="noreferrer">EIP-55</a>. <br />A checksummed address is an address that contains uppercase letters, as specified in <a href="https://eips.ethereum.org/EIPS/eip-55" target="_blank" rel="noreferrer">EIP-55</a>.
<br />Checksummed addresses are meant to help prevent users from sending transactions to the wrong address. <br />Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.
@ -264,10 +264,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
{props.remixdActivated ? {props.remixdActivated ?
(<CustomTooltip (<CustomTooltip
placement={'right'} placement={'right'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap text-left"
tooltipId="info-sync-compiled-contract" tooltipId="info-sync-compiled-contract"
tooltipText="Click here to import contracts compiled from an external framework. tooltipText={<span className="text-left">
This action is enabled when Remix is connected to an external framework (hardhat, truffle, foundry) through remixd." Click here to import contracts compiled from an external framework.<br/>
This action is enabled when Remix is connected to an external<br/> framework (hardhat, truffle, foundry) through remixd.
</span>}
> >
<button className="btn d-flex py-0" onClick={_ => { <button className="btn d-flex py-0" onClick={_ => {
props.syncContracts() props.syncContracts()
@ -281,17 +283,19 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
<div className="udapp_subcontainer"> <div className="udapp_subcontainer">
<CustomTooltip <CustomTooltip
placement={"right"} placement={"right"}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap text-left"
tooltipId="remixUdappContractNamesTooltip" tooltipId="remixUdappContractNamesTooltip"
tooltipText={contractOptions.title} tooltipText={contractOptions.title}
> >
<select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}> <div id="udappcontractNamesWrapper" className="w-100">
<select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block', pointerEvents: contractOptions.disabled ? 'none' : 'auto' }}>
{(contractList[currentFile] || []).map((contract, index) => { {(contractList[currentFile] || []).map((contract, index) => {
return <option key={index} value={contract.alias}> return <option key={index} value={contract.alias}>
{contract.alias} - {contract.file} {contract.alias} - {contract.file}
</option> </option>
})} })}
</select> </select>
</div>
</CustomTooltip> </CustomTooltip>
<span className="py-1" style={{ display: abiLabel.display }}>{abiLabel.content}</span> <span className="py-1" style={{ display: abiLabel.display }}>{abiLabel.content}</span>
</div> </div>
@ -324,9 +328,9 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
/> />
<CustomTooltip <CustomTooltip
placement={'right'} placement={'right'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap text-left"
tooltipId="remixIpfsUdappTooltip" tooltipId="remixIpfsUdappTooltip"
tooltipText="Publishing the source code and metadata to IPFS facilitates source code verification using Sourcify and will greatly foster contract adoption (auditing, debugging, calling it, etc...)" tooltipText={<span className="text-start">Publishing the source code and metadata to IPFS facilitates<br/> source code verification using Sourcify and will greatly foster<br/> contract adoption (auditing, debugging, calling it, etc...)</span>}
> >
<label <label
htmlFor="deployAndRunPublishToIPFS" htmlFor="deployAndRunPublishToIPFS"
@ -346,7 +350,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
<div className="udapp_button udapp_atAddressSect "> <div className="udapp_button udapp_atAddressSect ">
<CustomTooltip <CustomTooltip
placement={'top-end'} placement={'top-end'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap text-left"
tooltipId="runAndDeployAddresstooltip" tooltipId="runAndDeployAddresstooltip"
tooltipText={atAddressOptions.title} tooltipText={atAddressOptions.title}
> >
@ -358,9 +362,9 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
</CustomTooltip> </CustomTooltip>
<CustomTooltip <CustomTooltip
placement={'top-end'} placement={'top-end'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap text-left"
tooltipId="runAndDeployAddressInputtooltip" tooltipId="runAndDeployAddressInputtooltip"
tooltipText={"address of contract"} tooltipText={"Address of contract"}
> >
<input <input
ref={atAddressValue} ref={atAddressValue}

@ -556,7 +556,9 @@ export function ContractGUI (props: ContractGUIProps) {
<label className="mt-2 text-left d-block"> <label className="mt-2 text-left d-block">
Proxy Address : Proxy Address :
</label> </label>
<input style={{ height: 32 }} className="form-control udapp_input" data-id="ERC1967AddressInput" placeholder='proxy address' title='Enter previously deployed proxy address on the selected network' onChange={handleSetProxyAddress} onBlur={() => validateProxyAddress(proxyAddress) } /> <CustomTooltip placement="right" tooltipText={'Enter previously deployed proxy address on the selected network'}>
<input style={{ height: 32 }} className="form-control udapp_input" data-id="ERC1967AddressInput" placeholder='proxy address' onChange={handleSetProxyAddress} onBlur={() => validateProxyAddress(proxyAddress) } />
</CustomTooltip>
{ proxyAddressError && <span className='text-lowercase' data-id="errorMsgProxyAddress" style={{ fontSize: '.8em' }}>{ proxyAddressError }</span> } { proxyAddressError && <span className='text-lowercase' data-id="errorMsgProxyAddress" style={{ fontSize: '.8em' }}>{ proxyAddressError }</span> }
</div> : </div> :
<span className='text-capitalize' data-id="lastDeployedERC1967Address" style={{ fontSize: '.8em' }}>{ proxyAddress || proxyAddressError }</span> <span className='text-capitalize' data-id="lastDeployedERC1967Address" style={{ fontSize: '.8em' }}>{ proxyAddress || proxyAddressError }</span>

@ -28,18 +28,24 @@ export function EnvironmentUI (props: EnvironmentProps) {
<div className="udapp_crow"> <div className="udapp_crow">
<label id="selectExEnv" className="udapp_settingsLabel"> <label id="selectExEnv" className="udapp_settingsLabel">
<FormattedMessage id='udapp.environment' defaultMessage='Environment' /> <FormattedMessage id='udapp.environment' defaultMessage='Environment' />
<CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="info-recorder" <CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="info-recorder"
tooltipText="Open chainlist and add a new provider for the chain you want to interact to."> tooltipText="Open chainlist.org and get the connection specs of the chain you want to interact with.">
<a href='https://chainlist.org/' target='_blank'><i style={{ fontSize: 'medium' }} className={'ml-2 fad fa-plug'} aria-hidden="true"></i></a> <a href='https://chainlist.org/' target='_blank'><i style={{ fontSize: 'medium' }} className={'ml-2 fad fa-plug'} aria-hidden="true"></i></a>
</CustomTooltip> </CustomTooltip>
</label> </label>
<div className="udapp_environment"> <div className="udapp_environment">
<Dropdown id="selectExEnvOptions" data-id="settingsSelectEnvOptions" className='udapp_selectExEnvOptions'> <Dropdown id="selectExEnvOptions" data-id="settingsSelectEnvOptions" className='udapp_selectExEnvOptions'>
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" className="btn btn-light btn-block w-100 d-inline-block border border-dark form-control" icon={null}> <Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" className="btn btn-light btn-block w-100 d-inline-block border border-dark form-control" icon={null}>
{ isL2(currentProvider) && 'L2 - '} { isL2(currentProvider) && 'L2 - '}
{ currentProvider && currentProvider.content } { currentProvider && currentProvider.content }
{ currentProvider && bridges[currentProvider.value] && <CustomTooltip placement={'right'} tooltipClasses="text-nowrap" tooltipId="info-recorder" tooltipText="Click to open a bridge for converting L1 mainnet ETH to the selected network currency."> { currentProvider && bridges[currentProvider.value] && <CustomTooltip
placement={'right'}
tooltipClasses="text-nowrap"
tooltipId="info-recorder"
tooltipText="Click to open a bridge for converting L1 mainnet ETH to the selected network currency."
>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-plug'} aria-hidden="true" onClick={() => { window.open(bridges[currentProvider.value], '_blank') }}></i> <i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-plug'} aria-hidden="true" onClick={() => { window.open(bridges[currentProvider.value], '_blank') }}></i>
</CustomTooltip>} </CustomTooltip>}
</Dropdown.Toggle> </Dropdown.Toggle>
@ -53,14 +59,16 @@ export function EnvironmentUI (props: EnvironmentProps) {
}} }}
data-id={`dropdown-item-${value}`} data-id={`dropdown-item-${value}`}
> >
<span className="pl-3">{ isL2({ value }) && 'L2 - ' }{ content }</span> <span className="">{ isL2({ value }) && 'L2 - ' }{ content }</span>
</Dropdown.Item> </Dropdown.Item>
)) ))
} }
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
<CustomTooltip placement={'bottom-start'} tooltipClasses="text-wrap" tooltipId="runAndDeployAddresstooltip"
<CustomTooltip placement={'right-start'} tooltipClasses="text-wrap" tooltipId="runAndDeployAddresstooltip"
tooltipText={<FormattedMessage id='udapp.environmentDocs' defaultMessage='Click for docs about Environment' />}> tooltipText={<FormattedMessage id='udapp.environmentDocs' defaultMessage='Click for docs about Environment' />}>
<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"></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"></i></a>
</CustomTooltip> </CustomTooltip>
</div> </div>

@ -13,7 +13,7 @@ export function GasPriceUI (props: GasPriceProps) {
<div className="udapp_crow"> <div className="udapp_crow">
<label className="udapp_settingsLabel"><FormattedMessage id='udapp.gasLimit' defaultMessage='Gas limit' /></label> <label className="udapp_settingsLabel"><FormattedMessage id='udapp.gasLimit' defaultMessage='Gas limit' /></label>
<CustomTooltip <CustomTooltip
placement={'right-end'} placement={'right'}
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipId="remixGasPriceTooltip" tooltipId="remixGasPriceTooltip"
tooltipText={"The default gas limit is 3M. Adjust as needed."} tooltipText={"The default gas limit is 3M. Adjust as needed."}

@ -4,7 +4,7 @@ import { NetworkProps } from '../types'
export function NetworkUI (props: NetworkProps) { export function NetworkUI (props: NetworkProps) {
return ( return (
<div className="udapp_crow"> <div className="">
<div className="udapp_settingsLabel"> <div className="udapp_settingsLabel">
</div> </div>
<div className="udapp_environment" data-id="settingsNetworkEnv"> <div className="udapp_environment" data-id="settingsNetworkEnv">

@ -28,7 +28,7 @@ export function RecorderUI (props: RecorderProps) {
return ( return (
<div className="udapp_cardContainer list-group-item border border-bottom"> <div className="udapp_cardContainer list-group-item border-top border-bottom">
<div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}> <div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}>
<div className="d-flex justify-content-center align-items-center"> <div className="d-flex justify-content-center align-items-center">
<label className="mt-1 udapp_recorderSectionLabel"> <label className="mt-1 udapp_recorderSectionLabel">
@ -46,7 +46,7 @@ export function RecorderUI (props: RecorderProps) {
placement={'right'} placement={'right'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap"
tooltipId="info-recorder" tooltipId="info-recorder"
tooltipText="Save transactions (deployed contracts and function executions) and replay them in another environment e.g Transactions created in Remix VM can be replayed in the Injected Provider." tooltipText={<span>Save transactions (deployed contracts and function executions) <br />and replay them in another environment e.g Transactions created <br />in Remix VM can be replayed in the Injected Provider.</span>}
> >
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle align-self-center'} aria-hidden="true"></i> <i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
</CustomTooltip> </CustomTooltip>
@ -64,7 +64,7 @@ export function RecorderUI (props: RecorderProps) {
placement={'right'} placement={'right'}
tooltipClasses="text-wrap" tooltipClasses="text-wrap"
tooltipId="tooltip-livemode-recorder" tooltipId="tooltip-livemode-recorder"
tooltipText="If contracts are updated after recording transactions, checking this box will run recorded transactions with the latest copy of the compiled contracts" tooltipText={<span>If contracts are updated after recording transactions,<br/> checking this box will run recorded transactions <br/>with the latest copy of the compiled contracts</span>}
> >
<label className="form-check-label custom-control-label" data-id="runtabLivemodeInput" htmlFor="livemode-recorder">Run transactions using the latest compilation result</label> <label className="form-check-label custom-control-label" data-id="runtabLivemodeInput" htmlFor="livemode-recorder">Run transactions using the latest compilation result</label>
</CustomTooltip> </CustomTooltip>

@ -336,7 +336,7 @@ export function UniversalDappUI (props: UdappProps) {
placement={"bottom-end"} placement={"bottom-end"}
tooltipClasses="text-wrap" tooltipClasses="text-wrap"
tooltipId="receiveEthDocstoolTip" tooltipId="receiveEthDocstoolTip"
tooltipText={"check out docs for using 'receive'/'fallback'"} tooltipText={"Click for docs about using 'receive'/'fallback'"}
> >
<a <a
href="https://solidity.readthedocs.io/en/v0.6.2/contracts.html#receive-ether-function" href="https://solidity.readthedocs.io/en/v0.6.2/contracts.html#receive-ether-function"

@ -45,8 +45,6 @@
width: 164px; width: 164px;
min-width: 164px; min-width: 164px;
} }
.udapp_col2_2 {
}
.udapp_select { .udapp_select {
font-weight: normal; font-weight: normal;
width: 100%; width: 100%;
@ -314,7 +312,7 @@
.udapp_cActionsWrapper { .udapp_cActionsWrapper {
border-top-left-radius: 0; border-top-left-radius: 0;
border-bottom-left-radius: 0.25rem; border-bottom-left-radius: 0.25rem;
border-top-rightt-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0.25rem; border-bottom-right-radius: 0.25rem;
padding: 8px 10px 7px; padding: 8px 10px 7px;
} }

@ -5,6 +5,7 @@ import { CompilerContainerProps } from './types'
import { ConfigurationSettings } from '@remix-project/remix-lib-ts' import { ConfigurationSettings } from '@remix-project/remix-lib-ts'
import { checkSpecialChars, CustomTooltip, extractNameFromKey } from '@remix-ui/helper' import { checkSpecialChars, CustomTooltip, extractNameFromKey } from '@remix-ui/helper'
import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '@remix-project/remix-solidity' import { canUseWorker, baseURLBin, baseURLWasm, urlFromVersion, pathToURL, promisedMiniXhr } from '@remix-project/remix-solidity'
import { compilerReducer, compilerInitialState } from './reducers/compiler' import { compilerReducer, compilerInitialState } from './reducers/compiler'
import { resetEditorMode, listenToEvents } from './actions/compiler' import { resetEditorMode, listenToEvents } from './actions/compiler'
import { getValidLanguage } from '@remix-project/remix-solidity' import { getValidLanguage } from '@remix-project/remix-solidity'
@ -732,6 +733,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
setToggleExpander(!toggleExpander) setToggleExpander(!toggleExpander)
} }
return ( return (
<section> <section>
<article> <article>
@ -860,7 +862,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
tooltipText={<span>{'Language specification available from Compiler >= v0.5.7'}</span>} tooltipText={<span>{'Language specification available from Compiler >= v0.5.7'}</span>}
> >
<div id="compilerLanguageSelectorWrapper"> <div id="compilerLanguageSelectorWrapper">
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector"> <select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector" style={{ pointerEvents: state.useFileConfiguration ? 'none' : 'auto' }}>
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option> <option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option> <option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select> </select>
@ -944,9 +946,13 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
{(configFilePath === '' && state.useFileConfiguration) && <div> No config file selected</div>} {(configFilePath === '' && state.useFileConfiguration) && <div> No config file selected</div>}
</div>} </div>}
> >
<div className="d-flex align-items-center justify-content-center">
{ <i ref={compileIcon} className="fas fa-sync remixui_iconbtn ml-4" aria-hidden="true"></i> }
<div className="d-flex justify-content-between align-items-center">
<span> <span>
{ <i ref={compileIcon} className="fas fa-sync remixui_iconbtn" aria-hidden="true"></i> }
<FormattedMessage id='solidity.compile' defaultMessage='Compile' /> <FormattedMessage id='solidity.compile' defaultMessage='Compile' />
</span>
<span className="ml-1">
{typeof state.compiledFileName === 'string' {typeof state.compiledFileName === 'string'
? extractNameFromKey(state.compiledFileName) || ? extractNameFromKey(state.compiledFileName) ||
`<${intl.formatMessage({ `<${intl.formatMessage({
@ -958,6 +964,8 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
defaultMessage: 'no file selected', defaultMessage: 'no file selected',
})}>`} })}>`}
</span> </span>
</div>
</div>
</CustomTooltip> </CustomTooltip>
</button> </button>
<div className='d-flex align-items-center'> <div className='d-flex align-items-center'>
@ -969,7 +977,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton} disabled={(configFilePath === '' && state.useFileConfiguration) || disableCompileButton}
> >
<CustomTooltip <CustomTooltip
placement="auto" placement="right"
tooltipId="overlay-tooltip-compile-run" tooltipId="overlay-tooltip-compile-run"
tooltipText={<div className="text-left"> tooltipText={<div className="text-left">
{!(configFilePath === '' && state.useFileConfiguration) && <div><b>Ctrl+Shift+S</b> for compiling and script execution</div>} {!(configFilePath === '' && state.useFileConfiguration) && <div><b>Ctrl+Shift+S</b> for compiling and script execution</div>}
@ -982,7 +990,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</CustomTooltip> </CustomTooltip>
</button> </button>
<CustomTooltip <CustomTooltip
placement="auto" placement="right"
tooltipId="overlay-tooltip-compile-run-doc" tooltipId="overlay-tooltip-compile-run-doc"
tooltipText={<div className="text-left p-2"> tooltipText={<div className="text-left p-2">
<div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div> <div>Choose the script to execute right after compilation by adding the `dev-run-script` natspec tag, as in:</div>
@ -996,7 +1004,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
contract ContractName {'{}'}<br /> contract ContractName {'{}'}<br />
</code> </code>
</pre> </pre>
Click to know more Click the i icon to learn more
</div>} </div>}
> >
<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> <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>

@ -259,7 +259,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
finalLogs = finalLogs + '&emsp;' + formattedLog + '\n' finalLogs = finalLogs + '&emsp;' + formattedLog + '\n'
} }
_paq.push(['trackEvent', 'solidityUnitTesting', 'hardhat', 'console.log']) _paq.push(['trackEvent', 'solidityUnitTesting', 'hardhat', 'console.log'])
testTab.call('terminal', 'log', { type: 'log', value: finalLogs }) testTab.call('terminal', 'logHtml', { type: 'log', value: finalLogs })
} }
const discardHighlight = async () => { const discardHighlight = async () => {

@ -19,12 +19,12 @@ const StaticAnalyserButton = ({
return ( return (
<button className={classList} disabled={disabled} onClick={onClick}> <button className={classList} disabled={disabled} onClick={onClick}>
<CustomTooltip <CustomTooltip
placement="bottom-start" placement="right"
tooltipId="ssaRunButtonTooltip" tooltipId="ssaRunButtonTooltip"
tooltipClasses="text-nowrap" tooltipClasses="text-nowrap"
tooltipText={title} tooltipText={title}
> >
<span> <span className="pl-3 pr-4">
{buttonText} {buttonText}
</span> </span>
</CustomTooltip> </CustomTooltip>

@ -458,7 +458,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
expand={false} expand={false}
> >
<div> <div>
<RemixUiCheckbox onClick={() => handleCheckOrUncheckCategory(category)} id={categoryId} inputType="checkbox" label={`Select ${category[0].categoryDisplayName}`} name='checkCategoryEntry' checked={category.map(x => x._index.toString()).every(el => categoryIndex.includes(el))} onChange={() => {}}/> <RemixUiCheckbox onClick={() => handleCheckOrUncheckCategory(category)} id={categoryId} inputType="checkbox" label={`Select ${category[0].categoryDisplayName}`} name='checkCategoryEntry' checked={category.map(x => x._index.toString()).every(el => categoryIndex.includes(el))} onChange={() => {}} title={category[0].categoryDisplayName} tooltipPlacement="right"/>
</div> </div>
<div className="w-100 d-block px-2 my-1 entries collapse multi-collapse" id={`heading${categoryId}`}> <div className="w-100 d-block px-2 my-1 entries collapse multi-collapse" id={`heading${categoryId}`}>
{category.map((item, i) => { {category.map((item, i) => {

@ -1,3 +1,4 @@
import { CustomTooltip } from '@remix-ui/helper'
import React from 'react' import React from 'react'
import BasicLogo from './BasicLogo' import BasicLogo from './BasicLogo'
interface HomeProps { interface HomeProps {
@ -6,16 +7,20 @@ interface HomeProps {
function Home ({ verticalIconPlugin }: HomeProps) { function Home ({ verticalIconPlugin }: HomeProps) {
return ( return (
<CustomTooltip
placement="right"
tooltipText={"Home"}
>
<div <div
className="mt-2 my-1 remixui_homeIcon" className="mt-2 my-1 remixui_homeIcon"
onClick={async () => await verticalIconPlugin.activateHome()} onClick={async () => await verticalIconPlugin.activateHome()}
{...{ plugin: 'home'}} {...{ plugin: 'home'}}
title="Home"
data-id="verticalIconsHomeIcon" data-id="verticalIconsHomeIcon"
id="verticalIconsHomeIcon" id="verticalIconsHomeIcon"
> >
<BasicLogo /> <BasicLogo />
</div> </div>
</CustomTooltip>
) )
} }

@ -5,6 +5,7 @@ import Badge from './Badge'
import { iconBadgeReducer, IconBadgeReducerAction } from '../reducers/iconBadgeReducer' import { iconBadgeReducer, IconBadgeReducerAction } from '../reducers/iconBadgeReducer'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { IconRecord } from '../types' import { IconRecord } from '../types'
import { CustomTooltip } from '@remix-ui/helper'
export interface IconStatus { export interface IconStatus {
key: string key: string
@ -84,13 +85,18 @@ const Icon = ({
return ( return (
<> <>
<CustomTooltip
placement={name === 'settings' ? 'right' : name === 'search' ? 'bottom' :
name === 'udapp' ? 'bottom' : "top"}
tooltipText={title}
delay={{ show: 1000, hide: 0 }}
>
<div <div
className={`remixui_icon m-2 pt-1`} className={`remixui_icon m-2 pt-1`}
onClick={() => { onClick={() => {
(verticalIconPlugin as any).toggle(name) (verticalIconPlugin as any).toggle(name)
}} }}
{...{plugin: name}} {...{plugin: name}}
title={title}
onContextMenu={(e: any) => { onContextMenu={(e: any) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
@ -105,6 +111,7 @@ const Icon = ({
badgeStatus={badgeStatus} badgeStatus={badgeStatus}
/> />
</div> </div>
</CustomTooltip>
{showContext ? ( {showContext ? (
<VerticalIconsContextMenu <VerticalIconsContextMenu
pageX={pageX} pageX={pageX}

@ -22,9 +22,9 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
}, },
{ {
action: 'publishToGist', action: 'publishToGist',
title: 'Publish all the current workspace files (only root) to a github gist', title: 'Publish all the current workspace files to a github gist',
icon: 'fab fa-github', icon: 'fab fa-github',
placement: 'top-start' placement: 'bottom-start'
}, },
{ {
action: 'uploadFile', action: 'uploadFile',
@ -36,7 +36,7 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
action: 'updateGist', action: 'updateGist',
title: 'Update the current [gist] explorer', title: 'Update the current [gist] explorer',
icon: 'fab fa-github', icon: 'fab fa-github',
placement: 'right-start' placement: 'bottom-start'
} }
].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })), ].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })),
actions: {} actions: {}

@ -264,7 +264,7 @@ export function Workspace () {
<option style={{fontSize: "small"}} value='remixDefault'>Default</option> <option style={{fontSize: "small"}} value='remixDefault'>Default</option>
<option style={{fontSize: "small"}} value='blank'>Blank</option> <option style={{fontSize: "small"}} value='blank'>Blank</option>
</optgroup> </optgroup>
<optgroup style={{fontSize: "medium"}} label="OpenZepplin"> <optgroup style={{fontSize: "medium"}} label="OpenZeppelin">
<option style={{fontSize: "small"}} value='ozerc20'>ERC20</option> <option style={{fontSize: "small"}} value='ozerc20'>ERC20</option>
<option style={{fontSize: "small"}} value='ozerc721'>ERC721</option> <option style={{fontSize: "small"}} value='ozerc721'>ERC721</option>
<option style={{fontSize: "small"}} value='ozerc1155'>ERC1155</option> <option style={{fontSize: "small"}} value='ozerc1155'>ERC1155</option>

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-url-resolver", "name": "@remix-project/remix-url-resolver",
"version": "0.0.40", "version": "0.0.41",
"description": "Solidity import url resolver engine", "description": "Solidity import url resolver engine",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -25,7 +25,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@erebos/bzz-node": "^0.13.0", "@erebos/bzz-node": "^0.13.0",
"axios": ">=0.21.1", "axios": "1.1.2",
"url": "^0.11.0", "url": "^0.11.0",
"valid-url": "^1.0.9" "valid-url": "^1.0.9"
}, },
@ -41,5 +41,5 @@
"typescript": "^3.1.6" "typescript": "^3.1.6"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "2cec195f5a3678b17745155536a1714d9b1a5694" "gitHead": "569f7d53f6f218d99aa8281929b541ab7e00058f"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-ws-templates", "name": "@remix-project/remix-ws-templates",
"version": "1.0.6", "version": "1.0.7",
"description": "Create a Remix IDE workspace using different templates", "description": "Create a Remix IDE workspace using different templates",
"main": "src/index.js", "main": "src/index.js",
"types": "src/index.d.ts", "types": "src/index.d.ts",
@ -27,5 +27,5 @@
"ethers": "^5.4.2", "ethers": "^5.4.2",
"web3": "^1.5.1" "web3": "^1.5.1"
}, },
"gitHead": "2cec195f5a3678b17745155536a1714d9b1a5694" "gitHead": "569f7d53f6f218d99aa8281929b541ab7e00058f"
} }

@ -40,7 +40,7 @@
"@remixproject/plugin-api": "^0.3.11", "@remixproject/plugin-api": "^0.3.11",
"@remixproject/plugin-utils": "^0.3.11", "@remixproject/plugin-utils": "^0.3.11",
"@remixproject/plugin-ws": "^0.3.11", "@remixproject/plugin-ws": "^0.3.11",
"axios": ">=0.21.1", "axios": "1.1.2",
"chokidar": "^2.1.8", "chokidar": "^2.1.8",
"commander": "^2.20.3", "commander": "^2.20.3",
"fs-extra": "^3.0.1", "fs-extra": "^3.0.1",

@ -168,7 +168,7 @@
"@types/nightwatch": "^2.3.1", "@types/nightwatch": "^2.3.1",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.2", "async": "^2.6.2",
"axios": ">=0.26.0", "axios": "1.1.2",
"bootstrap": "^5.1.3", "bootstrap": "^5.1.3",
"brace": "^0.8.0", "brace": "^0.8.0",
"change-case": "^4.1.1", "change-case": "^4.1.1",

@ -5866,13 +5866,22 @@ axe-core@^4.0.2:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5"
integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA== integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==
axios@*, axios@>=0.26.0: axios@*:
version "0.26.0" version "0.26.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.0.tgz#9a318f1c69ec108f8cd5f3c3d390366635e13928" resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.0.tgz#9a318f1c69ec108f8cd5f3c3d390366635e13928"
integrity sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og== integrity sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==
dependencies: dependencies:
follow-redirects "^1.14.8" follow-redirects "^1.14.8"
axios@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.2.tgz#8b6f6c540abf44ab98d9904e8daf55351ca4a331"
integrity sha512-bznQyETwElsXl2RK7HLLwb5GPpOLlycxHCtrpDR/4RqqBzjARaOTo3jz4IgtntWUYee7Ne4S8UHd92VCuzPaWA==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
axios@^0.21.1: axios@^0.21.1:
version "0.21.4" version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
@ -11268,6 +11277,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.12.1, follow-redirects@^1.14.0, fol
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
for-each@~0.3.3: for-each@~0.3.3:
version "0.3.3" version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
@ -19426,6 +19440,11 @@ proxy-addr@~2.0.5:
forwarded "0.2.0" forwarded "0.2.0"
ipaddr.js "1.9.1" ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
prr@~1.0.1: prr@~1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"

Loading…
Cancel
Save