Merge remote-tracking branch 'upstream/master' into spacesailor24/web3js-v4-upgrade

pull/5370/head
Oleksii Kosynskyi 1 year ago
commit 5942374311
  1. 5
      .eslintrc.json
  2. 4
      .prettierrc.json
  3. 8
      apps/debugger/src/app/app.tsx
  4. 11
      apps/debugger/src/main.tsx
  5. 65
      apps/debugger/webpack.config.js
  6. 26
      apps/doc-gen/src/app/App.tsx
  7. 25
      apps/doc-gen/src/app/hooks/useLocalStorage.tsx
  8. 23
      apps/doc-gen/src/app/views/ErrorView.tsx
  9. 10
      apps/doc-gen/src/main.tsx
  10. 25
      apps/doc-gen/webpack.config.js
  11. 5
      apps/doc-viewer/src/app/App.tsx
  12. 4
      apps/doc-viewer/src/main.tsx
  13. 28
      apps/doc-viewer/webpack.config.js
  14. 20
      apps/etherscan/src/app/AppContext.tsx
  15. 37
      apps/etherscan/src/app/RemixPlugin.tsx
  16. 76
      apps/etherscan/src/app/app.tsx
  17. 47
      apps/etherscan/src/app/components/HeaderWithSettings.tsx
  18. 26
      apps/etherscan/src/app/components/SubmitButton.tsx
  19. 5
      apps/etherscan/src/app/hooks/useLocalStorage.tsx
  20. 10
      apps/etherscan/src/app/layouts/Default.tsx
  21. 16
      apps/etherscan/src/app/routes.tsx
  22. 37
      apps/etherscan/src/app/views/CaptureKeyView.tsx
  23. 16
      apps/etherscan/src/app/views/ErrorView.tsx
  24. 15
      apps/etherscan/src/app/views/HomeView.tsx
  25. 115
      apps/etherscan/src/app/views/ReceiptsView.tsx
  26. 170
      apps/etherscan/src/app/views/VerifyView.tsx
  27. 14
      apps/etherscan/src/main.tsx
  28. 62
      apps/etherscan/webpack.config.js
  29. 56
      apps/remix-ide-e2e/nightwatch.ts
  30. 68
      apps/remix-ide-e2e/src/local-plugin/src/app/app.tsx
  31. 8
      apps/remix-ide-e2e/src/local-plugin/src/app/logger.tsx
  32. 2
      apps/remix-ide/background.js
  33. 8
      apps/remix-ide/src/app/components/hidden-panel.tsx
  34. 6
      apps/remix-ide/src/app/components/main-panel.tsx
  35. 122
      apps/remix-ide/src/app/components/preload.tsx
  36. 7
      apps/remix-ide/src/app/components/side-panel.tsx
  37. 28
      apps/remix-ide/src/app/components/vertical-icons.tsx
  38. 12
      apps/remix-ide/src/app/plugins/contractFlattener.tsx
  39. 2
      apps/remix-ide/src/app/plugins/notification.tsx
  40. 150
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  41. 14
      apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx
  42. 80
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  43. 17
      apps/remix-ide/src/app/plugins/solidity-script.tsx
  44. 103
      apps/remix-ide/src/app/plugins/solidity-umlgen.tsx
  45. 16
      apps/remix-ide/src/app/providers/abstract-provider.tsx
  46. 34
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  47. 23
      apps/remix-ide/src/app/providers/external-http-provider.tsx
  48. 17
      apps/remix-ide/src/app/providers/foundry-provider.tsx
  49. 17
      apps/remix-ide/src/app/providers/ganache-provider.tsx
  50. 13
      apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx
  51. 13
      apps/remix-ide/src/app/providers/hardhat-provider.tsx
  52. 19
      apps/remix-ide/src/app/providers/injected-L2-provider.tsx
  53. 1
      apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx
  54. 1
      apps/remix-ide/src/app/providers/injected-optimism-provider.tsx
  55. 2
      apps/remix-ide/src/app/providers/injected-provider-default.tsx
  56. 24
      apps/remix-ide/src/app/providers/injected-provider.tsx
  57. 13
      apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx
  58. 13
      apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx
  59. 36
      apps/remix-ide/src/app/providers/vm-provider.tsx
  60. 6
      apps/remix-ide/src/app/tabs/search.tsx
  61. 10
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  62. 251
      apps/remix-ide/src/blockchain/blockchain.tsx
  63. 7
      apps/remix-ide/src/index.tsx
  64. 78
      apps/remix-ide/webpack.config.js
  65. 8
      apps/solhint/src/app/App.tsx
  66. 34
      apps/solhint/webpack.config.js
  67. 64
      apps/solidity-compiler/webpack.config.js
  68. 33
      apps/vyper/src/app/app.tsx
  69. 12
      apps/vyper/src/app/components/CompilerButton.tsx
  70. 15
      apps/vyper/src/app/components/LocalUrl.tsx
  71. 54
      apps/vyper/src/app/components/VyperResult.tsx
  72. 3
      apps/vyper/src/app/components/WarnRemote.tsx
  73. 27
      apps/vyper/src/app/utils/compiler.tsx
  74. 31
      apps/vyper/src/app/utils/remix-client.tsx
  75. 14
      apps/vyper/src/main.tsx
  76. 65
      apps/vyper/webpack.config.js
  77. 4
      apps/walletconnect/src/app/app.tsx
  78. 9
      apps/walletconnect/src/app/walletConnectUI.tsx
  79. 5
      apps/walletconnect/src/main.tsx
  80. 64
      apps/walletconnect/webpack.config.js
  81. 8
      libs/remix-debug/test.ts
  82. 13
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  83. 3
      libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx
  84. 44
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  85. 42
      libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx
  86. 14
      libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx
  87. 11
      libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx
  88. 2
      libs/remix-ui/app/src/lib/remix-app/context/context.tsx
  89. 41
      libs/remix-ui/app/src/lib/remix-app/context/provider.tsx
  90. 40
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  91. 61
      libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx
  92. 31
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  93. 4
      libs/remix-ui/debugger-ui/src/hooks/extract-data.tsx
  94. 209
      libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx
  95. 155
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  96. 9
      libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx
  97. 20
      libs/remix-ui/debugger-ui/src/lib/step-manager/step-manager.tsx
  98. 39
      libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx
  99. 56
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/assembly-items.tsx
  100. 6
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/calldata-panel.tsx
  101. Some files were not shown because too many files have changed in this diff Show More

@ -48,6 +48,11 @@
"no-empty": "off", "no-empty": "off",
"jsx-a11y/anchor-is-valid": "off", "jsx-a11y/anchor-is-valid": "off",
"@typescript-eslint/no-inferrable-types": "off", "@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "off",
"react-hooks/exhaustive-deps": "off",
"array-callback-return": "off",
"prefer-spread": "off",
"indent": ["error", 2] "indent": ["error", 2]
} }
}, },

@ -1,6 +1,7 @@
{ {
"tabWidth": 2, "tabWidth": 2,
"useTabs": false, "useTabs": false,
"printWidth": 180,
"semi": false, "semi": false,
"singleQuote": true, "singleQuote": true,
"quoteProps": "consistent", "quoteProps": "consistent",
@ -9,6 +10,5 @@
"trailingComma": "none", "trailingComma": "none",
"jsxBracketSameLine": false, "jsxBracketSameLine": false,
"arrowParens": "always", "arrowParens": "always",
"singleAttributePerLine": false, "singleAttributePerLine": false
"ignorePath": ".prettierignore"
} }

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, {useState, useEffect} from 'react'
import {DebuggerUI} from '@remix-ui/debugger-ui' // eslint-disable-line import {DebuggerUI} from '@remix-ui/debugger-ui' // eslint-disable-line
@ -11,7 +11,7 @@ export const App = () => {
<div className="debugger"> <div className="debugger">
<DebuggerUI debuggerAPI={remix} /> <DebuggerUI debuggerAPI={remix} />
</div> </div>
); )
}; }
export default App; export default App

@ -1,9 +1,6 @@
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom'
import App from './app/app'; import App from './app/app'
ReactDOM.render( ReactDOM.render(<App />, document.getElementById('root'))
<App />,
document.getElementById('root')
);

@ -1,8 +1,7 @@
const {composePlugins, withNx} = require('@nrwl/webpack') const {composePlugins, withNx} = require('@nrwl/webpack')
const webpack = require('webpack') const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack. // Nx plugins for webpack.
module.exports = composePlugins(withNx(), (config) => { module.exports = composePlugins(withNx(), (config) => {
@ -12,56 +11,52 @@ module.exports = composePlugins(withNx(), (config) => {
// add fallback for node modules // add fallback for node modules
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"), crypto: require.resolve('crypto-browserify'),
"stream": require.resolve("stream-browserify"), stream: require.resolve('stream-browserify'),
"path": require.resolve("path-browserify"), path: require.resolve('path-browserify'),
"http": require.resolve("stream-http"), http: require.resolve('stream-http'),
"https": require.resolve("https-browserify"), https: require.resolve('https-browserify'),
"constants": require.resolve("constants-browserify"), constants: require.resolve('constants-browserify'),
"os": false, //require.resolve("os-browserify/browser"), os: false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"), timers: false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"), zlib: require.resolve('browserify-zlib'),
"fs": false, fs: false,
"module": false, module: false,
"tls": false, tls: false,
"net": false, net: false,
"readline": false, readline: false,
"child_process": false, child_process: false,
"buffer": require.resolve("buffer/"), buffer: require.resolve('buffer/'),
"vm": require.resolve('vm-browserify'), vm: require.resolve('vm-browserify')
} }
// add externals // add externals
config.externals = { config.externals = {
...config.externals, ...config.externals,
solc: 'solc', solc: 'solc'
} }
// add public path // add public path
config.output.publicPath = '/' config.output.publicPath = '/'
// add copy & provide plugin // add copy & provide plugin
config.plugins.push( config.plugins.push(
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}) })
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
// set minimizer // set minimizer
config.optimization.minimizer = [ config.optimization.minimizer = [
new TerserPlugin({ new TerserPlugin({
@ -71,17 +66,17 @@ module.exports = composePlugins(withNx(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
config.watchOptions = { config.watchOptions = {
ignored: /node_modules/ ignored: /node_modules/
} }
return config; return config
}); })

@ -8,9 +8,9 @@ export const client = new DocGenClient()
const App = () => { const App = () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [themeType, setThemeType] = useState<string>('dark'); const [themeType, setThemeType] = useState<string>('dark')
const [hasBuild, setHasBuild] = useState<boolean>(false); const [hasBuild, setHasBuild] = useState<boolean>(false)
const [fileName, setFileName] = useState<string>(''); const [fileName, setFileName] = useState<string>('')
useEffect(() => { useEffect(() => {
const watchThemeSwitch = async () => { const watchThemeSwitch = async () => {
@ -21,19 +21,27 @@ const App = () => {
setHasBuild(true) setHasBuild(true)
setFileName(fileName) setFileName(fileName)
}) })
client.eventEmitter.on('docsGenerated', (docs: string[]) => { client.eventEmitter.on('docsGenerated', (docs: string[]) => {})
})
} }
watchThemeSwitch() watchThemeSwitch()
}, []) }, [])
return ( return (
<div className="p-3"> <div className="p-3">
<h5 className="h-5 mb-3">Compile a Solidity contract and generate its documentation as Markdown. (Right-click on a contract in the File Explorer and select "Generate Docs" from the context menu.).</h5> <h5 className="h-5 mb-3">
{fileName && <div className="border-bottom border-top px-2 py-3 justify-center align-items-center d-flex"> Compile a Solidity contract and generate its documentation as Markdown. (Right-click on a contract in the File
Explorer and select "Generate Docs" from the context menu.).
</h5>
{fileName && (
<div className="border-bottom border-top px-2 py-3 justify-center align-items-center d-flex">
<h6>File: {fileName}</h6> <h6>File: {fileName}</h6>
</div>} </div>
{hasBuild && <button className="btn btn-primary btn-block mt-4" onClick={() => client.generateDocs()}>Generate Docs</button>} )}
{hasBuild && (
<button className="btn btn-primary btn-block mt-4" onClick={() => client.generateDocs()}>
Generate Docs
</button>
)}
</div> </div>
) )
} }

@ -1,4 +1,4 @@
import { useState } from "react"; import { useState } from 'react'
export function useLocalStorage(key: string, initialValue: any) { export function useLocalStorage(key: string, initialValue: any) {
// State to store our value // State to store our value
@ -6,32 +6,31 @@ export function useLocalStorage(key: string, initialValue: any) {
const [storedValue, setStoredValue] = useState(() => { const [storedValue, setStoredValue] = useState(() => {
try { try {
// Get from local storage by key // Get from local storage by key
const item = window.localStorage.getItem(key); const item = window.localStorage.getItem(key)
// Parse stored json or if none return initialValue // Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue; return item ? JSON.parse(item) : initialValue
} catch (error) { } catch (error) {
// If error also return initialValue // If error also return initialValue
console.log(error); console.log(error)
return initialValue; return initialValue
} }
}); })
// Return a wrapped version of useState's setter function that ... // Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage. // ... persists the new value to localStorage.
const setValue = (value: any) => { const setValue = (value: any) => {
try { try {
// Allow value to be a function so we have same API as useState // Allow value to be a function so we have same API as useState
const valueToStore = const valueToStore = value instanceof Function ? value(storedValue) : value
value instanceof Function ? value(storedValue) : value;
// Save state // Save state
setStoredValue(valueToStore); setStoredValue(valueToStore)
// Save to local storage // Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore)); window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) { } catch (error) {
// A more advanced implementation would handle the error case // A more advanced implementation would handle the error case
console.log(error); console.log(error)
}
} }
};
return [storedValue, setValue]; return [storedValue, setValue]
} }

@ -1,31 +1,28 @@
import React from "react"; import React from 'react'
export const ErrorView: React.FC = () => { export const ErrorView: React.FC = () => {
return ( return (
<div <div
style={{ style={{
width: "100%", width: '100%',
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
alignItems: "center", alignItems: 'center',
}} }}
> >
<img <img
style={{ paddingBottom: "2em" }} style={{ paddingBottom: '2em' }}
width="250" width="250"
src="https://res.cloudinary.com/key-solutions/image/upload/v1580400635/solid/error-png.png" src="https://res.cloudinary.com/key-solutions/image/upload/v1580400635/solid/error-png.png"
alt="Error page" alt="Error page"
/> />
<h5>Sorry, something unexpected happened. </h5> <h5>Sorry, something unexpected happened. </h5>
<h5> <h5>
Please raise an issue:{" "} Please raise an issue:{' '}
<a <a style={{ color: 'red' }} href="https://github.com/Machinalabs/remix-ethdoc-plugin/issues">
style={{ color: "red" }}
href="https://github.com/Machinalabs/remix-ethdoc-plugin/issues"
>
Here Here
</a> </a>
</h5> </h5>
</div> </div>
); )
}; }

@ -1,11 +1,11 @@
import React from "react"; import React from 'react'
import ReactDOM from "react-dom"; import ReactDOM from 'react-dom'
import App from "./app/App"; import App from './app/App'
// import { Routes } from "./routes"; // import { Routes } from "./routes";
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>,
document.getElementById("root") document.getElementById('root'),
); )

@ -1,19 +1,19 @@
const { composePlugins, withNx } = require('@nrwl/webpack') const { composePlugins, withNx } = require('@nrwl/webpack')
const { withReact } = require('@nrwl/react') const { withReact } = require('@nrwl/react')
const webpack = require('webpack') const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack. // Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => { module.exports = composePlugins(withNx(), withReact(), config => {
// Update the webpack config as needed here. // Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())` // e.g. `config.plugins.push(new MyPlugin())`
// add fallback for node modules // add fallback for node modules
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"path": require.resolve("path-browserify"), path: require.resolve('path-browserify'),
"fs": false, fs: false,
} }
// add externals // add externals
@ -24,10 +24,9 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
config.module.rules.push({ config.module.rules.push({
test: /\.hbs$/, test: /\.hbs$/,
type: 'asset/source' type: 'asset/source',
}) })
// add public path // add public path
config.output.publicPath = '/' config.output.publicPath = '/'
@ -38,16 +37,14 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser',
}), }),
new webpack.DefinePlugin({ new webpack.DefinePlugin({}),
}),
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre',
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
@ -67,7 +64,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
extractComments: false, extractComments: false,
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin(),
]; ]
return config; return config
}) })

@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react" import React, {useEffect, useState} from 'react'
import { DocViewer } from "./docviewer" import {DocViewer} from './docviewer'
import ReactMarkdown from 'react-markdown' import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
@ -11,7 +11,6 @@ export default function App() {
client.eventEmitter.on('contentsReady', (fileContents: string) => { client.eventEmitter.on('contentsReady', (fileContents: string) => {
setContents(fileContents) setContents(fileContents)
}) })
}, []) }, [])
return ( return (
<> <>

@ -6,5 +6,5 @@ ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>,
document.getElementById("root") document.getElementById('root')
); )

@ -1,8 +1,8 @@
const {composePlugins, withNx} = require('@nrwl/webpack') const {composePlugins, withNx} = require('@nrwl/webpack')
const {withReact} = require('@nrwl/react') const {withReact} = require('@nrwl/react')
const webpack = require('webpack') const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack. // Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => { module.exports = composePlugins(withNx(), withReact(), (config) => {
@ -12,7 +12,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// add externals // add externals
config.externals = { config.externals = {
...config.externals, ...config.externals,
solc: 'solc', solc: 'solc'
} }
// add public path // add public path
@ -23,18 +23,16 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}),
new webpack.DefinePlugin({
}), }),
new webpack.DefinePlugin({})
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
@ -48,13 +46,13 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
return config; return config
}) })

@ -1,24 +1,24 @@
import React from "react" import React from 'react'
import { PluginClient } from "@remixproject/plugin" import {PluginClient} from '@remixproject/plugin'
import { Receipt, ThemeType } from "./types" import {Receipt, ThemeType} from './types'
export const AppContext = React.createContext({ export const AppContext = React.createContext({
apiKey: "", apiKey: '',
setAPIKey: (value: string) => { setAPIKey: (value: string) => {
console.log("Set API Key from Context") console.log('Set API Key from Context')
}, },
clientInstance: {} as PluginClient, clientInstance: {} as PluginClient,
receipts: [] as Receipt[], receipts: [] as Receipt[],
setReceipts: (receipts: Receipt[]) => { setReceipts: (receipts: Receipt[]) => {
console.log("Calling Set Receipts") console.log('Calling Set Receipts')
}, },
contracts: [] as string[], contracts: [] as string[],
setContracts: (contracts: string[]) => { setContracts: (contracts: string[]) => {
console.log("Calling Set Contract Names") console.log('Calling Set Contract Names')
}, },
themeType: "dark" as ThemeType, themeType: 'dark' as ThemeType,
setThemeType: (themeType: ThemeType) => { setThemeType: (themeType: ThemeType) => {
console.log("Calling Set Theme Type") console.log('Calling Set Theme Type')
}, }
}) })

@ -1,24 +1,43 @@
import { PluginClient } from '@remixproject/plugin'; import {PluginClient} from '@remixproject/plugin'
import { verify, EtherScanReturn } from './utils/verify'; import {verify, EtherScanReturn} from './utils/verify'
import { getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus } from './utils'; import {getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus} from './utils'
export class RemixClient extends PluginClient { export class RemixClient extends PluginClient {
loaded() { loaded() {
return this.onload() return this.onload()
} }
async verify (apiKey: string, contractAddress: string, contractArguments: string, contractName: string, compilationResultParam: any, chainRef?: number | string, isProxyContract?: boolean, expectedImplAddress?: string) { async verify(
const result = await verify(apiKey, contractAddress, contractArguments, contractName, compilationResultParam, chainRef, isProxyContract, expectedImplAddress, this, apiKey: string,
(value: EtherScanReturn) => {}, (value: string) => {}) contractAddress: string,
contractArguments: string,
contractName: string,
compilationResultParam: any,
chainRef?: number | string,
isProxyContract?: boolean,
expectedImplAddress?: string
) {
const result = await verify(
apiKey,
contractAddress,
contractArguments,
contractName,
compilationResultParam,
chainRef,
isProxyContract,
expectedImplAddress,
this,
(value: EtherScanReturn) => {},
(value: string) => {}
)
return result return result
} }
async receiptStatus(receiptGuid: string, apiKey: string, isProxyContract: boolean) { async receiptStatus(receiptGuid: string, apiKey: string, isProxyContract: boolean) {
try { try {
const {network, networkId} = await getNetworkName(this) const {network, networkId} = await getNetworkName(this)
if (network === "vm") { if (network === 'vm') {
throw new Error("Cannot check the receipt status in the selected network") throw new Error('Cannot check the receipt status in the selected network')
} }
const etherscanApi = getEtherScanApi(networkId) const etherscanApi = getEtherScanApi(networkId)
let receiptStatus let receiptStatus

@ -1,22 +1,19 @@
import React, { useState, useEffect, useRef } from "react" import React, {useState, useEffect, useRef} from 'react'
import { import {CompilationFileSources, CompilationResult} from '@remixproject/plugin-api'
CompilationFileSources,
CompilationResult,
} from "@remixproject/plugin-api"
import { RemixClient } from "./RemixPlugin"; import {RemixClient} from './RemixPlugin'
import { createClient } from "@remixproject/plugin-webview"; import {createClient} from '@remixproject/plugin-webview'
import { AppContext } from "./AppContext" import {AppContext} from './AppContext'
import { DisplayRoutes } from "./routes" import {DisplayRoutes} from './routes'
import { useLocalStorage } from "./hooks/useLocalStorage" import {useLocalStorage} from './hooks/useLocalStorage'
import { getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus } from "./utils" import {getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus} from './utils'
import { Receipt, ThemeType } from "./types" import {Receipt, ThemeType} from './types'
import "./App.css" import './App.css'
export const getNewContractNames = (compilationResult: CompilationResult) => { export const getNewContractNames = (compilationResult: CompilationResult) => {
const compiledContracts = compilationResult.contracts const compiledContracts = compilationResult.contracts
@ -31,11 +28,11 @@ export const getNewContractNames = (compilationResult: CompilationResult) => {
} }
const App = () => { const App = () => {
const [apiKey, setAPIKey] = useLocalStorage("apiKey", "") const [apiKey, setAPIKey] = useLocalStorage('apiKey', '')
const [clientInstance, setClientInstance] = useState(undefined as any) const [clientInstance, setClientInstance] = useState(undefined as any)
const [receipts, setReceipts] = useLocalStorage("receipts", []) const [receipts, setReceipts] = useLocalStorage('receipts', [])
const [contracts, setContracts] = useState([] as string[]) const [contracts, setContracts] = useState([] as string[])
const [themeType, setThemeType] = useState("dark" as ThemeType) const [themeType, setThemeType] = useState('dark' as ThemeType)
const timer = useRef(null) const timer = useRef(null)
const clientInstanceRef = useRef(clientInstance) const clientInstanceRef = useRef(clientInstance)
@ -49,26 +46,15 @@ const App = () => {
const loadClient = async () => { const loadClient = async () => {
await client.onload() await client.onload()
setClientInstance(client) setClientInstance(client)
client.on("solidity", client.on('solidity', 'compilationFinished', (fileName: string, source: CompilationFileSources, languageVersion: string, data: CompilationResult) => {
"compilationFinished",
(
fileName: string,
source: CompilationFileSources,
languageVersion: string,
data: CompilationResult
) => {
const newContractsNames = getNewContractNames(data) const newContractsNames = getNewContractNames(data)
const newContractsToSave: string[] = [ const newContractsToSave: string[] = [...contractsRef.current, ...newContractsNames]
...contractsRef.current,
...newContractsNames,
]
const uniqueContracts: string[] = [...new Set(newContractsToSave)] const uniqueContracts: string[] = [...new Set(newContractsToSave)]
setContracts(uniqueContracts) setContracts(uniqueContracts)
} })
)
//const currentTheme = await client.call("theme", "currentTheme") //const currentTheme = await client.call("theme", "currentTheme")
//setThemeType(currentTheme.quality) //setThemeType(currentTheme.quality)
@ -82,7 +68,7 @@ const App = () => {
useEffect(() => { useEffect(() => {
let receiptsNotVerified: Receipt[] = receipts.filter((item: Receipt) => { let receiptsNotVerified: Receipt[] = receipts.filter((item: Receipt) => {
return item.status === "Pending in queue" || item.status === "Max rate limit reached" return item.status === 'Pending in queue' || item.status === 'Max rate limit reached'
}) })
if (receiptsNotVerified.length > 0) { if (receiptsNotVerified.length > 0) {
@ -96,36 +82,26 @@ const App = () => {
return return
} }
if (network === "vm") { if (network === 'vm') {
return return
} }
let newReceipts = receipts let newReceipts = receipts
for (const item of receiptsNotVerified) { for (const item of receiptsNotVerified) {
await new Promise(r => setTimeout(r, 500)) // avoid api rate limit exceed. await new Promise((r) => setTimeout(r, 500)) // avoid api rate limit exceed.
let status let status
if (item.isProxyContract) { if (item.isProxyContract) {
status = await getProxyContractReceiptStatus( status = await getProxyContractReceiptStatus(item.guid, apiKey, getEtherScanApi(networkId))
item.guid,
apiKey,
getEtherScanApi(networkId)
)
if (status.status === '1') { if (status.status === '1') {
status.message = status.result status.message = status.result
status.result = 'Successfully Updated' status.result = 'Successfully Updated'
} }
} else } else status = await getReceiptStatus(item.guid, apiKey, getEtherScanApi(networkId))
status = await getReceiptStatus( if (status.result === 'Pass - Verified' || status.result === 'Already Verified' || status.result === 'Successfully Updated') {
item.guid,
apiKey,
getEtherScanApi(networkId)
)
if (status.result === "Pass - Verified" || status.result === "Already Verified" ||
status.result === "Successfully Updated") {
newReceipts = newReceipts.map((currentReceipt: Receipt) => { newReceipts = newReceipts.map((currentReceipt: Receipt) => {
if (currentReceipt.guid === item.guid) { if (currentReceipt.guid === item.guid) {
let res = { const res = {
...currentReceipt, ...currentReceipt,
status: status.result, status: status.result
} }
if (currentReceipt.isProxyContract) res.message = status.message if (currentReceipt.isProxyContract) res.message = status.message
return res return res
@ -135,7 +111,7 @@ const App = () => {
} }
} }
receiptsNotVerified = newReceipts.filter((item: Receipt) => { receiptsNotVerified = newReceipts.filter((item: Receipt) => {
return item.status === "Pending in queue" || item.status === "Max rate limit reached" return item.status === 'Pending in queue' || item.status === 'Max rate limit reached'
}) })
if (timer.current && receiptsNotVerified.length === 0) { if (timer.current && receiptsNotVerified.length === 0) {
clearInterval(timer.current) clearInterval(timer.current)
@ -157,7 +133,7 @@ const App = () => {
contracts, contracts,
setContracts, setContracts,
themeType, themeType,
setThemeType, setThemeType
}} }}
> >
<DisplayRoutes /> <DisplayRoutes />

@ -1,8 +1,8 @@
import React from "react" import React from 'react'
import { NavLink } from "react-router-dom" import {NavLink} from 'react-router-dom'
import {CustomTooltip} from '@remix-ui/helper' import {CustomTooltip} from '@remix-ui/helper'
import { AppContext } from "../AppContext" import {AppContext} from '../AppContext'
interface Props { interface Props {
title?: string title?: string
@ -18,17 +18,13 @@ const HomeIcon: React.FC<IconProps> = ({ from }: IconProps) => {
<NavLink <NavLink
data-id="home" data-id="home"
to={{ to={{
pathname: "/" pathname: '/'
}} }}
className={({ isActive }) => isActive ? "border border-secondary shadow-none btn p-1 m-0" : "border-0 shadow-none btn p-1 m-0"} className={({isActive}) => (isActive ? 'border border-secondary shadow-none btn p-1 m-0' : 'border-0 shadow-none btn p-1 m-0')}
style={ ({ isActive }) => !isActive ? { width: "1.8rem", filter: "contrast(0.5)"} : {width: "1.8rem"}} style={({isActive}) => (!isActive ? {width: '1.8rem', filter: 'contrast(0.5)'} : {width: '1.8rem'})}
state={from} state={from}
> >
<CustomTooltip <CustomTooltip tooltipText="Home" tooltipId="etherscan-nav-home" placement="bottom">
tooltipText='Home'
tooltipId='etherscan-nav-home'
placement='bottom'
>
<i className="fas fa-home"></i> <i className="fas fa-home"></i>
</CustomTooltip> </CustomTooltip>
</NavLink> </NavLink>
@ -40,17 +36,13 @@ const ReceiptsIcon: React.FC<IconProps> = ({ from }: IconProps) => {
<NavLink <NavLink
data-id="receipts" data-id="receipts"
to={{ to={{
pathname: "/receipts" pathname: '/receipts'
}} }}
className={({ isActive }) => isActive ? "border border-secondary shadow-none btn p-1 m-0" : "border-0 shadow-none btn p-1 m-0"} className={({isActive}) => (isActive ? 'border border-secondary shadow-none btn p-1 m-0' : 'border-0 shadow-none btn p-1 m-0')}
style={ ({ isActive }) => !isActive ? { width: "1.8rem", filter: "contrast(0.5)"} : {width: "1.8rem"}} style={({isActive}) => (!isActive ? {width: '1.8rem', filter: 'contrast(0.5)'} : {width: '1.8rem'})}
state={from} state={from}
> >
<CustomTooltip <CustomTooltip tooltipText="Receipts" tooltipId="etherscan-nav-receipts" placement="bottom">
tooltipText='Receipts'
tooltipId='etherscan-nav-receipts'
placement='bottom'
>
<i className="fas fa-receipt"></i> <i className="fas fa-receipt"></i>
</CustomTooltip> </CustomTooltip>
</NavLink> </NavLink>
@ -62,27 +54,20 @@ const SettingsIcon: React.FC<IconProps> = ({ from }: IconProps) => {
<NavLink <NavLink
data-id="settings" data-id="settings"
to={{ to={{
pathname: "/settings" pathname: '/settings'
}} }}
className={({ isActive }) => isActive ? "border border-secondary shadow-none btn p-1 m-0" : "border-0 shadow-none btn p-1 m-0"} className={({isActive}) => (isActive ? 'border border-secondary shadow-none btn p-1 m-0' : 'border-0 shadow-none btn p-1 m-0')}
style={ ({ isActive }) => !isActive ? { width: "1.8rem", filter: "contrast(0.5)"} : {width: "1.8rem"}} style={({isActive}) => (!isActive ? {width: '1.8rem', filter: 'contrast(0.5)'} : {width: '1.8rem'})}
state={from} state={from}
> >
<CustomTooltip <CustomTooltip tooltipText="Settings" tooltipId="etherscan-nav-settings" placement="bottom">
tooltipText='Settings'
tooltipId='etherscan-nav-settings'
placement='bottom'
>
<i className="fas fa-cog"></i> <i className="fas fa-cog"></i>
</CustomTooltip> </CustomTooltip>
</NavLink> </NavLink>
) )
} }
export const HeaderWithSettings: React.FC<Props> = ({ export const HeaderWithSettings: React.FC<Props> = ({title = '', from}) => {
title = "",
from,
}) => {
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
{() => ( {() => (

@ -1,4 +1,4 @@
import React from "react" import React from 'react'
import {CustomTooltip} from '@remix-ui/helper' import {CustomTooltip} from '@remix-ui/helper'
interface Props { interface Props {
@ -8,35 +8,21 @@ interface Props {
disable?: boolean disable?: boolean
} }
export const SubmitButton: React.FC<Props> = ({ export const SubmitButton: React.FC<Props> = ({text, dataId, isSubmitting = false, disable = true}) => {
text,
dataId,
isSubmitting = false,
disable = true
}) => {
return ( return (
<div> <div>
<button <button data-id={dataId} type="submit" className="btn btn-primary btn-block p-1 text-decoration-none" disabled={disable}>
data-id={dataId}
type="submit"
className="btn btn-primary btn-block p-1 text-decoration-none"
disabled={disable}
>
<CustomTooltip <CustomTooltip
tooltipText={disable ? "Fill in the valid value(s) and select a supported network" : "Click to proceed"} tooltipText={disable ? 'Fill in the valid value(s) and select a supported network' : 'Click to proceed'}
tooltipId={'etherscan-submit-button-' + dataId} tooltipId={'etherscan-submit-button-' + dataId}
tooltipTextClasses="border bg-light text-dark p-1 pr-3" tooltipTextClasses="border bg-light text-dark p-1 pr-3"
placement='bottom' placement="bottom"
> >
<div> <div>
{!isSubmitting && text} {!isSubmitting && text}
{isSubmitting && ( {isSubmitting && (
<div> <div>
<span <span className="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true" />
className="spinner-border spinner-border-sm mr-1"
role="status"
aria-hidden="true"
/>
Verifying... Please wait Verifying... Please wait
</div> </div>
)} )}

@ -1,4 +1,4 @@
import { useState } from "react" import {useState} from 'react'
export function useLocalStorage(key: string, initialValue: any) { export function useLocalStorage(key: string, initialValue: any) {
// State to store our value // State to store our value
@ -21,8 +21,7 @@ export function useLocalStorage(key: string, initialValue: any) {
const setValue = (value: any) => { const setValue = (value: any) => {
try { try {
// Allow value to be a function so we have same API as useState // Allow value to be a function so we have same API as useState
const valueToStore = const valueToStore = value instanceof Function ? value(storedValue) : value
value instanceof Function ? value(storedValue) : value
// Save state // Save state
setStoredValue(valueToStore) setStoredValue(valueToStore)
// Save to local storage // Save to local storage

@ -1,17 +1,13 @@
import React, { PropsWithChildren } from "react" import React, {PropsWithChildren} from 'react'
import { HeaderWithSettings } from "../components" import {HeaderWithSettings} from '../components'
interface Props { interface Props {
from: string from: string
title?: string title?: string
} }
export const DefaultLayout: React.FC<PropsWithChildren<Props>> = ({ export const DefaultLayout: React.FC<PropsWithChildren<Props>> = ({children, from, title}) => {
children,
from,
title
}) => {
return ( return (
<div> <div>
<HeaderWithSettings from={from} title={title} /> <HeaderWithSettings from={from} title={title} />

@ -1,13 +1,8 @@
import React from "react" import React from 'react'
import { import {HashRouter as Router, Route, Routes, RouteProps} from 'react-router-dom'
HashRouter as Router,
Route,
Routes,
RouteProps,
} from "react-router-dom"
import { ErrorView, HomeView, ReceiptsView, CaptureKeyView } from "./views" import {ErrorView, HomeView, ReceiptsView, CaptureKeyView} from './views'
import { DefaultLayout } from "./layouts" import {DefaultLayout} from './layouts'
interface Props extends RouteProps { interface Props extends RouteProps {
component: any // TODO: new (props: any) => React.Component component: any // TODO: new (props: any) => React.Component
@ -25,8 +20,7 @@ export const DisplayRoutes = () => (
</DefaultLayout> </DefaultLayout>
} }
/> />
<Route path="/error" <Route path="/error" element={<ErrorView />} />
element={<ErrorView />} />
<Route <Route
path="/receipts" path="/receipts"
element={ element={

@ -1,15 +1,15 @@
import React, { useState } from "react" import React, {useState} from 'react'
import { Formik, ErrorMessage, Field } from "formik" import {Formik, ErrorMessage, Field} from 'formik'
import { useNavigate, useLocation } from "react-router-dom" import {useNavigate, useLocation} from 'react-router-dom'
import { AppContext } from "../AppContext" import {AppContext} from '../AppContext'
import { SubmitButton } from "../components" import {SubmitButton} from '../components'
export const CaptureKeyView: React.FC = () => { export const CaptureKeyView: React.FC = () => {
const location = useLocation() const location = useLocation()
const navigate = useNavigate() const navigate = useNavigate()
const [msg, setMsg] = useState("") const [msg, setMsg] = useState('')
return ( return (
<AppContext.Consumer> <AppContext.Consumer>
{({apiKey, clientInstance, setAPIKey}) => { {({apiKey, clientInstance, setAPIKey}) => {
@ -21,9 +21,9 @@ export const CaptureKeyView: React.FC = () => {
validate={(values) => { validate={(values) => {
const errors = {} as any const errors = {} as any
if (!values.apiKey) { if (!values.apiKey) {
errors.apiKey = "Required" errors.apiKey = 'Required'
} else if (values.apiKey.length !== 34) { } else if (values.apiKey.length !== 34) {
errors.apiKey = "API key should be 34 characters long" errors.apiKey = 'API key should be 34 characters long'
} }
return errors return errors
}} }}
@ -31,7 +31,7 @@ export const CaptureKeyView: React.FC = () => {
const apiKey = values.apiKey const apiKey = values.apiKey
if (apiKey.length === 34) { if (apiKey.length === 34) {
setAPIKey(values.apiKey) setAPIKey(values.apiKey)
navigate((location && location.state ? location.state : '/')) navigate(location && location.state ? location.state : '/')
} }
}} }}
> >
@ -40,20 +40,12 @@ export const CaptureKeyView: React.FC = () => {
<div className="form-group mb-2"> <div className="form-group mb-2">
<label htmlFor="apikey">API Key</label> <label htmlFor="apikey">API Key</label>
<Field <Field
className={ className={errors.apiKey && touched.apiKey ? 'form-control form-control-sm is-invalid' : 'form-control form-control-sm'}
errors.apiKey && touched.apiKey
? "form-control form-control-sm is-invalid"
: "form-control form-control-sm"
}
type="password" type="password"
name="apiKey" name="apiKey"
placeholder="e.g. GM1T20XY6JGSAPWKDCYZ7B2FJXKTJRFVGZ" placeholder="e.g. GM1T20XY6JGSAPWKDCYZ7B2FJXKTJRFVGZ"
/> />
<ErrorMessage <ErrorMessage className="invalid-feedback" name="apiKey" component="div" />
className="invalid-feedback"
name="apiKey"
component="div"
/>
</div> </div>
<div> <div>
@ -63,12 +55,7 @@ export const CaptureKeyView: React.FC = () => {
)} )}
</Formik> </Formik>
<div <div data-id="api-key-result" className="text-primary mt-4 text-center" style={{fontSize: '0.8em'}} dangerouslySetInnerHTML={{__html: msg}} />
data-id="api-key-result"
className="text-primary mt-4 text-center"
style={{fontSize: "0.8em"}}
dangerouslySetInnerHTML={{ __html: msg }}
/>
</div> </div>
) )
}} }}

@ -1,21 +1,13 @@
import React from "react" import React from 'react'
export const ErrorView: React.FC = () => { export const ErrorView: React.FC = () => {
return ( return (
<div className="d-flex w-100 flex-column align-items-center"> <div className="d-flex w-100 flex-column align-items-center">
<img <img className="pb-4" width="250" src="https://res.cloudinary.com/key-solutions/image/upload/v1580400635/solid/error-png.png" alt="Error page" />
className="pb-4"
width="250"
src="https://res.cloudinary.com/key-solutions/image/upload/v1580400635/solid/error-png.png"
alt="Error page"
/>
<h5>Sorry, something unexpected happened.</h5> <h5>Sorry, something unexpected happened.</h5>
<h5> <h5>
Please raise an issue:{" "} Please raise an issue:{' '}
<a <a className="text-danger" href="https://github.com/ethereum/remix-project/issues">
className="text-danger"
href="https://github.com/ethereum/remix-project/issues"
>
Here Here
</a> </a>
</h5> </h5>

@ -1,11 +1,11 @@
import React from "react" import React from 'react'
import { Navigate } from "react-router-dom" import {Navigate} from 'react-router-dom'
import { AppContext } from "../AppContext" import {AppContext} from '../AppContext'
import { Receipt } from "../types" import {Receipt} from '../types'
import { VerifyView } from "./VerifyView" import {VerifyView} from './VerifyView'
export const HomeView: React.FC = () => { export const HomeView: React.FC = () => {
return ( return (
@ -14,7 +14,7 @@ export const HomeView: React.FC = () => {
return !apiKey ? ( return !apiKey ? (
<Navigate <Navigate
to={{ to={{
pathname: "/settings" pathname: '/settings'
}} }}
/> />
) : ( ) : (
@ -28,8 +28,7 @@ export const HomeView: React.FC = () => {
}} }}
/> />
) )
} }}
}
</AppContext.Consumer> </AppContext.Consumer>
) )
} }

@ -1,12 +1,12 @@
import React, { useState } from "react" import React, {useState} from 'react'
import { Formik, ErrorMessage, Field } from "formik" import {Formik, ErrorMessage, Field} from 'formik'
import { getEtherScanApi, getNetworkName, getReceiptStatus, getProxyContractReceiptStatus } from "../utils" import {getEtherScanApi, getNetworkName, getReceiptStatus, getProxyContractReceiptStatus} from '../utils'
import { Receipt } from "../types" import {Receipt} from '../types'
import { AppContext } from "../AppContext" import {AppContext} from '../AppContext'
import { SubmitButton } from "../components" import {SubmitButton} from '../components'
import { Navigate } from "react-router-dom" import {Navigate} from 'react-router-dom'
import { Button } from "react-bootstrap" import {Button} from 'react-bootstrap'
import {CustomTooltip} from '@remix-ui/helper' import {CustomTooltip} from '@remix-ui/helper'
interface FormValues { interface FormValues {
@ -17,38 +17,25 @@ export const ReceiptsView: React.FC = () => {
const [results, setResults] = useState({succeed: false, message: ''}) const [results, setResults] = useState({succeed: false, message: ''})
const [isProxyContractReceipt, setIsProxyContractReceipt] = useState(false) const [isProxyContractReceipt, setIsProxyContractReceipt] = useState(false)
const onGetReceiptStatus = async ( const onGetReceiptStatus = async (values: FormValues, clientInstance: any, apiKey: string) => {
values: FormValues,
clientInstance: any,
apiKey: string
) => {
try { try {
const {network, networkId} = await getNetworkName(clientInstance) const {network, networkId} = await getNetworkName(clientInstance)
if (network === "vm") { if (network === 'vm') {
setResults({ setResults({
succeed: false, succeed: false,
message: "Cannot verify in the selected network" message: 'Cannot verify in the selected network'
}) })
return return
} }
const etherscanApi = getEtherScanApi(networkId) const etherscanApi = getEtherScanApi(networkId)
let result let result
if (isProxyContractReceipt) { if (isProxyContractReceipt) {
result = await getProxyContractReceiptStatus( result = await getProxyContractReceiptStatus(values.receiptGuid, apiKey, etherscanApi)
values.receiptGuid,
apiKey,
etherscanApi
)
if (result.status === '1') { if (result.status === '1') {
result.message = result.result result.message = result.result
result.result = 'Successfully Updated' result.result = 'Successfully Updated'
} }
} else } else result = await getReceiptStatus(values.receiptGuid, apiKey, etherscanApi)
result = await getReceiptStatus(
values.receiptGuid,
apiKey,
etherscanApi
)
setResults({ setResults({
succeed: result.status === '1' ? true : false, succeed: result.status === '1' ? true : false,
message: result.result || (result.status === '0' ? 'Verification failed' : result.message) message: result.result || (result.status === '0' ? 'Verification failed' : result.message)
@ -67,42 +54,32 @@ export const ReceiptsView: React.FC = () => {
return !apiKey ? ( return !apiKey ? (
<Navigate <Navigate
to={{ to={{
pathname: "/settings" pathname: '/settings'
}} }}
/> />
) : ( ) : (
<div> <div>
<Formik <Formik
initialValues={{ receiptGuid: "" }} initialValues={{receiptGuid: ''}}
validate={(values) => { validate={(values) => {
const errors = {} as any const errors = {} as any
if (!values.receiptGuid) { if (!values.receiptGuid) {
errors.receiptGuid = "Required" errors.receiptGuid = 'Required'
} }
return errors return errors
}} }}
onSubmit={(values) => onSubmit={(values) => onGetReceiptStatus(values, clientInstance, apiKey)}
onGetReceiptStatus(values, clientInstance, apiKey)
}
> >
{({errors, touched, handleSubmit, handleChange}) => ( {({errors, touched, handleSubmit, handleChange}) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className="form-group mb-2"> <div className="form-group mb-2">
<label htmlFor="receiptGuid">Receipt GUID</label> <label htmlFor="receiptGuid">Receipt GUID</label>
<Field <Field
className={ className={errors.receiptGuid && touched.receiptGuid ? 'form-control form-control-sm is-invalid' : 'form-control form-control-sm'}
errors.receiptGuid && touched.receiptGuid
? "form-control form-control-sm is-invalid"
: "form-control form-control-sm"
}
type="text" type="text"
name="receiptGuid" name="receiptGuid"
/> />
<ErrorMessage <ErrorMessage className="invalid-feedback" name="receiptGuid" component="div" />
className="invalid-feedback"
name="receiptGuid"
component="div"
/>
</div> </div>
<div className="d-flex mb-2 custom-control custom-checkbox"> <div className="d-flex mb-2 custom-control custom-checkbox">
@ -117,7 +94,9 @@ export const ReceiptsView: React.FC = () => {
else setIsProxyContractReceipt(false) else setIsProxyContractReceipt(false)
}} }}
/> />
<label className="form-check-label custom-control-label" htmlFor="isProxyReceipt">It's a proxy contract GUID</label> <label className="form-check-label custom-control-label" htmlFor="isProxyReceipt">
It's a proxy contract GUID
</label>
</div> </div>
<SubmitButton text="Check" disable={!touched.receiptGuid || (touched.receiptGuid && errors.receiptGuid) ? true : false} /> <SubmitButton text="Check" disable={!touched.receiptGuid || (touched.receiptGuid && errors.receiptGuid) ? true : false} />
</form> </form>
@ -125,22 +104,27 @@ export const ReceiptsView: React.FC = () => {
</Formik> </Formik>
<div <div
className={results['succeed'] ? "text-success mt-3 text-center" : "text-danger mt-3 text-center"} className={results['succeed'] ? 'text-success mt-3 text-center' : 'text-danger mt-3 text-center'}
dangerouslySetInnerHTML={{ __html: results.message ? results.message : '' }} dangerouslySetInnerHTML={{
__html: results.message ? results.message : ''
}}
/> />
<ReceiptsTable receipts={receipts} /><br/> <ReceiptsTable receipts={receipts} />
<CustomTooltip <br />
tooltipText="Clear the list of receipts" <CustomTooltip tooltipText="Clear the list of receipts" tooltipId="etherscan-clear-receipts" placement="bottom">
tooltipId='etherscan-clear-receipts' <Button
placement='bottom' className="btn-sm"
onClick={() => {
setReceipts([])
}}
> >
<Button className="btn-sm" onClick={() => { setReceipts([]) }} >Clear</Button> Clear
</Button>
</CustomTooltip> </CustomTooltip>
</div> </div>
) )
} }}
}
</AppContext.Consumer> </AppContext.Consumer>
) )
} }
@ -162,20 +146,23 @@ const ReceiptsTable: React.FC<{ receipts: Receipt[] }> = ({ receipts }) => {
receipts.map((item: Receipt, index) => { receipts.map((item: Receipt, index) => {
return ( return (
<tr key={item.guid}> <tr key={item.guid}>
<td className={(item.status === 'Pass - Verified' || item.status === 'Successfully Updated') <td
? 'text-success' : (item.status === 'Pending in queue' className={
? 'text-warning' : (item.status === 'Already Verified' item.status === 'Pass - Verified' || item.status === 'Successfully Updated'
? 'text-info': 'text-secondary'))}> ? 'text-success'
{item.status} : item.status === 'Pending in queue'
{item.status === 'Successfully Updated' && <CustomTooltip ? 'text-warning'
placement={'bottom'} : item.status === 'Already Verified'
tooltipClasses="text-wrap" ? 'text-info'
tooltipId="etherscan-receipt-proxy-status" : 'text-secondary'
tooltipText={item.message} }
> >
{item.status}
{item.status === 'Successfully Updated' && (
<CustomTooltip placement={'bottom'} tooltipClasses="text-wrap" tooltipId="etherscan-receipt-proxy-status" tooltipText={item.message}>
<i style={{fontSize: 'small'}} className={'ml-1 fal fa-info-circle align-self-center'} aria-hidden="true"></i> <i style={{fontSize: 'small'}} className={'ml-1 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
</CustomTooltip> </CustomTooltip>
} )}
</td> </td>
<td>{item.guid}</td> <td>{item.guid}</td>
</tr> </tr>

@ -1,16 +1,14 @@
import React, { useEffect, useRef, useState } from "react" import React, {useEffect, useRef, useState} from 'react'
import Web3 from 'web3' import Web3 from 'web3'
import { import {PluginClient} from '@remixproject/plugin'
PluginClient,
} from "@remixproject/plugin"
import {CustomTooltip} from '@remix-ui/helper' import {CustomTooltip} from '@remix-ui/helper'
import { Formik, ErrorMessage, Field } from "formik" import {Formik, ErrorMessage, Field} from 'formik'
import { SubmitButton } from "../components" import {SubmitButton} from '../components'
import { Receipt } from "../types" import {Receipt} from '../types'
import { verify } from "../utils/verify" import {verify} from '../utils/verify'
import { etherscanScripts } from "@remix-project/remix-ws-templates" import {etherscanScripts} from '@remix-project/remix-ws-templates'
interface Props { interface Props {
client: PluginClient client: PluginClient
@ -25,15 +23,10 @@ interface FormValues {
expectedImplAddress?: string expectedImplAddress?: string
} }
export const VerifyView: React.FC<Props> = ({ export const VerifyView: React.FC<Props> = ({apiKey, client, contracts, onVerifiedContract}) => {
apiKey, const [results, setResults] = useState('')
client, const [networkName, setNetworkName] = useState('Loading...')
contracts, const [selectedContract, setSelectedContract] = useState('')
onVerifiedContract,
}) => {
const [results, setResults] = useState("")
const [networkName, setNetworkName] = useState("Loading...")
const [selectedContract, setSelectedContract] = useState("")
const [showConstructorArgs, setShowConstructorArgs] = useState(false) const [showConstructorArgs, setShowConstructorArgs] = useState(false)
const [isProxyContract, setIsProxyContract] = useState(false) const [isProxyContract, setIsProxyContract] = useState(false)
const [constructorInputs, setConstructorInputs] = useState([]) const [constructorInputs, setConstructorInputs] = useState([])
@ -41,13 +34,13 @@ export const VerifyView: React.FC<Props> = ({
useEffect(() => { useEffect(() => {
if (client && client.on) { if (client && client.on) {
client.on("blockchain" as any, 'networkStatus', (result) => { client.on('blockchain' as any, 'networkStatus', (result) => {
setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`) setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`)
}) })
} }
return () => { return () => {
// To fix memory leak // To fix memory leak
if (client && client.off) client.off("blockchain" as any, 'networkStatus') if (client && client.off) client.off('blockchain' as any, 'networkStatus')
} }
}, [client]) }, [client])
@ -56,7 +49,7 @@ export const VerifyView: React.FC<Props> = ({
}, [contracts]) }, [contracts])
const updateConsFields = (contractName) => { const updateConsFields = (contractName) => {
client.call("compilerArtefacts" as any, "getArtefactsByContractName", contractName).then((result) => { client.call('compilerArtefacts' as any, 'getArtefactsByContractName', contractName).then((result) => {
const {artefact} = result const {artefact} = result
if (artefact && artefact.abi && artefact.abi[0] && artefact.abi[0].type && artefact.abi[0].type === 'constructor' && artefact.abi[0].inputs.length > 0) { if (artefact && artefact.abi && artefact.abi[0] && artefact.abi[0].type && artefact.abi[0].type === 'constructor' && artefact.abi[0].inputs.length > 0) {
setConstructorInputs(artefact.abi[0].inputs) setConstructorInputs(artefact.abi[0].inputs)
@ -69,13 +62,10 @@ export const VerifyView: React.FC<Props> = ({
} }
const onVerifyContract = async (values: FormValues) => { const onVerifyContract = async (values: FormValues) => {
const compilationResult = (await client.call( const compilationResult = (await client.call('solidity', 'getCompilationResult')) as any
"solidity",
"getCompilationResult"
)) as any
if (!compilationResult) { if (!compilationResult) {
throw new Error("no compilation result available") throw new Error('no compilation result available')
} }
const constructorValues = [] const constructorValues = []
@ -83,9 +73,9 @@ export const VerifyView: React.FC<Props> = ({
if (key.startsWith('contractArgValue')) constructorValues.push(values[key]) if (key.startsWith('contractArgValue')) constructorValues.push(values[key])
} }
const web3 = new Web3() const web3 = new Web3()
const constructorTypes = constructorInputs.map(e => e.type) const constructorTypes = constructorInputs.map((e) => e.type)
let contractArguments = web3.eth.abi.encodeParameters(constructorTypes, constructorValues) let contractArguments = web3.eth.abi.encodeParameters(constructorTypes, constructorValues)
contractArguments = contractArguments.replace("0x", "") contractArguments = contractArguments.replace('0x', '')
verificationResult.current = await verify( verificationResult.current = await verify(
apiKey, apiKey,
@ -98,7 +88,7 @@ export const VerifyView: React.FC<Props> = ({
values.expectedImplAddress, values.expectedImplAddress,
client, client,
onVerifiedContract, onVerifiedContract,
setResults, setResults
) )
setResults(verificationResult.current['message']) setResults(verificationResult.current['message'])
} }
@ -107,52 +97,42 @@ export const VerifyView: React.FC<Props> = ({
<div> <div>
<Formik <Formik
initialValues={{ initialValues={{
contractName: "", contractName: '',
contractAddress: "" contractAddress: ''
}} }}
validate={(values) => { validate={(values) => {
const errors = {} as any const errors = {} as any
if (!values.contractName) { if (!values.contractName) {
errors.contractName = "Required" errors.contractName = 'Required'
} }
if (!values.contractAddress) { if (!values.contractAddress) {
errors.contractAddress = "Required" errors.contractAddress = 'Required'
} }
if (values.contractAddress.trim() === "" || !values.contractAddress.startsWith('0x') if (values.contractAddress.trim() === '' || !values.contractAddress.startsWith('0x') || values.contractAddress.length !== 42) {
|| values.contractAddress.length !== 42) { errors.contractAddress = 'Please enter a valid contract address'
errors.contractAddress = "Please enter a valid contract address"
} }
return errors return errors
}} }}
onSubmit={(values) => onVerifyContract(values)} onSubmit={(values) => onVerifyContract(values)}
> >
{({errors, touched, handleSubmit, handleChange, isSubmitting}) => { {({errors, touched, handleSubmit, handleChange, isSubmitting}) => {
return (<form onSubmit={handleSubmit}> return (
<form onSubmit={handleSubmit}>
<div className="form-group"> <div className="form-group">
<label htmlFor="network">Selected Network</label> <label htmlFor="network">Selected Network</label>
<CustomTooltip <CustomTooltip
tooltipText="Network is fetched from 'Deploy and Run Transactions' plugin's ENVIRONMENT field" tooltipText="Network is fetched from 'Deploy and Run Transactions' plugin's ENVIRONMENT field"
tooltipId='etherscan-impl-address2' tooltipId="etherscan-impl-address2"
placement='bottom' placement="bottom"
> >
<Field <Field className="form-control" type="text" name="network" value={networkName} disabled={true} />
className="form-control"
type="text"
name="network"
value={networkName}
disabled={true}
/>
</CustomTooltip> </CustomTooltip>
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="contractName">Contract Name</label> <label htmlFor="contractName">Contract Name</label>
<Field <Field
as="select" as="select"
className={ className={errors.contractName && touched.contractName && contracts.length ? 'form-control is-invalid' : 'form-control'}
errors.contractName && touched.contractName && contracts.length
? "form-control is-invalid"
: "form-control"
}
name="contractName" name="contractName"
onChange={async (e) => { onChange={async (e) => {
handleChange(e) handleChange(e)
@ -169,60 +149,30 @@ export const VerifyView: React.FC<Props> = ({
</option> </option>
))} ))}
</Field> </Field>
<ErrorMessage <ErrorMessage className="invalid-feedback" name="contractName" component="div" />
className="invalid-feedback"
name="contractName"
component="div"
/>
</div> </div>
<div className={showConstructorArgs ? 'form-group d-block' : 'form-group d-none'}> <div className={showConstructorArgs ? 'form-group d-block' : 'form-group d-none'}>
<label>Constructor Arguments</label> <label>Constructor Arguments</label>
{constructorInputs.map((item, index) => { {constructorInputs.map((item, index) => {
return ( return (
<div className="d-flex"> <div className="d-flex">
<Field <Field className="form-control m-1" type="text" key={`contractArgName${index}`} name={`contractArgName${index}`} value={item.name} disabled={true} />
className="form-control m-1" <CustomTooltip tooltipText={`value of ${item.name}`} tooltipId={`etherscan-constructor-value${index}`} placement="top">
type="text" <Field className="form-control m-1" type="text" key={`contractArgValue${index}`} name={`contractArgValue${index}`} placeholder={item.type} />
key={`contractArgName${index}`}
name={`contractArgName${index}`}
value={item.name}
disabled={true}
/>
<CustomTooltip
tooltipText={`value of ${item.name}`}
tooltipId={`etherscan-constructor-value${index}`}
placement='top'
>
<Field
className="form-control m-1"
type="text"
key={`contractArgValue${index}`}
name={`contractArgValue${index}`}
placeholder={item.type}
/>
</CustomTooltip> </CustomTooltip>
</div> </div>
)} )
)} })}
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="contractAddress">Contract Address</label> <label htmlFor="contractAddress">Contract Address</label>
<Field <Field
className={ className={errors.contractAddress && touched.contractAddress ? 'form-control is-invalid' : 'form-control'}
errors.contractAddress && touched.contractAddress
? "form-control is-invalid"
: "form-control"
}
type="text" type="text"
name="contractAddress" name="contractAddress"
placeholder="e.g. 0x11b79afc03baf25c631dd70169bb6a3160b2706e" placeholder="e.g. 0x11b79afc03baf25c631dd70169bb6a3160b2706e"
/> />
<ErrorMessage <ErrorMessage className="invalid-feedback" name="contractAddress" component="div" />
className="invalid-feedback"
name="contractAddress"
component="div"
/>
<div className="d-flex mb-2 custom-control custom-checkbox"> <div className="d-flex mb-2 custom-control custom-checkbox">
<Field <Field
className="custom-control-input" className="custom-control-input"
@ -235,43 +185,40 @@ export const VerifyView: React.FC<Props> = ({
else setIsProxyContract(false) else setIsProxyContract(false)
}} }}
/> />
<label className="form-check-label custom-control-label" htmlFor="isProxy">It's a proxy contract address</label> <label className="form-check-label custom-control-label" htmlFor="isProxy">
It's a proxy contract address
</label>
</div> </div>
</div> </div>
<div className={isProxyContract ? 'form-group d-block' : 'form-group d-none'}> <div className={isProxyContract ? 'form-group d-block' : 'form-group d-none'}>
<label htmlFor="expectedImplAddress">Expected Implementation Address</label> <label htmlFor="expectedImplAddress">Expected Implementation Address</label>
<CustomTooltip <CustomTooltip
tooltipText='Providing expected implementation address enforces a check to ensure the returned implementation contract address is same as address picked up by the verifier' tooltipText="Providing expected implementation address enforces a check to ensure the returned implementation contract address is same as address picked up by the verifier"
tooltipId='etherscan-impl-address' tooltipId="etherscan-impl-address"
placement='bottom' placement="bottom"
> >
<Field <Field className="form-control" type="text" name="expectedImplAddress" placeholder="verified implementation contract address" />
className="form-control"
type="text"
name="expectedImplAddress"
placeholder="verified implementation contract address"
/>
</CustomTooltip> </CustomTooltip>
<i style={{fontSize: 'x-small'}} className={'ml-1 fal fa-info-circle align-self-center'} aria-hidden="true"></i> <i style={{fontSize: 'x-small'}} className={'ml-1 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
<label> &nbsp;Make sure contract is already verified on Etherscan</label> <label> &nbsp;Make sure contract is already verified on Etherscan</label>
</div> </div>
<SubmitButton dataId="verify-contract" text="Verify" <SubmitButton
dataId="verify-contract"
text="Verify"
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
disable={ !contracts.length || disable={
!contracts.length ||
!touched.contractName || !touched.contractName ||
!touched.contractAddress || !touched.contractAddress ||
(touched.contractName && errors.contractName) || (touched.contractName && errors.contractName) ||
(touched.contractAddress && errors.contractAddress) || (touched.contractAddress && errors.contractAddress) ||
(networkName === 'VM (Not supported)') networkName === 'VM (Not supported)'
? true ? true
: false} : false
}
/> />
<br /> <br />
<CustomTooltip <CustomTooltip tooltipText="Generate the required TS scripts to verify a contract on Etherscan" tooltipId="etherscan-generate-scripts" placement="bottom">
tooltipText='Generate the required TS scripts to verify a contract on Etherscan'
tooltipId='etherscan-generate-scripts'
placement='bottom'
>
<button <button
type="button" type="button"
className="mr-2 mb-2 py-1 px-2 btn btn-secondary btn-block" className="mr-2 mb-2 py-1 px-2 btn btn-secondary btn-block"
@ -282,13 +229,14 @@ export const VerifyView: React.FC<Props> = ({
Generate Verification Scripts Generate Verification Scripts
</button> </button>
</CustomTooltip> </CustomTooltip>
</form>) </form>
)
}} }}
</Formik> </Formik>
<div <div
data-id="verify-result" data-id="verify-result"
className={verificationResult.current['succeed'] ? "text-success mt-4 text-center" : "text-danger mt-4 text-center"} className={verificationResult.current['succeed'] ? 'text-success mt-4 text-center' : 'text-danger mt-4 text-center'}
style={{fontSize: "0.8em"}} style={{fontSize: '0.8em'}}
dangerouslySetInnerHTML={{__html: results}} dangerouslySetInnerHTML={{__html: results}}
/> />
{/* <div style={{ display: "block", textAlign: "center", marginTop: "1em" }}> {/* <div style={{ display: "block", textAlign: "center", marginTop: "1em" }}>

@ -1,7 +1,11 @@
import { StrictMode } from 'react'; import {StrictMode} from 'react'
import * as ReactDOM from 'react-dom'; import * as ReactDOM from 'react-dom'
import App from './app/app'
import App from './app/app'; ReactDOM.render(
<StrictMode>
ReactDOM.render(<StrictMode><App /></StrictMode>, document.getElementById('root')); <App />
</StrictMode>,
document.getElementById('root')
)

@ -1,7 +1,7 @@
const {composePlugins, withNx} = require('@nrwl/webpack') const {composePlugins, withNx} = require('@nrwl/webpack')
const webpack = require('webpack') const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const versionData = { const versionData = {
timestamp: Date.now(), timestamp: Date.now(),
@ -15,29 +15,29 @@ module.exports = composePlugins(withNx(), (config) => {
// add fallback for node modules // add fallback for node modules
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"), crypto: require.resolve('crypto-browserify'),
"stream": require.resolve("stream-browserify"), stream: require.resolve('stream-browserify'),
"path": require.resolve("path-browserify"), path: require.resolve('path-browserify'),
"http": require.resolve("stream-http"), http: require.resolve('stream-http'),
"https": require.resolve("https-browserify"), https: require.resolve('https-browserify'),
"constants": require.resolve("constants-browserify"), constants: require.resolve('constants-browserify'),
"os": false, //require.resolve("os-browserify/browser"), os: false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"), timers: false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"), zlib: require.resolve('browserify-zlib'),
"fs": false, fs: false,
"module": false, module: false,
"tls": false, tls: false,
"net": false, net: false,
"readline": false, readline: false,
"child_process": false, child_process: false,
"buffer": require.resolve("buffer/"), buffer: require.resolve('buffer/'),
"vm": require.resolve('vm-browserify'), vm: require.resolve('vm-browserify')
} }
// add externals // add externals
config.externals = { config.externals = {
...config.externals, ...config.externals,
solc: 'solc', solc: 'solc'
} }
// add public path // add public path
@ -47,26 +47,24 @@ module.exports = composePlugins(withNx(), (config) => {
config.output.filename = `[name].plugin-etherscan.${versionData.timestamp}.js` config.output.filename = `[name].plugin-etherscan.${versionData.timestamp}.js`
config.output.chunkFilename = `[name].plugin-etherscan.${versionData.timestamp}.js` config.output.chunkFilename = `[name].plugin-etherscan.${versionData.timestamp}.js`
// add copy & provide plugin // add copy & provide plugin
config.plugins.push( config.plugins.push(
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}) })
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
// set minimizer // set minimizer
config.optimization.minimizer = [ config.optimization.minimizer = [
new TerserPlugin({ new TerserPlugin({
@ -76,17 +74,17 @@ module.exports = composePlugins(withNx(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
config.watchOptions = { config.watchOptions = {
ignored: /node_modules/ ignored: /node_modules/
} }
return config; return config
}); })

@ -7,7 +7,7 @@ module.exports = {
globals_path: '', globals_path: '',
test_settings: { test_settings: {
default: { 'default': {
selenium_port: 4444, selenium_port: 4444,
selenium_host: 'localhost', selenium_host: 'localhost',
globals: { globals: {
@ -23,28 +23,29 @@ module.exports = {
exclude: ['dist/apps/remix-ide-e2e/src/tests/runAndDeploy.test.js', 'dist/apps/remix-ide-e2e/src/tests/pluginManager.test.ts'] exclude: ['dist/apps/remix-ide-e2e/src/tests/runAndDeploy.test.js', 'dist/apps/remix-ide-e2e/src/tests/pluginManager.test.ts']
}, },
chrome: { 'chrome': {
desiredCapabilities: { desiredCapabilities: {
browserName: 'chrome', 'browserName': 'chrome',
javascriptEnabled: true, 'javascriptEnabled': true,
acceptSslCerts: true, 'acceptSslCerts': true,
'goog:chromeOptions': { 'goog:chromeOptions': {
args: ['window-size=2560,1440', args: [
'window-size=2560,1440',
'start-fullscreen', 'start-fullscreen',
'--no-sandbox', '--no-sandbox',
'--headless', '--headless',
'--verbose', '--verbose',
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36", '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
] ]
} }
} }
}, },
chromeDesktop: { 'chromeDesktop': {
desiredCapabilities: { desiredCapabilities: {
browserName: 'chrome', 'browserName': 'chrome',
javascriptEnabled: true, 'javascriptEnabled': true,
acceptSslCerts: true, 'acceptSslCerts': true,
'goog:chromeOptions': { 'goog:chromeOptions': {
args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox'] args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox']
} }
@ -53,40 +54,33 @@ module.exports = {
'chrome-runAndDeploy': { 'chrome-runAndDeploy': {
desiredCapabilities: { desiredCapabilities: {
browserName: 'chrome', 'browserName': 'chrome',
javascriptEnabled: true, 'javascriptEnabled': true,
acceptSslCerts: true, 'acceptSslCerts': true,
'goog:chromeOptions': { 'goog:chromeOptions': {
args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox', '--headless', '--verbose'] args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox', '--headless', '--verbose']
} }
} }
}, },
firefoxDesktop: { 'firefoxDesktop': {
desiredCapabilities: { desiredCapabilities: {
browserName: 'firefox', 'browserName': 'firefox',
javascriptEnabled: true, 'javascriptEnabled': true,
acceptSslCerts: true, 'acceptSslCerts': true,
'moz:firefoxOptions': { 'moz:firefoxOptions': {
args: [ args: ['-width=2560', '-height=1440']
'-width=2560',
'-height=1440'
]
} }
} }
}, },
firefox: { 'firefox': {
desiredCapabilities: { desiredCapabilities: {
browserName: 'firefox', 'browserName': 'firefox',
javascriptEnabled: true, 'javascriptEnabled': true,
acceptSslCerts: true, 'acceptSslCerts': true,
'moz:firefoxOptions': { 'moz:firefoxOptions': {
args: [ args: ['-headless', '-width=2560', '-height=1440']
'-headless',
'-width=2560',
'-height=1440'
]
} }
} }
} }

@ -1,4 +1,3 @@
import React, {useEffect, useState} from 'react' import React, {useEffect, useState} from 'react'
import {RemixPlugin} from './Client' import {RemixPlugin} from './Client'
import {Logger} from './logger' import {Logger} from './logger'
@ -24,7 +23,19 @@ function App () {
const [log, setLog] = useState<any>() const [log, setLog] = useState<any>()
const [started, setStarted] = useState<boolean>(false) const [started, setStarted] = useState<boolean>(false)
const [events, setEvents] = useState<any>() const [events, setEvents] = useState<any>()
const [profiles, setProfiles] = useState<Profile[]>([pluginManagerProfile, filePanelProfile, filSystemProfile, dGitProfile, networkProfile, settingsProfile, editorProfile, compilerProfile, udappProfile, contentImportProfile, windowProfile]) const [profiles, setProfiles] = useState<Profile[]>([
pluginManagerProfile,
filePanelProfile,
filSystemProfile,
dGitProfile,
networkProfile,
settingsProfile,
editorProfile,
compilerProfile,
udappProfile,
contentImportProfile,
windowProfile
])
const handleChange = ({target}: any) => { const handleChange = ({target}: any) => {
setPayload(target.value) setPayload(target.value)
@ -44,7 +55,7 @@ function App () {
const p = await client.call('manager', 'getProfile', name) const p = await client.call('manager', 'getProfile', name)
addProfiles = [...addProfiles, p] addProfiles = [...addProfiles, p]
} }
setProfiles(profiles => [...profiles, ...addProfiles]) setProfiles((profiles) => [...profiles, ...addProfiles])
profiles.map((profile: Profile) => { profiles.map((profile: Profile) => {
if (profile.events) { if (profile.events) {
@ -77,7 +88,9 @@ function App () {
let ob: any = null let ob: any = null
try { try {
ob = JSON.parse(payload) ob = JSON.parse(payload)
if (ob && !Array.isArray(ob)) { ob = [ob] } if (ob && !Array.isArray(ob)) {
ob = [ob]
}
} catch (e) {} } catch (e) {}
const args = ob || [payload] const args = ob || [payload]
setStarted(true) setStarted(true)
@ -97,30 +110,41 @@ function App () {
return ( return (
<div className="App container-fluid"> <div className="App container-fluid">
<h5>PLUGIN API TESTER</h5> <h5>PLUGIN API TESTER</h5>
<label id='callStatus'>{started ? <>start</> : <>stop</> }</label><br></br> <label id="callStatus">{started ? <>start</> : <>stop</>}</label>
<br></br>
<label>method results</label> <label>method results</label>
<Logger id='methods' log={log}></Logger> <Logger id="methods" log={log}></Logger>
<label>events</label> <label>events</label>
<Logger id='events' log={events}></Logger> <Logger id="events" log={events}></Logger>
<input <input className="form-control w-100" type="text" id="payload" placeholder="Enter payload here..." value={payload} onChange={handleChange} data-id="payload-input" />
className='form-control w-100'
type="text"
id="payload"
placeholder="Enter payload here..."
value={payload}
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) => {
return <button data-id={`${profile.name}:${method}`} key={method} className='btn btn-primary btn-sm ml-1 mb-1' onClick={async () => await clientMethod(profile, method)}>{method}</button> return (
<button data-id={`${profile.name}:${method}`} key={method} className="btn btn-primary btn-sm ml-1 mb-1" onClick={async () => await clientMethod(profile, method)}>
{method}
</button>
)
}) })
const events = profile.events ? profile.events.map((event: string) => { const events = profile.events
return <label key={event} className='m-1'>{event}</label> ? profile.events.map((event: string) => {
}) : null return (
return <div key={profile.name} className='small border-bottom'><label className='text-uppercase'>{profile.name}</label><br></br>{methods}<br></br>{events ? <label>EVENTS:</label> : null}{events}</div> <label key={event} className="m-1">
{event}
</label>
)
})
: null
return (
<div key={profile.name} className="small border-bottom">
<label className="text-uppercase">{profile.name}</label>
<br></br>
{methods}
<br></br>
{events ? <label>EVENTS:</label> : null}
{events}
</div>
)
})} })}
</div> </div>
) )
} }

@ -1,9 +1,13 @@
import React from 'react' import React from 'react'
interface loggerProps { interface loggerProps {
log: any, log: any
id: string id: string
} }
export const Logger: React.FC<loggerProps> = (props) => { export const Logger: React.FC<loggerProps> = (props) => {
return (<div id={props.id} className="jumbotron overflow-auto text-break mb-1 p-2">{props.log}</div>) return (
<div id={props.id} className="jumbotron overflow-auto text-break mb-1 p-2">
{props.log}
</div>
)
} }

@ -4,7 +4,7 @@
chrome.browserAction.onClicked.addListener(function (tab) { chrome.browserAction.onClicked.addListener(function (tab) {
chrome.storage.sync.set({'chrome-app-sync': true}) chrome.storage.sync.set({'chrome-app-sync': true})
chrome.tabs.create({ 'url': chrome.extension.getURL('index.html') }, function (tab) { chrome.tabs.create({url: chrome.extension.getURL('index.html')}, function (tab) {
// tab opened // tab opened
}) })
}) })

@ -38,13 +38,15 @@ export class HiddenPanel extends AbstractPanel {
render() { render() {
return ( return (
<div className='pluginsContainer'><PluginViewWrapper plugin={this} /></div> <div className="pluginsContainer">
); <PluginViewWrapper plugin={this} />
</div>
)
} }
renderComponent() { renderComponent() {
this.dispatch({ this.dispatch({
plugins: this.plugins, plugins: this.plugins
}) })
} }
} }

@ -59,7 +59,11 @@ export class MainPanel extends AbstractPanel {
} }
render() { render() {
return <div style={{height: '100%', width: '100%'}} data-id='mainPanelPluginsContainer'><PluginViewWrapper plugin={this} /></div> return (
<div style={{height: '100%', width: '100%'}} data-id="mainPanelPluginsContainer">
<PluginViewWrapper plugin={this} />
</div>
)
} }
updateComponent(state: any) { updateComponent(state: any) {

@ -7,10 +7,9 @@ import { indexedDBFileSystem } from '../files/filesystems/indexedDB'
import {localStorageFS} from '../files/filesystems/localStorage' import {localStorageFS} from '../files/filesystems/localStorage'
import {fileSystemUtility, migrationTestData} from '../files/filesystems/fileSystemUtility' import {fileSystemUtility, migrationTestData} from '../files/filesystems/fileSystemUtility'
import './styles/preload.css' import './styles/preload.css'
const _paq = window._paq = window._paq || [] const _paq = (window._paq = window._paq || [])
export const Preload = () => { export const Preload = () => {
const [supported, setSupported] = useState<boolean>(true) const [supported, setSupported] = useState<boolean>(true)
const [error, setError] = useState<boolean>(false) const [error, setError] = useState<boolean>(false)
const [showDownloader, setShowDownloader] = useState<boolean>(false) const [showDownloader, setShowDownloader] = useState<boolean>(false)
@ -18,12 +17,19 @@ export const Preload = () => {
const remixIndexedDB = useRef<fileSystem>(new indexedDBFileSystem()) const remixIndexedDB = useRef<fileSystem>(new indexedDBFileSystem())
const localStorageFileSystem = useRef<fileSystem>(new localStorageFS()) const localStorageFileSystem = useRef<fileSystem>(new localStorageFS())
// url parameters to e2e test the fallbacks and error warnings // url parameters to e2e test the fallbacks and error warnings
const testmigrationFallback = useRef<boolean>(window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') const testmigrationFallback = useRef<boolean>(
const testmigrationResult = useRef<boolean>(window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
const testBlockStorage = useRef<boolean>(window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') )
const testmigrationResult = useRef<boolean>(
window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
)
const testBlockStorage = useRef<boolean>(
window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
)
function loadAppComponent() { function loadAppComponent() {
import('../../app').then((AppComponent) => { import('../../app')
.then((AppComponent) => {
const appComponent = new AppComponent.default() const appComponent = new AppComponent.default()
appComponent.run().then(() => { appComponent.run().then(() => {
render( render(
@ -33,7 +39,8 @@ export const Preload = () => {
document.getElementById('root') document.getElementById('root')
) )
}) })
}).catch(err => { })
.catch((err) => {
_paq.push(['trackEvent', 'Preload', 'error', err && err.message]) _paq.push(['trackEvent', 'Preload', 'error', err && err.message])
console.error('Error loading Remix:', err) console.error('Error loading Remix:', err)
setError(true) setError(true)
@ -56,7 +63,10 @@ export const Preload = () => {
} }
const setFileSystems = async () => { const setFileSystems = async () => {
const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback.current || testBlockStorage.current)? null: remixIndexedDB.current, testBlockStorage.current? null:localStorageFileSystem.current]) const fsLoaded = await remixFileSystems.current.setFileSystem([
testmigrationFallback.current || testBlockStorage.current ? null : remixIndexedDB.current,
testBlockStorage.current ? null : localStorageFileSystem.current
])
if (fsLoaded) { if (fsLoaded) {
console.log(fsLoaded.name + ' activated') console.log(fsLoaded.name + ' activated')
_paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name]) _paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name])
@ -76,69 +86,89 @@ export const Preload = () => {
useEffect(() => { useEffect(() => {
async function loadStorage() { async function loadStorage() {
await remixFileSystems.current.addFileSystem(remixIndexedDB.current) || _paq.push(['trackEvent', 'Storage', 'error', 'indexedDB not supported']) ;(await remixFileSystems.current.addFileSystem(remixIndexedDB.current)) || _paq.push(['trackEvent', 'Storage', 'error', 'indexedDB not supported'])
await remixFileSystems.current.addFileSystem(localStorageFileSystem.current) || _paq.push(['trackEvent', 'Storage', 'error', 'localstorage not supported']) ;(await remixFileSystems.current.addFileSystem(localStorageFileSystem.current)) || _paq.push(['trackEvent', 'Storage', 'error', 'localstorage not supported'])
await testmigration() await testmigration()
remixIndexedDB.current.loaded && await remixIndexedDB.current.checkWorkspaces() remixIndexedDB.current.loaded && (await remixIndexedDB.current.checkWorkspaces())
localStorageFileSystem.current.loaded && await localStorageFileSystem.current.checkWorkspaces() localStorageFileSystem.current.loaded && (await localStorageFileSystem.current.checkWorkspaces())
remixIndexedDB.current.loaded && ( (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces)? await setFileSystems():setShowDownloader(true)) remixIndexedDB.current.loaded && (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces ? await setFileSystems() : setShowDownloader(true))
!remixIndexedDB.current.loaded && await setFileSystems() !remixIndexedDB.current.loaded && (await setFileSystems())
} }
loadStorage() loadStorage()
}, []) }, [])
return <> return (
<div className='preload-container'> <>
<div className='preload-logo pb-4'> <div className="preload-container">
<div className="preload-logo pb-4">
{logo} {logo}
<div className="info-secondary splash"> <div className="info-secondary splash">
REMIX IDE REMIX IDE
<br /> <br />
<span className='version'> v{packageJson.version}</span> <span className="version"> v{packageJson.version}</span>
</div>
</div> </div>
{!supported ? (
<div className="preload-info-container alert alert-warning">
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser.
</div> </div>
{!supported ? ) : null}
<div className='preload-info-container alert alert-warning'> {error ? (
Your browser does not support any of the filesystems required by Remix. <div className="preload-info-container alert alert-danger text-left">
Either change the settings in your browser or use a supported browser. An unknown error has occurred while loading the application.
</div> : null} <br></br>
{error ?
<div className='preload-info-container alert alert-danger text-left'>
An unknown error has occurred while loading the application.<br></br>
Doing a hard refresh might fix this issue:<br></br> Doing a hard refresh might fix this issue:<br></br>
<div className='pt-2'> <div className="pt-2">
Windows:<br></br> Windows:<br></br>- Chrome: CTRL + F5 or CTRL + Reload Button
- Chrome: CTRL + F5 or CTRL + Reload Button<br></br> <br></br>- Firefox: CTRL + SHIFT + R or CTRL + F5<br></br>
- Firefox: CTRL + SHIFT + R or CTRL + F5<br></br> </div>
<div className="pt-2">
MacOS:<br></br>- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
</div> </div>
<div className='pt-2'> <div className="pt-2">
MacOS:<br></br> Linux:<br></br>- Chrome & FireFox: CTRL + SHIFT + R<br></br>
- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
</div> </div>
<div className='pt-2'>
Linux:<br></br>
- Chrome & FireFox: CTRL + SHIFT + R<br></br>
</div> </div>
</div> : null} ) : null}
{showDownloader ? {showDownloader ? (
<div className='preload-info-container alert alert-info'> <div className="preload-info-container alert alert-info">
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work. This app will be updated now. Please download a backup of your files now to make sure you don't lose your work.
<br></br> <br></br>
You don't need to do anything else, your files will be available when the app loads. You don't need to do anything else, your files will be available when the app loads.
<div onClick={async () => { await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup</div> <div
<div onClick={async () => { await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup</div> onClick={async () => {
</div> : null} await downloadBackup()
{(supported && !error && !showDownloader) ? }}
data-id="downloadbackup-btn"
className="btn btn-primary mt-1"
>
download backup
</div>
<div
onClick={async () => {
await migrateAndLoad()
}}
data-id="skipbackup-btn"
className="btn btn-primary mt-1"
>
skip backup
</div>
</div>
) : null}
{supported && !error && !showDownloader ? (
<div> <div>
<i className="fas fa-spinner fa-spin fa-2x"></i> <i className="fas fa-spinner fa-spin fa-2x"></i>
</div> : null} </div>
) : null}
</div> </div>
</> </>
)
} }
const logo = (
const logo = <svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100"> <svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100">
<path d="M91.84,35a.09.09,0,0,1-.1-.07,41,41,0,0,0-79.48,0,.09.09,0,0,1-.1.07C9.45,35,1,35.35,1,42.53c0,8.56,1,16,6,20.32,2.16,1.85,5.81,2.3,9.27,2.22a44.4,44.4,0,0,0,6.45-.68.09.09,0,0,0,.06-.15A34.81,34.81,0,0,1,17,45c0-.1,0-.21,0-.31a35,35,0,0,1,70,0c0,.1,0,.21,0,.31a34.81,34.81,0,0,1-5.78,19.24.09.09,0,0,0,.06.15,44.4,44.4,0,0,0,6.45.68c3.46.08,7.11-.37,9.27-2.22,5-4.27,6-11.76,6-20.32C103,35.35,94.55,35,91.84,35Z" /> <path d="M91.84,35a.09.09,0,0,1-.1-.07,41,41,0,0,0-79.48,0,.09.09,0,0,1-.1.07C9.45,35,1,35.35,1,42.53c0,8.56,1,16,6,20.32,2.16,1.85,5.81,2.3,9.27,2.22a44.4,44.4,0,0,0,6.45-.68.09.09,0,0,0,.06-.15A34.81,34.81,0,0,1,17,45c0-.1,0-.21,0-.31a35,35,0,0,1,70,0c0,.1,0,.21,0,.31a34.81,34.81,0,0,1-5.78,19.24.09.09,0,0,0,.06.15,44.4,44.4,0,0,0,6.45.68c3.46.08,7.11-.37,9.27-2.22,5-4.27,6-11.76,6-20.32C103,35.35,94.55,35,91.84,35Z" />
<path d="M52,74,25.4,65.13a.1.1,0,0,0-.1.17L51.93,91.93a.1.1,0,0,0,.14,0L78.7,65.3a.1.1,0,0,0-.1-.17L52,74A.06.06,0,0,1,52,74Z" /> <path d="M52,74,25.4,65.13a.1.1,0,0,0-.1.17L51.93,91.93a.1.1,0,0,0,.14,0L78.7,65.3a.1.1,0,0,0-.1-.17L52,74A.06.06,0,0,1,52,74Z" />
<path d="M75.68,46.9,82,45a.09.09,0,0,0,.08-.09,29.91,29.91,0,0,0-.87-6.94.11.11,0,0,0-.09-.08l-6.43-.58a.1.1,0,0,1-.06-.18l4.78-4.18a.13.13,0,0,0,0-.12,30.19,30.19,0,0,0-3.65-6.07.09.09,0,0,0-.11,0l-5.91,2a.1.1,0,0,1-.12-.14L72.19,23a.11.11,0,0,0,0-.12,29.86,29.86,0,0,0-5.84-4.13.09.09,0,0,0-.11,0l-4.47,4.13a.1.1,0,0,1-.17-.07l.09-6a.1.1,0,0,0-.07-.1,30.54,30.54,0,0,0-7-1.47.1.1,0,0,0-.1.07l-2.38,5.54a.1.1,0,0,1-.18,0l-2.37-5.54a.11.11,0,0,0-.11-.06,30,30,0,0,0-7,1.48.12.12,0,0,0-.07.1l.08,6.05a.09.09,0,0,1-.16.07L37.8,18.76a.11.11,0,0,0-.12,0,29.75,29.75,0,0,0-5.83,4.13.11.11,0,0,0,0,.12l2.59,5.6a.11.11,0,0,1-.13.14l-5.9-2a.11.11,0,0,0-.12,0,30.23,30.23,0,0,0-3.62,6.08.11.11,0,0,0,0,.12l4.79,4.19a.1.1,0,0,1-.06.17L23,37.91a.1.1,0,0,0-.09.07A29.9,29.9,0,0,0,22,44.92a.1.1,0,0,0,.07.1L28.4,47a.1.1,0,0,1,0,.18l-5.84,3.26a.16.16,0,0,0,0,.11,30.17,30.17,0,0,0,2.1,6.76c.32.71.67,1.4,1,2.08a.1.1,0,0,0,.06,0L52,68.16H52l26.34-8.78a.1.1,0,0,0,.06-.05,30.48,30.48,0,0,0,3.11-8.88.1.1,0,0,0-.05-.11l-5.83-3.26A.1.1,0,0,1,75.68,46.9Z" /> <path d="M75.68,46.9,82,45a.09.09,0,0,0,.08-.09,29.91,29.91,0,0,0-.87-6.94.11.11,0,0,0-.09-.08l-6.43-.58a.1.1,0,0,1-.06-.18l4.78-4.18a.13.13,0,0,0,0-.12,30.19,30.19,0,0,0-3.65-6.07.09.09,0,0,0-.11,0l-5.91,2a.1.1,0,0,1-.12-.14L72.19,23a.11.11,0,0,0,0-.12,29.86,29.86,0,0,0-5.84-4.13.09.09,0,0,0-.11,0l-4.47,4.13a.1.1,0,0,1-.17-.07l.09-6a.1.1,0,0,0-.07-.1,30.54,30.54,0,0,0-7-1.47.1.1,0,0,0-.1.07l-2.38,5.54a.1.1,0,0,1-.18,0l-2.37-5.54a.11.11,0,0,0-.11-.06,30,30,0,0,0-7,1.48.12.12,0,0,0-.07.1l.08,6.05a.09.09,0,0,1-.16.07L37.8,18.76a.11.11,0,0,0-.12,0,29.75,29.75,0,0,0-5.83,4.13.11.11,0,0,0,0,.12l2.59,5.6a.11.11,0,0,1-.13.14l-5.9-2a.11.11,0,0,0-.12,0,30.23,30.23,0,0,0-3.62,6.08.11.11,0,0,0,0,.12l4.79,4.19a.1.1,0,0,1-.06.17L23,37.91a.1.1,0,0,0-.09.07A29.9,29.9,0,0,0,22,44.92a.1.1,0,0,0,.07.1L28.4,47a.1.1,0,0,1,0,.18l-5.84,3.26a.16.16,0,0,0,0,.11,30.17,30.17,0,0,0,2.1,6.76c.32.71.67,1.4,1,2.08a.1.1,0,0,0,.06,0L52,68.16H52l26.34-8.78a.1.1,0,0,0,.06-.05,30.48,30.48,0,0,0,3.11-8.88.1.1,0,0,0-.05-.11l-5.83-3.26A.1.1,0,0,1,75.68,46.9Z" />
</svg> </svg>
)

@ -85,8 +85,11 @@ export class SidePanel extends AbstractPanel {
render() { render() {
return ( return (
<section className='panel plugin-manager'> <PluginViewWrapper plugin={this} /></section> <section className="panel plugin-manager">
); {' '}
<PluginViewWrapper plugin={this} />
</section>
)
} }
updateComponent(state: any) { updateComponent(state: any) {

@ -31,27 +31,34 @@ export class VerticalIcons extends Plugin {
renderComponent() { renderComponent() {
const fixedOrder = ['filePanel', 'search', 'solidity', 'udapp', 'debugger', 'solidityStaticAnalysis', 'solidityUnitTesting', 'pluginManager'] const fixedOrder = ['filePanel', 'search', 'solidity', 'udapp', 'debugger', 'solidityStaticAnalysis', 'solidityUnitTesting', 'pluginManager']
const divived = Object.values(this.icons).map((value) => { return { const divived = Object.values(this.icons)
.map((value) => {
return {
...value, ...value,
isRequired: fixedOrder.indexOf(value.profile.name) > -1 isRequired: fixedOrder.indexOf(value.profile.name) > -1
}}).sort((a,b) => { }
})
.sort((a, b) => {
return a.timestamp - b.timestamp return a.timestamp - b.timestamp
}) })
const required = divived.filter((value) => value.isRequired).sort((a,b) => { const required = divived
.filter((value) => value.isRequired)
.sort((a, b) => {
return fixedOrder.indexOf(a.profile.name) - fixedOrder.indexOf(b.profile.name) return fixedOrder.indexOf(a.profile.name) - fixedOrder.indexOf(b.profile.name)
}) })
const sorted: IconRecord[] = [ const sorted: IconRecord[] = [
...required, ...required,
...divived.filter((value) => { return !value.isRequired }) ...divived.filter((value) => {
return !value.isRequired
})
] ]
this.dispatch({ this.dispatch({
verticalIconsPlugin: this, verticalIconsPlugin: this,
icons: sorted icons: sorted
}) })
} }
setDispatch(dispatch: React.Dispatch<any>) { setDispatch(dispatch: React.Dispatch<any>) {
@ -112,15 +119,14 @@ export class VerticalIcons extends Plugin {
} }
updateComponent(state: any) { updateComponent(state: any) {
return <RemixUiVerticalIconsPanel return <RemixUiVerticalIconsPanel verticalIconsPlugin={state.verticalIconsPlugin} icons={state.icons} />
verticalIconsPlugin={state.verticalIconsPlugin}
icons={state.icons}
/>
} }
render() { render() {
return ( return (
<div id='icon-panel'><PluginViewWrapper plugin={this} /></div> <div id="icon-panel">
); <PluginViewWrapper plugin={this} />
</div>
)
} }
} }

@ -3,7 +3,7 @@ import { Plugin } from '@remixproject/engine'
import {customAction} from '@remixproject/plugin-api' import {customAction} from '@remixproject/plugin-api'
import {concatSourceFiles, getDependencyGraph, normalizeContractPath} from '@remix-ui/solidity-compiler' import {concatSourceFiles, getDependencyGraph, normalizeContractPath} from '@remix-ui/solidity-compiler'
const _paq = window._paq = window._paq || [] const _paq = (window._paq = window._paq || [])
const profile = { const profile = {
name: 'contractflattener', name: 'contractflattener',
@ -11,11 +11,10 @@ const profile = {
description: 'Flatten solidity contracts', description: 'Flatten solidity contracts',
methods: ['flattenAContract', 'flattenContract'], methods: ['flattenAContract', 'flattenContract'],
events: [], events: [],
maintainedBy: 'Remix', maintainedBy: 'Remix'
} }
export class ContractFlattener extends Plugin { export class ContractFlattener extends Plugin {
triggerFlattenContract: boolean = false triggerFlattenContract: boolean = false
constructor() { constructor() {
super(profile) super(profile)
@ -48,8 +47,7 @@ export class ContractFlattener extends Plugin {
* Takes the flattened result, writes it to a file and returns the result. * Takes the flattened result, writes it to a file and returns the result.
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
async flattenContract (source: { sources: any, target: string }, async flattenContract(source: {sources: any; target: string}, filePath: string, data: {contracts: any; sources: any}): Promise<string> {
filePath: string, data: { contracts: any, sources: any }): Promise<string> {
const appendage = '_flattened.sol' const appendage = '_flattened.sol'
const normalized = normalizeContractPath(filePath) const normalized = normalizeContractPath(filePath)
const path = `${normalized[normalized.length - 2]}${appendage}` const path = `${normalized[normalized.length - 2]}${appendage}`
@ -60,9 +58,7 @@ export class ContractFlattener extends Plugin {
let sources let sources
try { try {
dependencyGraph = getDependencyGraph(ast, filePath) dependencyGraph = getDependencyGraph(ast, filePath)
sorted = dependencyGraph.isEmpty() sorted = dependencyGraph.isEmpty() ? [filePath] : dependencyGraph.sort().reverse()
? [filePath]
: dependencyGraph.sort().reverse()
sources = source.sources sources = source.sources
result = concatSourceFiles(sorted, sources) result = concatSourceFiles(sorted, sources)
} catch (err) { } catch (err) {

@ -5,7 +5,7 @@ import { AlertModal } from '@remix-ui/app'
import {dispatchModalInterface} from '@remix-ui/app' import {dispatchModalInterface} from '@remix-ui/app'
interface INotificationApi { interface INotificationApi {
events: StatusEvents, events: StatusEvents
methods: { methods: {
modal: (args: AppModal) => void modal: (args: AppModal) => void
alert: (args: AlertModal) => void alert: (args: AlertModal) => void

@ -9,29 +9,75 @@ import CodeParserAntlrService from './services/code-parser-antlr-service'
import CodeParserImports, {CodeParserImportsData} from './services/code-parser-imports' import CodeParserImports, {CodeParserImportsData} from './services/code-parser-imports'
import React from 'react' import React from 'react'
import {Profile} from '@remixproject/plugin-utils' import {Profile} from '@remixproject/plugin-utils'
import { ContractDefinitionAstNode, EventDefinitionAstNode, FunctionCallAstNode, FunctionDefinitionAstNode, IdentifierAstNode, ImportDirectiveAstNode, ModifierDefinitionAstNode, SourceUnitAstNode, StructDefinitionAstNode, VariableDeclarationAstNode } from '@remix-project/remix-analyzer' import {
ContractDefinitionAstNode,
EventDefinitionAstNode,
FunctionCallAstNode,
FunctionDefinitionAstNode,
IdentifierAstNode,
ImportDirectiveAstNode,
ModifierDefinitionAstNode,
SourceUnitAstNode,
StructDefinitionAstNode,
VariableDeclarationAstNode
} from '@remix-project/remix-analyzer'
import {lastCompilationResult, RemixApi} from '@remixproject/plugin-api' import {lastCompilationResult, RemixApi} from '@remixproject/plugin-api'
import {antlr} from './types' import {antlr} from './types'
import {ParseResult} from './types/antlr-types' import {ParseResult} from './types/antlr-types'
const profile: Profile = { const profile: Profile = {
name: 'codeParser', name: 'codeParser',
methods: ['nodesAtPosition', 'getContractNodes', 'getCurrentFileNodes', 'getLineColumnOfNode', 'getLineColumnOfPosition', 'getFunctionParamaters', 'getDeclaration', 'getFunctionReturnParameters', 'getVariableDeclaration', 'getNodeDocumentation', 'getNodeLink', 'listAstNodes', 'getANTLRBlockAtPosition', 'getLastNodeInLine', 'resolveImports', 'parseSolidity', 'getNodesWithScope', 'getNodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'getGasEstimates', 'getImports'], methods: [
'nodesAtPosition',
'getContractNodes',
'getCurrentFileNodes',
'getLineColumnOfNode',
'getLineColumnOfPosition',
'getFunctionParamaters',
'getDeclaration',
'getFunctionReturnParameters',
'getVariableDeclaration',
'getNodeDocumentation',
'getNodeLink',
'listAstNodes',
'getANTLRBlockAtPosition',
'getLastNodeInLine',
'resolveImports',
'parseSolidity',
'getNodesWithScope',
'getNodesWithName',
'getNodes',
'compile',
'getNodeById',
'getLastCompilationResult',
'positionOfDefinition',
'definitionAtPosition',
'jumpToDefinition',
'referrencesAtPosition',
'referencesOf',
'getActiveHighlights',
'gasEstimation',
'declarationOf',
'getGasEstimates',
'getImports'
],
events: [], events: [],
version: '0.0.1' version: '0.0.1'
} }
export function isNodeDefinition(node: genericASTNode) { export function isNodeDefinition(node: genericASTNode) {
return node.nodeType === 'ContractDefinition' || return (
node.nodeType === 'ContractDefinition' ||
node.nodeType === 'FunctionDefinition' || node.nodeType === 'FunctionDefinition' ||
node.nodeType === 'ModifierDefinition' || node.nodeType === 'ModifierDefinition' ||
node.nodeType === 'VariableDeclaration' || node.nodeType === 'VariableDeclaration' ||
node.nodeType === 'StructDefinition' || node.nodeType === 'StructDefinition' ||
node.nodeType === 'EventDefinition' node.nodeType === 'EventDefinition'
)
} }
export type genericASTNode = export type genericASTNode =
ContractDefinitionAstNode | ContractDefinitionAstNode
| FunctionDefinitionAstNode | FunctionDefinitionAstNode
| ModifierDefinitionAstNode | ModifierDefinitionAstNode
| VariableDeclarationAstNode | VariableDeclarationAstNode
@ -51,13 +97,12 @@ interface declarationIndexNode {
} }
interface codeParserIndex { interface codeParserIndex {
declarations: declarationIndexNode, declarations: declarationIndexNode
flatReferences: flatReferenceIndexNode, flatReferences: flatReferenceIndexNode
nodesPerFile: any nodesPerFile: any
} }
export class CodeParser extends Plugin { export class CodeParser extends Plugin {
compilerAbstract: CompilerAbstract compilerAbstract: CompilerAbstract
currentFile: string currentFile: string
nodeIndex: codeParserIndex nodeIndex: codeParserIndex
@ -80,7 +125,6 @@ export class CodeParser extends Plugin {
debuggerIsOn: boolean = false debuggerIsOn: boolean = false
constructor(astWalker: any) { constructor(astWalker: any) {
super(profile) super(profile)
this.astWalker = astWalker this.astWalker = astWalker
@ -106,7 +150,6 @@ export class CodeParser extends Plugin {
} }
async onActivation() { async onActivation() {
this.gasService = new CodeParserGasService(this) this.gasService = new CodeParserGasService(this)
this.compilerService = new CodeParserCompiler(this) this.compilerService = new CodeParserCompiler(this)
this.antlrService = new CodeParserAntlrService(this) this.antlrService = new CodeParserAntlrService(this)
@ -189,7 +232,7 @@ export class CodeParser extends Plugin {
} }
getSubNodes<T extends genericASTNode>(node: T): number[] { getSubNodes<T extends genericASTNode>(node: T): number[] {
return node.nodeType == "ContractDefinition" && node.contractDependencies; return node.nodeType == 'ContractDefinition' && node.contractDependencies
} }
/** /**
@ -200,7 +243,7 @@ export class CodeParser extends Plugin {
_buildIndex(compilationResult: CompilationResult, source) { _buildIndex(compilationResult: CompilationResult, source) {
if (compilationResult && compilationResult.sources) { if (compilationResult && compilationResult.sources) {
const callback = (node: genericASTNode) => { const callback = (node: genericASTNode) => {
if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { if (node && 'referencedDeclaration' in node && node.referencedDeclaration) {
if (!this.nodeIndex.declarations[node.referencedDeclaration]) { if (!this.nodeIndex.declarations[node.referencedDeclaration]) {
this.nodeIndex.declarations[node.referencedDeclaration] = [] this.nodeIndex.declarations[node.referencedDeclaration] = []
} }
@ -211,9 +254,7 @@ export class CodeParser extends Plugin {
for (const s in compilationResult.sources) { for (const s in compilationResult.sources) {
this.astWalker.walkFull(compilationResult.sources[s].ast, callback) this.astWalker.walkFull(compilationResult.sources[s].ast, callback)
} }
} }
} }
// NODE HELPERS // NODE HELPERS
@ -232,15 +273,13 @@ export class CodeParser extends Plugin {
return '(' + params.toString() + ')' return '(' + params.toString() + ')'
} }
_flatNodeList(contractNode: ContractDefinitionAstNode, fileName: string, inScope: boolean, compilatioResult: any) { _flatNodeList(contractNode: ContractDefinitionAstNode, fileName: string, inScope: boolean, compilatioResult: any) {
const index = {} const index = {}
const contractName: string = contractNode.name const contractName: string = contractNode.name
const callback = (node) => { const callback = (node) => {
if (inScope && node.scope !== contractNode.id if (inScope && node.scope !== contractNode.id && !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition'))
&& !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition'))
return return
if (inScope) node.isClassNode = true; if (inScope) node.isClassNode = true
node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult) node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult)
node.functionName = node.name + this._getInputParams(node) node.functionName = node.name + this._getInputParams(node)
node.contractName = contractName node.contractName = contractName
@ -261,7 +300,10 @@ export class CodeParser extends Plugin {
if (node.nodeType === 'ContractDefinition') { if (node.nodeType === 'ContractDefinition') {
const flatNodes = this._flatNodeList(node, fileName, false, compilationResult) const flatNodes = this._flatNodeList(node, fileName, false, compilationResult)
node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilationResult) node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilationResult)
nodesByContract.contracts[node.name] = { contractDefinition: node, contractNodes: flatNodes } nodesByContract.contracts[node.name] = {
contractDefinition: node,
contractNodes: flatNodes
}
const baseNodes = {} const baseNodes = {}
const baseNodesWithBaseContractScope = {} const baseNodesWithBaseContractScope = {}
if (node.linearizedBaseContracts) { if (node.linearizedBaseContracts) {
@ -271,19 +313,16 @@ export class CodeParser extends Plugin {
const callback = (node) => { const callback = (node) => {
node.contractName = (baseContract as any).name node.contractName = (baseContract as any).name
node.contractId = (baseContract as any).id node.contractId = (baseContract as any).id
node.isBaseNode = true; node.isBaseNode = true
baseNodes[node.id] = node baseNodes[node.id] = node
if ((node.scope && node.scope === baseContract.id) if ((node.scope && node.scope === baseContract.id) || node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition') {
|| node.nodeType === 'EnumDefinition'
|| node.nodeType === 'EventDefinition'
) {
baseNodesWithBaseContractScope[node.id] = node baseNodesWithBaseContractScope[node.id] = node
} }
if (node.members) { if (node.members) {
for (const member of node.members) { for (const member of node.members) {
member.contractName = (baseContract as any).name member.contractName = (baseContract as any).name
member.contractId = (baseContract as any).id member.contractId = (baseContract as any).id
member.isBaseNode = true; member.isBaseNode = true
} }
} }
} }
@ -296,10 +335,9 @@ export class CodeParser extends Plugin {
nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult) nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult)
} }
if (node.nodeType === 'ImportDirective') { if (node.nodeType === 'ImportDirective') {
const imported = await this.resolveImports(node, {}) const imported = await this.resolveImports(node, {})
for (const importedNode of (Object.values(imported) as any)) { for (const importedNode of Object.values(imported) as any) {
if (importedNode.nodes) if (importedNode.nodes)
for (const subNode of importedNode.nodes) { for (const subNode of importedNode.nodes) {
nodesByContract.imports[subNode.id] = subNode nodesByContract.imports[subNode.id] = subNode
@ -312,7 +350,6 @@ export class CodeParser extends Plugin {
} }
_getContractGasEstimate(node: ContractDefinitionAstNode | FunctionDefinitionAstNode, contractName: string, fileName: string, compilationResult: lastCompilationResult) { _getContractGasEstimate(node: ContractDefinitionAstNode | FunctionDefinitionAstNode, contractName: string, fileName: string, compilationResult: lastCompilationResult) {
const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile] const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile]
for (const name in contracts) { for (const name in contracts) {
if (name === contractName) { if (name === contractName) {
@ -335,7 +372,7 @@ export class CodeParser extends Plugin {
} else { } else {
return { return {
creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost, creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost,
codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost, codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost
} }
} }
} }
@ -357,8 +394,18 @@ export class CodeParser extends Plugin {
} }
if (!lastCompilationResult) return [] if (!lastCompilationResult) return []
const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile) const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile)
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data && lastCompilationResult.data.sources && lastCompilationResult.data.sources[this.currentFile]) { if (
const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(type, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file]) lastCompilationResult &&
lastCompilationResult.languageversion.indexOf('soljson') === 0 &&
lastCompilationResult.data &&
lastCompilationResult.data.sources &&
lastCompilationResult.data.sources[this.currentFile]
) {
const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(
type,
position,
lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file]
)
return nodes return nodes
} }
return [] return []
@ -417,7 +464,7 @@ export class CodeParser extends Plugin {
* @returns * @returns
*/ */
declarationOf<T extends genericASTNode>(node: T) { declarationOf<T extends genericASTNode>(node: T) {
if (node && ('referencedDeclaration' in node) && node.referencedDeclaration) { if (node && 'referencedDeclaration' in node && node.referencedDeclaration) {
return this.nodeIndex.flatReferences[node.referencedDeclaration] return this.nodeIndex.flatReferences[node.referencedDeclaration]
} }
return null return null
@ -436,7 +483,7 @@ export class CodeParser extends Plugin {
node = nodes[nodes.length - 1] node = nodes[nodes.length - 1]
nodeDefinition = node nodeDefinition = node
if (!isNodeDefinition(node)) { if (!isNodeDefinition(node)) {
nodeDefinition = await this.declarationOf(node) || node nodeDefinition = (await this.declarationOf(node)) || node
} }
if (node.nodeType === 'ImportDirective') { if (node.nodeType === 'ImportDirective') {
for (const key in this.nodeIndex.flatReferences) { for (const key in this.nodeIndex.flatReferences) {
@ -464,22 +511,22 @@ export class CodeParser extends Plugin {
return nodeDefinition return nodeDefinition
} }
} }
} }
async getContractNodes(contractName: string) { async getContractNodes(contractName: string) {
if (this.nodeIndex.nodesPerFile if (
&& this.nodeIndex.nodesPerFile[this.currentFile] this.nodeIndex.nodesPerFile &&
&& this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] this.nodeIndex.nodesPerFile[this.currentFile] &&
&& this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName].contractNodes) { this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] &&
this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName].contractNodes
) {
return this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] return this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName]
} }
return false return false
} }
async getCurrentFileNodes() { async getCurrentFileNodes() {
if (this.nodeIndex.nodesPerFile if (this.nodeIndex.nodesPerFile && this.nodeIndex.nodesPerFile[this.currentFile]) {
&& this.nodeIndex.nodesPerFile[this.currentFile]) {
return this.nodeIndex.nodesPerFile[this.currentFile] return this.nodeIndex.nodesPerFile[this.currentFile]
} }
return false return false
@ -535,8 +582,6 @@ export class CodeParser extends Plugin {
return imported return imported
} }
/** /**
* *
* @param node * @param node
@ -553,7 +598,7 @@ export class CodeParser extends Plugin {
} }
} }
} }
if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) { if (node && 'referencedDeclaration' in node && node.referencedDeclaration) {
highlights(node.referencedDeclaration) highlights(node.referencedDeclaration)
const current = this.nodeIndex.flatReferences[node.referencedDeclaration] const current = this.nodeIndex.flatReferences[node.referencedDeclaration]
results.push(current) results.push(current)
@ -586,8 +631,6 @@ export class CodeParser extends Plugin {
return this.nodeIndex.flatReferences return this.nodeIndex.flatReferences
} }
/** /**
* *
* @param node * @param node
@ -629,9 +672,9 @@ export class CodeParser extends Plugin {
* @returns * @returns
*/ */
async getNodeDocumentation(node: genericASTNode) { async getNodeDocumentation(node: genericASTNode) {
if (("documentation" in node) && node.documentation && (node.documentation as any).text) { if ('documentation' in node && node.documentation && (node.documentation as any).text) {
let text = ''; let text = ''
(node.documentation as any).text.split('\n').forEach(line => { ;(node.documentation as any).text.split('\n').forEach((line) => {
text += `${line.trim()}\n` text += `${line.trim()}\n`
}) })
return text return text
@ -651,11 +694,9 @@ export class CodeParser extends Plugin {
} else { } else {
if (node.typeName && node.typeName.name) { if (node.typeName && node.typeName.name) {
return `${node.typeName.name} ${nodeVisibility}${nodeName}` return `${node.typeName.name} ${nodeVisibility}${nodeName}`
} } else if (node.typeName && node.typeName.namePath) {
else if (node.typeName && node.typeName.namePath) {
return `${node.typeName.namePath} ${nodeVisibility}${nodeName}` return `${node.typeName.namePath} ${nodeVisibility}${nodeName}`
} } else {
else {
return `${nodeName}${nodeName}` return `${nodeName}${nodeName}`
} }
} }
@ -667,7 +708,7 @@ export class CodeParser extends Plugin {
* @returns * @returns
*/ */
async getFunctionParamaters(node: any) { async getFunctionParamaters(node: any) {
const localParam = (node.parameters && node.parameters.parameters) || (node.parameters) const localParam = (node.parameters && node.parameters.parameters) || node.parameters
if (localParam) { if (localParam) {
const params = [] const params = []
for (const param of localParam) { for (const param of localParam) {
@ -683,7 +724,7 @@ export class CodeParser extends Plugin {
* @returns * @returns
*/ */
async getFunctionReturnParameters(node: any) { async getFunctionReturnParameters(node: any) {
const localParam = (node.returnParameters && node.returnParameters.parameters) const localParam = node.returnParameters && node.returnParameters.parameters
if (localParam) { if (localParam) {
const params = [] const params = []
for (const param of localParam) { for (const param of localParam) {
@ -692,7 +733,4 @@ export class CodeParser extends Plugin {
return `(${params.join(', ')})` return `(${params.join(', ')})`
} }
} }
} }

@ -48,13 +48,9 @@ export class PermissionHandlerPlugin extends Plugin {
switchMode(from: Profile, to: Profile, method: string, set: boolean, sensitiveCall: boolean) { switchMode(from: Profile, to: Profile, method: string, set: boolean, sensitiveCall: boolean) {
if (sensitiveCall) { if (sensitiveCall) {
set set ? (this.sessionPermissions[to.name][method][from.name] = {}) : delete this.sessionPermissions[to.name][method][from.name]
? this.sessionPermissions[to.name][method][from.name] = {}
: delete this.sessionPermissions[to.name][method][from.name]
} else { } else {
set set ? (this.permissions[to.name][method][from.name] = {}) : delete this.permissions[to.name][method][from.name]
? this.permissions[to.name][method][from.name] = {}
: delete this.permissions[to.name][method][from.name]
} }
} }
@ -124,10 +120,10 @@ export class PermissionHandlerPlugin extends Plugin {
} }
const modal: AppModal = { const modal: AppModal = {
id: 'PermissionHandler', id: 'PermissionHandler',
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />, title: <FormattedMessage id="permissionHandler.permissionNeededFor" values={{to: to.displayName || to.name}} />,
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>, message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>,
okLabel: <FormattedMessage id='permissionHandler.accept' />, okLabel: <FormattedMessage id="permissionHandler.accept" />,
cancelLabel: <FormattedMessage id='permissionHandler.decline' /> cancelLabel: <FormattedMessage id="permissionHandler.decline" />
} }
const result = await this.call('notification', 'modal', modal) const result = await this.call('notification', 'modal', modal)

@ -19,10 +19,10 @@ const profile = {
description: 'Using Remixd daemon, allow to access file system', description: 'Using Remixd daemon, allow to access file system',
kind: 'other', kind: 'other',
version: packageJson.version, version: packageJson.version,
repo: "https://github.com/ethereum/remix-project/tree/master/libs/remixd", repo: 'https://github.com/ethereum/remix-project/tree/master/libs/remixd',
maintainedBy: "Remix", maintainedBy: 'Remix',
documentation: "https://remix-ide.readthedocs.io/en/latest/remixd.html", documentation: 'https://remix-ide.readthedocs.io/en/latest/remixd.html',
authorContact: "" authorContact: ''
} }
export class RemixdHandle extends WebsocketPlugin { export class RemixdHandle extends WebsocketPlugin {
@ -62,7 +62,6 @@ export class RemixdHandle extends WebsocketPlugin {
} }
await this.appManager.deactivatePlugin('remixd') await this.appManager.deactivatePlugin('remixd')
} }
async callPluginMethod(key: string, payload?: any[]) { async callPluginMethod(key: string, payload?: any[]) {
@ -91,7 +90,8 @@ export class RemixdHandle extends WebsocketPlugin {
this.canceled() this.canceled()
} else { } else {
const intervalId = setInterval(() => { const intervalId = setInterval(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed if (!this.socket || (this.socket && this.socket.readyState === 3)) {
// 3 means connection closed
clearInterval(intervalId) clearInterval(intervalId)
const alert: AlertModal = { const alert: AlertModal = {
id: 'connectionAlert', id: 'connectionAlert',
@ -103,7 +103,7 @@ export class RemixdHandle extends WebsocketPlugin {
}, 3000) }, 3000)
this.localhostProvider.init(() => { this.localhostProvider.init(() => {
this.call('filePanel', 'setWorkspace', {name: LOCALHOST, isLocalhost: true}, true) this.call('filePanel', 'setWorkspace', {name: LOCALHOST, isLocalhost: true}, true)
}); })
for (const plugin of this.dependentPlugins) { for (const plugin of this.dependentPlugins) {
await this.appManager.activatePlugin(plugin) await this.appManager.activatePlugin(plugin)
} }
@ -118,7 +118,7 @@ export class RemixdHandle extends WebsocketPlugin {
title: 'Access file system using remixd', title: 'Access file system using remixd',
message: remixdDialog(), message: remixdDialog(),
okLabel: 'Connect', okLabel: 'Connect',
cancelLabel: 'Cancel', cancelLabel: 'Cancel'
} }
const result = await this.call('notification', 'modal', mod) const result = await this.call('notification', 'modal', mod)
if (result) { if (result) {
@ -126,7 +126,8 @@ export class RemixdHandle extends WebsocketPlugin {
this.localhostProvider.preInit() this.localhostProvider.preInit()
super.activate() super.activate()
setTimeout(() => { setTimeout(() => {
if (!this.socket || (this.socket && this.socket.readyState === 3)) { // 3 means connection closed if (!this.socket || (this.socket && this.socket.readyState === 3)) {
// 3 means connection closed
connection(new Error('Connection with daemon failed.')) connection(new Error('Connection with daemon failed.'))
} else { } else {
connection() connection()
@ -135,14 +136,15 @@ export class RemixdHandle extends WebsocketPlugin {
} catch (error) { } catch (error) {
connection(error) connection(error)
} }
} } else {
else {
await this.canceled() await this.canceled()
} }
} else { } else {
try { try {
super.activate() super.activate()
setTimeout(() => { connection() }, 2000) setTimeout(() => {
connection()
}, 2000)
} catch (error) { } catch (error) {
connection(error) connection(error)
} }
@ -153,37 +155,53 @@ export class RemixdHandle extends WebsocketPlugin {
function remixdDialog() { function remixdDialog() {
const commandText = 'remixd' const commandText = 'remixd'
const fullCommandText = 'remixd -s <path-to-the-shared-folder> -u <remix-ide-instance-URL>' const fullCommandText = 'remixd -s <path-to-the-shared-folder> -u <remix-ide-instance-URL>'
return (<> return (
<div className=''> <>
<div className='mb-2 text-break'> <div className="">
Access your local file system from Remix IDE using <a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">Remixd NPM package</a>. <div className="mb-2 text-break">
Access your local file system from Remix IDE using{' '}
<a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">
Remixd NPM package
</a>
.
</div> </div>
<div className='mb-2 text-break'> <div className="mb-2 text-break">
Remixd <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">documentation</a>. Remixd{' '}
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">
documentation
</a>
.
</div> </div>
<div className='mb-2 text-break'> <div className="mb-2 text-break">
The remixd command is: The remixd command is:
<br /><b>{commandText}</b> <br />
<b>{commandText}</b>
</div> </div>
<div className='mb-2 text-break'> <div className="mb-2 text-break">
The remixd command without options uses the terminal's current directory as the shared directory and the shared Remix domain can only be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org The remixd command without options uses the terminal's current directory as the shared directory and the shared Remix domain can only be https://remix.ethereum.org,
https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org
</div> </div>
<div className='mb-2 text-break'> <div className="mb-2 text-break">
Example command with flags: <br /> Example command with flags: <br />
<b>{fullCommandText}</b> <b>{fullCommandText}</b>
</div> </div>
<div className='mb-2 text-break'> <div className="mb-2 text-break">
For info about ports, see <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a> For info about ports, see{' '}
</div> <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">
<div className='mb-2 text-break'> Remixd ports usage
This feature is still in Alpha. We recommend to keep a backup of the shared folder. </a>
</div> </div>
<div className='mb-2 text-break'> <div className="mb-2 text-break">This feature is still in Alpha. We recommend to keep a backup of the shared folder.</div>
<div className="mb-2 text-break">
<h6 className="text-danger"> <h6 className="text-danger">
Before using, make sure remixd version is latest i.e. <b>v{remixdVersion}</b> Before using, make sure remixd version is latest i.e. <b>v{remixdVersion}</b>
<br></br><a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">Read here how to update it</a> <br></br>
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">
Read here how to update it
</a>
</h6> </h6>
</div> </div>
</div> </div>
</>) </>
)
} }

@ -3,7 +3,7 @@ import { format } from 'util'
import {Plugin} from '@remixproject/engine' import {Plugin} from '@remixproject/engine'
import {compile} from '@remix-project/remix-solidity' import {compile} from '@remix-project/remix-solidity'
import {Transaction} from 'web3-types' import {Transaction} from 'web3-types'
const _paq = window._paq = window._paq || [] //eslint-disable-line const _paq = (window._paq = window._paq || []) //eslint-disable-line
const profile = { const profile = {
name: 'solidity-script', name: 'solidity-script',
@ -23,7 +23,6 @@ export class SolidityScript extends Plugin {
let content = await this.call('fileManager', 'readFile', path) let content = await this.call('fileManager', 'readFile', path)
const params = await this.call('solidity', 'getCompilerParameters') const params = await this.call('solidity', 'getCompilerParameters')
content = ` content = `
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
@ -42,7 +41,9 @@ export class SolidityScript extends Plugin {
// compile // compile
const compilation = await compile(targets, params, async (url, cb) => { const compilation = await compile(targets, params, async (url, cb) => {
await this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)) await this.call('contentImport', 'resolveAndSave', url)
.then((result) => cb(null, result))
.catch((error) => cb(error.message))
}) })
if (compilation.data.error) { if (compilation.data.error) {
@ -83,9 +84,12 @@ export class SolidityScript extends Plugin {
const hhlogs = await web3.testPlugin.getHHLogsForTx(receiptCall.transactionHash) const hhlogs = await web3.testPlugin.getHHLogsForTx(receiptCall.transactionHash)
if (hhlogs && hhlogs.length) { if (hhlogs && hhlogs.length) {
const finalLogs = <div><div><b>console.log:</b></div> const finalLogs = (
{ <div>
hhlogs.map((log) => { <div>
<b>console.log:</b>
</div>
{hhlogs.map((log) => {
let formattedLog let formattedLog
// Hardhat implements the same formatting options that can be found in Node.js' console.log, // Hardhat implements the same formatting options that can be found in Node.js' console.log,
// which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args
@ -99,6 +103,7 @@ export class SolidityScript extends Plugin {
return <div>{formattedLog}</div> return <div>{formattedLog}</div>
})} })}
</div> </div>
)
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) _paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'logHtml', finalLogs) this.call('terminal', 'logHtml', finalLogs)
} }

@ -13,7 +13,7 @@ import { customAction } from '@remixproject/plugin-api'
import {ClassOptions} from 'sol2uml/lib/converterClass2Dot' import {ClassOptions} from 'sol2uml/lib/converterClass2Dot'
const parser = (window as any).SolidityParser const parser = (window as any).SolidityParser
const _paq = window._paq = window._paq || [] const _paq = (window._paq = window._paq || [])
const profile = { const profile = {
name: 'solidityumlgen', name: 'solidityumlgen',
@ -21,7 +21,7 @@ const profile = {
description: 'Generates UML diagram in svg format from last compiled contract', description: 'Generates UML diagram in svg format from last compiled contract',
location: 'mainPanel', location: 'mainPanel',
methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'], methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'],
events: [], events: []
} }
/** /**
@ -54,7 +54,6 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
this.currentlySelectedTheme = '' this.currentlySelectedTheme = ''
this.themeName = '' this.themeName = ''
this.activeTheme = {} as ThemeSummary this.activeTheme = {} as ThemeSummary
this.appManager = appManager this.appManager = appManager
this.element = document.createElement('div') this.element = document.createElement('div')
@ -73,14 +72,20 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
const normalized = normalizeContractPath(file) const normalized = normalizeContractPath(file)
this.currentFile = normalized[normalized.length - 1] this.currentFile = normalized[normalized.length - 1]
try { try {
if (data.sources && Object.keys(data.sources).length > 1) { // we should flatten first as there are multiple asts if (data.sources && Object.keys(data.sources).length > 1) {
// we should flatten first as there are multiple asts
result = await this.flattenContract(source, file, data) result = await this.flattenContract(source, file, data)
} }
const ast = result.length > 1 ? parser.parse(result) : parser.parse(source.sources[file].content) const ast = result.length > 1 ? parser.parse(result) : parser.parse(source.sources[file].content)
this.umlClasses = convertAST2UmlClasses(ast, this.currentFile) this.umlClasses = convertAST2UmlClasses(ast, this.currentFile)
let umlDot = '' let umlDot = ''
this.activeTheme = await this.call('theme', 'currentTheme') this.activeTheme = await this.call('theme', 'currentTheme')
umlDot = convertUmlClasses2Dot(this.umlClasses, false, { backColor: this.activeTheme.backgroundColor, textColor: this.activeTheme.textColor, shapeColor: this.activeTheme.shapeColor, fillColor: this.activeTheme.fillColor }) umlDot = convertUmlClasses2Dot(this.umlClasses, false, {
backColor: this.activeTheme.backgroundColor,
textColor: this.activeTheme.textColor,
shapeColor: this.activeTheme.shapeColor,
fillColor: this.activeTheme.fillColor
})
const payload = vizRenderStringSync(umlDot) const payload = vizRenderStringSync(umlDot)
this.updatedSvg = payload this.updatedSvg = payload
_paq.push(['trackEvent', 'solidityumlgen', 'umlgenerated']) _paq.push(['trackEvent', 'solidityumlgen', 'umlgenerated'])
@ -93,8 +98,7 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
} }
getThemeCssVariables(cssVars: string) { getThemeCssVariables(cssVars: string) {
return window.getComputedStyle(document.documentElement) return window.getComputedStyle(document.documentElement).getPropertyValue(cssVars)
.getPropertyValue(cssVars)
} }
private handleThemeChange() { private handleThemeChange() {
@ -102,7 +106,12 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
this.currentlySelectedTheme = theme.quality this.currentlySelectedTheme = theme.quality
this.activeTheme = theme this.activeTheme = theme
this.themeDark = theme.backgroundColor this.themeDark = theme.backgroundColor
const umlDot = convertUmlClasses2Dot(this.umlClasses, false, { backColor: this.activeTheme.backgroundColor, textColor: this.activeTheme.textColor, shapeColor: this.activeTheme.shapeColor, fillColor: this.activeTheme.fillColor }) const umlDot = convertUmlClasses2Dot(this.umlClasses, false, {
backColor: this.activeTheme.backgroundColor,
textColor: this.activeTheme.textColor,
shapeColor: this.activeTheme.shapeColor,
fillColor: this.activeTheme.fillColor
})
this.updatedSvg = vizRenderStringSync(umlDot) this.updatedSvg = vizRenderStringSync(umlDot)
this.renderComponent() this.renderComponent()
await this.call('tabs', 'focus', 'solidityumlgen') await this.call('tabs', 'focus', 'solidityumlgen')
@ -138,8 +147,6 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
return result return result
} }
async showUmlDiagram(svgPayload: string) { async showUmlDiagram(svgPayload: string) {
this.updatedSvg = svgPayload this.updatedSvg = svgPayload
this.renderComponent() this.renderComponent()
@ -156,9 +163,11 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
} }
render() { render() {
return <div id='sol-uml-gen'> return (
<div id="sol-uml-gen">
<PluginViewWrapper plugin={this} /> <PluginViewWrapper plugin={this} />
</div> </div>
)
} }
renderComponent() { renderComponent() {
@ -171,12 +180,13 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
themeDark: this.themeDark, themeDark: this.themeDark,
fileName: this.currentFile, fileName: this.currentFile,
themeCollection: this.themeCollection, themeCollection: this.themeCollection,
activeTheme: this.activeTheme, activeTheme: this.activeTheme
}) })
} }
updateComponent(state: any) { updateComponent(state: any) {
return <RemixUiSolidityUmlGen return (
<RemixUiSolidityUmlGen
updatedSvg={state.updatedSvg} updatedSvg={state.updatedSvg}
loading={state.loading} loading={state.loading}
themeSelected={state.currentlySelectedTheme} themeSelected={state.currentlySelectedTheme}
@ -185,10 +195,10 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
themeCollection={state.themeCollection} themeCollection={state.themeCollection}
themeDark={state.themeDark} themeDark={state.themeDark}
/> />
)
} }
} }
interface Sol2umlClassOptions extends ClassOptions { interface Sol2umlClassOptions extends ClassOptions {
backColor?: string backColor?: string
shapeColor?: string shapeColor?: string
@ -198,12 +208,7 @@ interface Sol2umlClassOptions extends ClassOptions {
import {dirname} from 'path' import {dirname} from 'path'
import {convertClass2Dot} from 'sol2uml/lib/converterClass2Dot' import {convertClass2Dot} from 'sol2uml/lib/converterClass2Dot'
import { import {Association, ClassStereotype, ReferenceType, UmlClass} from 'sol2uml/lib/umlClass'
Association,
ClassStereotype,
ReferenceType,
UmlClass,
} from 'sol2uml/lib/umlClass'
import {findAssociatedClass} from 'sol2uml/lib/associations' import {findAssociatedClass} from 'sol2uml/lib/associations'
// const debug = require('debug')('sol2uml') // const debug = require('debug')('sol2uml')
@ -216,11 +221,7 @@ import { findAssociatedClass } from 'sol2uml/lib/associations'
* @param classOptions command line options for the `class` command * @param classOptions command line options for the `class` command
* @return dotString Graphviz's DOT format for defining nodes, edges and clusters. * @return dotString Graphviz's DOT format for defining nodes, edges and clusters.
*/ */
export function convertUmlClasses2Dot( export function convertUmlClasses2Dot(umlClasses: UmlClass[], clusterFolders: boolean = false, classOptions: Sol2umlClassOptions = {}): string {
umlClasses: UmlClass[],
clusterFolders: boolean = false,
classOptions: Sol2umlClassOptions = {}
): string {
let dotString: string = ` let dotString: string = `
digraph UmlClassDiagram { digraph UmlClassDiagram {
rankdir=BT rankdir=BT
@ -285,10 +286,7 @@ function sortUmlClassesByCodePath(umlClasses: UmlClass[]): UmlClass[] {
}) })
} }
export function addAssociationsToDot( export function addAssociationsToDot(umlClasses: UmlClass[], classOptions: ClassOptions = {}): string {
umlClasses: UmlClass[],
classOptions: ClassOptions = {}
): string {
let dotString: string = '' let dotString: string = ''
// for each class // for each class
@ -318,18 +316,9 @@ export function addAssociationsToDot(
// for each association in that class // for each association in that class
for (const association of Object.values(sourceUmlClass.associations)) { for (const association of Object.values(sourceUmlClass.associations)) {
const targetUmlClass = findAssociatedClass( const targetUmlClass = findAssociatedClass(association, sourceUmlClass, umlClasses)
association,
sourceUmlClass,
umlClasses
)
if (targetUmlClass) { if (targetUmlClass) {
dotString += addAssociationToDot( dotString += addAssociationToDot(sourceUmlClass, targetUmlClass, association, classOptions)
sourceUmlClass,
targetUmlClass,
association,
classOptions
)
} }
} }
} }
@ -337,41 +326,23 @@ export function addAssociationsToDot(
return dotString return dotString
} }
function addAssociationToDot( function addAssociationToDot(sourceUmlClass: UmlClass, targetUmlClass: UmlClass, association: Association, classOptions: ClassOptions = {}): string {
sourceUmlClass: UmlClass,
targetUmlClass: UmlClass,
association: Association,
classOptions: ClassOptions = {}
): string {
// do not include library or interface associations if hidden // do not include library or interface associations if hidden
// Or associations to Structs, Enums or Constants if they are hidden // Or associations to Structs, Enums or Constants if they are hidden
if ( if (
(classOptions.hideLibraries && (classOptions.hideLibraries && (sourceUmlClass.stereotype === ClassStereotype.Library || targetUmlClass.stereotype === ClassStereotype.Library)) ||
(sourceUmlClass.stereotype === ClassStereotype.Library || (classOptions.hideInterfaces && (targetUmlClass.stereotype === ClassStereotype.Interface || sourceUmlClass.stereotype === ClassStereotype.Interface)) ||
targetUmlClass.stereotype === ClassStereotype.Library)) || (classOptions.hideAbstracts && (targetUmlClass.stereotype === ClassStereotype.Abstract || sourceUmlClass.stereotype === ClassStereotype.Abstract)) ||
(classOptions.hideInterfaces && (classOptions.hideStructs && targetUmlClass.stereotype === ClassStereotype.Struct) ||
(targetUmlClass.stereotype === ClassStereotype.Interface || (classOptions.hideEnums && targetUmlClass.stereotype === ClassStereotype.Enum) ||
sourceUmlClass.stereotype === ClassStereotype.Interface)) || (classOptions.hideConstants && targetUmlClass.stereotype === ClassStereotype.Constant)
(classOptions.hideAbstracts &&
(targetUmlClass.stereotype === ClassStereotype.Abstract ||
sourceUmlClass.stereotype === ClassStereotype.Abstract)) ||
(classOptions.hideStructs &&
targetUmlClass.stereotype === ClassStereotype.Struct) ||
(classOptions.hideEnums &&
targetUmlClass.stereotype === ClassStereotype.Enum) ||
(classOptions.hideConstants &&
targetUmlClass.stereotype === ClassStereotype.Constant)
) { ) {
return '' return ''
} }
let dotString = `\n${sourceUmlClass.id} -> ${targetUmlClass.id} [` let dotString = `\n${sourceUmlClass.id} -> ${targetUmlClass.id} [`
if ( if (association.referenceType == ReferenceType.Memory || (association.realization && targetUmlClass.stereotype === ClassStereotype.Interface)) {
association.referenceType == ReferenceType.Memory ||
(association.realization &&
targetUmlClass.stereotype === ClassStereotype.Interface)
) {
dotString += 'style=dashed, ' dotString += 'style=dashed, '
} }

@ -4,17 +4,17 @@ import { Blockchain } from '../../blockchain/blockchain'
import {ethers} from 'ethers' import {ethers} from 'ethers'
export type JsonDataRequest = { export type JsonDataRequest = {
id: number, id: number
jsonrpc: string // version jsonrpc: string // version
method: string, method: string
params: Array<any>, params: Array<any>
} }
export type JsonDataResult = { export type JsonDataResult = {
id: number, id: number
jsonrpc: string // version jsonrpc: string // version
result?: any, result?: any
error?: any, error?: any
} }
export type RejectRequest = (error: Error) => void export type RejectRequest = (error: Error) => void
@ -61,7 +61,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
okLabel: 'OK', okLabel: 'OK',
cancelLabel: 'Cancel', cancelLabel: 'Cancel',
validationFn: (value) => { validationFn: (value) => {
if (!value) return { valid: false, message: "value is empty" } if (!value) return {valid: false, message: 'value is empty'}
if (value.startsWith('https://') || value.startsWith('http://')) { if (value.startsWith('https://') || value.startsWith('http://')) {
return { return {
valid: true, valid: true,
@ -110,7 +110,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
const modalContent: AlertModal = { const modalContent: AlertModal = {
id: this.profile.name, id: this.profile.name,
title: this.profile.displayName, title: this.profile.displayName,
message: `Error while connecting to the provider, provider not connected`, message: `Error while connecting to the provider, provider not connected`
} }
this.call('notification', 'alert', modalContent) this.call('notification', 'alert', modalContent)
} }

@ -10,14 +10,17 @@ export class CustomForkVMProvider extends BasicVMProvider {
inputs: any inputs: any
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-custom-fork', name: 'vm-custom-fork',
displayName: 'Custom fork - Remix VM', displayName: 'Custom fork - Remix VM',
kind: 'provider', kind: 'provider',
description: 'Custom fork - Remix VM', description: 'Custom fork - Remix VM',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = '' this.fork = ''
this.nodeUrl = '' this.nodeUrl = ''
@ -27,7 +30,8 @@ export class CustomForkVMProvider extends BasicVMProvider {
async init() { async init() {
const body = () => { const body = () => {
return <div> return (
<div>
<span>Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state.</span> <span>Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state.</span>
<div> <div>
<label className="mt-3 mb-1">Node URL</label> <label className="mt-3 mb-1">Node URL</label>
@ -35,17 +39,29 @@ export class CustomForkVMProvider extends BasicVMProvider {
</div> </div>
<div> <div>
<label className="mt-3 mb-1">Block number (or "latest")</label> <label className="mt-3 mb-1">Block number (or "latest")</label>
<input data-id="CustomForkBlockNumber" name="blockNumber" type="text" defaultValue="latest" placeholder='block number or "latest"' className="border form-control border-right-0" /> <input
data-id="CustomForkBlockNumber"
name="blockNumber"
type="text"
defaultValue="latest"
placeholder='block number or "latest"'
className="border form-control border-right-0"
/>
</div> </div>
<div> <div>
<label className="mt-3 mb-1">EVM</label> <label className="mt-3 mb-1">EVM</label>
<select data-id="CustomForkEvmType" name="evmType" defaultValue="merge" className="border form-control border-right-0"> <select data-id="CustomForkEvmType" name="evmType" defaultValue="merge" className="border form-control border-right-0">
{Object.keys(Hardfork).map((value, index) => { {Object.keys(Hardfork).map((value, index) => {
return <option value={Hardfork[value]} key={index}>{value}</option> return (
<option value={Hardfork[value]} key={index}>
{value}
</option>
)
})} })}
</select> </select>
</div> </div>
</div> </div>
)
} }
const result = await ((): Promise<any> => { const result = await ((): Promise<any> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -54,7 +70,7 @@ export class CustomForkVMProvider extends BasicVMProvider {
title: this.profile.displayName, title: this.profile.displayName,
message: body(), message: body(),
validationFn: (data: any) => { validationFn: (data: any) => {
if(data.nodeUrl !== '' && !data.nodeUrl.startsWith("http")) { if (data.nodeUrl !== '' && !data.nodeUrl.startsWith('http')) {
return { return {
valid: false, valid: false,
message: 'node URL should be a valid URL' message: 'node URL should be a valid URL'
@ -98,9 +114,9 @@ export class CustomForkVMProvider extends BasicVMProvider {
} }
return { return {
'fork': this.fork, fork: this.fork,
'nodeUrl': this.nodeUrl, nodeUrl: this.nodeUrl,
'blockNumber': this.blockNumber blockNumber: this.blockNumber
} }
} }
} }

@ -21,16 +21,29 @@ export class ExternalHttpProvider extends AbstractProvider {
return ( return (
<> <>
<div className=""> <div className="">
Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see <a href="https://geth.ethereum.org/docs/rpc/server" target="_blank" rel="noreferrer">Geth Docs on rpc server</a>) Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see{' '}
<div className="border p-1">geth --http --http.corsdomain https://remix.ethereum.org</div> <a href="https://geth.ethereum.org/docs/rpc/server" target="_blank" rel="noreferrer">
Geth Docs on rpc server
</a>
)<div className="border p-1">geth --http --http.corsdomain https://remix.ethereum.org</div>
<br /> <br />
To run Remix & a local Geth test node, use this command: (see <a href="https://geth.ethereum.org/getting-started/dev-mode" target="_blank" rel="noreferrer">Geth Docs on Dev mode</a>) To run Remix & a local Geth test node, use this command: (see{' '}
<div className="border p-1">geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console</div> <a href="https://geth.ethereum.org/getting-started/dev-mode" target="_blank" rel="noreferrer">
Geth Docs on Dev mode
</a>
)
<div className="border p-1">
geth --http --http.corsdomain="{window.origin}" --http.api web3,eth,debug,personal,net --vmdebug --datadir {thePath} --dev console
</div>
<br /> <br />
<br /> <br />
<b>WARNING:</b> It is not safe to use the --http.corsdomain flag with a wildcard: <b>--http.corsdomain *</b> <b>WARNING:</b> It is not safe to use the --http.corsdomain flag with a wildcard: <b>--http.corsdomain *</b>
<br /> <br />
<br />For more info: <a href="https://remix-ide.readthedocs.io/en/latest/run.html#more-about-web3-provider" target="_blank" rel="noreferrer">Remix Docs on External HTTP Provider</a> <br />
For more info:{' '}
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#more-about-web3-provider" target="_blank" rel="noreferrer">
Remix Docs on External HTTP Provider
</a>
<br /> <br />
<br /> <br />
External HTTP Provider Endpoint External HTTP Provider Endpoint

@ -18,11 +18,20 @@ export class FoundryProvider extends AbstractProvider {
body(): JSX.Element { body(): JSX.Element {
return ( return (
<div> Note: To run Anvil on your system, run: <div>
<div className="p-1 pl-3"><b>curl -L https://foundry.paradigm.xyz | bash</b></div> {' '}
<div className="p-1 pl-3"><b>anvil</b></div> Note: To run Anvil on your system, run:
<div className="p-1 pl-3">
<b>curl -L https://foundry.paradigm.xyz | bash</b>
</div>
<div className="p-1 pl-3">
<b>anvil</b>
</div>
<div className="pt-2 pb-4"> <div className="pt-2 pb-4">
For more info, visit: <a href="https://github.com/foundry-rs/foundry" target="_blank">Foundry Documentation</a> For more info, visit:{' '}
<a href="https://github.com/foundry-rs/foundry" target="_blank">
Foundry Documentation
</a>
</div> </div>
<div>Anvil JSON-RPC Endpoint:</div> <div>Anvil JSON-RPC Endpoint:</div>
</div> </div>

@ -18,11 +18,20 @@ export class GanacheProvider extends AbstractProvider {
body(): JSX.Element { body(): JSX.Element {
return ( return (
<div> Note: To run Ganache on your system, run: <div>
<div className="p-1 pl-3"><b>yarn global add ganache</b></div> {' '}
<div className="p-1 pl-3"><b>ganache</b></div> Note: To run Ganache on your system, run:
<div className="p-1 pl-3">
<b>yarn global add ganache</b>
</div>
<div className="p-1 pl-3">
<b>ganache</b>
</div>
<div className="pt-2 pb-4"> <div className="pt-2 pb-4">
For more info, visit: <a href="https://github.com/trufflesuite/ganache" target="_blank">Ganache Documentation</a> For more info, visit:{' '}
<a href="https://github.com/trufflesuite/ganache" target="_blank">
Ganache Documentation
</a>
</div> </div>
<div>Ganache JSON-RPC Endpoint:</div> <div>Ganache JSON-RPC Endpoint:</div>
</div> </div>

@ -5,14 +5,17 @@ export class GoerliForkVMProvider extends BasicVMProvider {
nodeUrl: string nodeUrl: string
blockNumber: number | 'latest' blockNumber: number | 'latest'
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-goerli-fork', name: 'vm-goerli-fork',
displayName: 'Goerli fork - Remix VM (London)', displayName: 'Goerli fork - Remix VM (London)',
kind: 'provider', kind: 'provider',
description: 'Remix VM (London)', description: 'Remix VM (London)',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = 'shanghai' this.fork = 'shanghai'
this.nodeUrl = 'https://remix-sepolia.ethdevops.io' this.nodeUrl = 'https://remix-sepolia.ethdevops.io'
@ -21,9 +24,9 @@ export class GoerliForkVMProvider extends BasicVMProvider {
async init() { async init() {
return { return {
'fork': this.fork, fork: this.fork,
'nodeUrl': this.nodeUrl, nodeUrl: this.nodeUrl,
'blockNumber': this.blockNumber blockNumber: this.blockNumber
} }
} }
} }

@ -18,10 +18,17 @@ export class HardhatProvider extends AbstractProvider {
body(): JSX.Element { body(): JSX.Element {
return ( return (
<div> Note: To run Hardhat network node on your system, go to hardhat project folder and run command: <div>
<div className="p-1 pl-3"><b>npx hardhat node</b></div> {' '}
Note: To run Hardhat network node on your system, go to hardhat project folder and run command:
<div className="p-1 pl-3">
<b>npx hardhat node</b>
</div>
<div className="pt-2 pb-4"> <div className="pt-2 pb-4">
For more info, visit: <a href="https://hardhat.org/getting-started/#connecting-a-wallet-or-dapp-to-hardhat-network" target="_blank">Hardhat Documentation</a> For more info, visit:{' '}
<a href="https://hardhat.org/getting-started/#connecting-a-wallet-or-dapp-to-hardhat-network" target="_blank">
Hardhat Documentation
</a>
</div> </div>
<div>Hardhat JSON-RPC Endpoint:</div> <div>Hardhat JSON-RPC Endpoint:</div>
</div> </div>

@ -15,8 +15,7 @@ export class InjectedL2Provider extends InjectedProviderDefaultBase {
async init() { async init() {
await super.init() await super.init()
if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addL2Network(this.chainName, this.chainId, this.rpcUrls) if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addL2Network(this.chainName, this.chainId, this.rpcUrls)
else else throw new Error('Cannot add the L2 network to main injected provider')
throw new Error('Cannot add the L2 network to main injected provider')
return {} return {}
} }
} }
@ -25,8 +24,8 @@ export const addL2Network = async (chainName: string, chainId: string, rpcUrls:
try { try {
await (window as any).ethereum.request({ await (window as any).ethereum.request({
method: 'wallet_switchEthereumChain', method: 'wallet_switchEthereumChain',
params: [{ chainId: chainId }], params: [{chainId: chainId}]
}); })
} catch (switchError) { } catch (switchError) {
// This error code indicates that the chain has not been added to MetaMask. // This error code indicates that the chain has not been added to MetaMask.
if (switchError.code === 4902) { if (switchError.code === 4902) {
@ -37,15 +36,15 @@ export const addL2Network = async (chainName: string, chainId: string, rpcUrls:
{ {
chainId: chainId, chainId: chainId,
chainName: chainName, chainName: chainName,
rpcUrls: rpcUrls, rpcUrls: rpcUrls
}, }
], ]
}); })
await (window as any).ethereum.request({ await (window as any).ethereum.request({
method: 'wallet_switchEthereumChain', method: 'wallet_switchEthereumChain',
params: [{ chainId: chainId }], params: [{chainId: chainId}]
}); })
} catch (addError) { } catch (addError) {
// handle "add" error // handle "add" error
} }

@ -11,7 +11,6 @@ const profile = {
} }
export class InjectedArbitrumOneProvider extends InjectedL2Provider { export class InjectedArbitrumOneProvider extends InjectedL2Provider {
constructor() { constructor() {
super(profile, 'Arbitrum One', '0xa4b1', ['https://arb1.arbitrum.io/rpc']) super(profile, 'Arbitrum One', '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
} }

@ -11,7 +11,6 @@ const profile = {
} }
export class Injected0ptimismProvider extends InjectedL2Provider { export class Injected0ptimismProvider extends InjectedL2Provider {
constructor() { constructor() {
super(profile, 'Optimism', '0xa', ['https://mainnet.optimism.io']) super(profile, 'Optimism', '0xa', ['https://mainnet.optimism.io'])
} }

@ -10,7 +10,7 @@ export class InjectedProviderDefaultBase extends InjectedProvider {
async init() { async init() {
const injectedProvider = this.getInjectedProvider() const injectedProvider = this.getInjectedProvider()
if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) { if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) {
if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).') if (!(await injectedProvider._metamask.isUnlocked())) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
} }
return super.init() return super.init()
} }

@ -25,8 +25,8 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
onActivation(): void { onActivation(): void {
try { try {
const web3Provider = this.getInjectedProvider() const web3Provider = this.getInjectedProvider()
web3Provider.on('accountsChanged', this.listenerAccountsChanged); web3Provider.on('accountsChanged', this.listenerAccountsChanged)
web3Provider.on('chainChanged', this.listenerChainChanged); web3Provider.on('chainChanged', this.listenerChainChanged)
} catch (error) { } catch (error) {
console.log('unable to listen on context changed') console.log('unable to listen on context changed')
} }
@ -44,17 +44,15 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
askPermission(throwIfNoInjectedProvider) { askPermission(throwIfNoInjectedProvider) {
const web3Provider = this.getInjectedProvider() const web3Provider = this.getInjectedProvider()
if (typeof web3Provider !== "undefined" && typeof web3Provider.request === "function") { if (typeof web3Provider !== 'undefined' && typeof web3Provider.request === 'function') {
web3Provider.request({ method: "eth_requestAccounts" }) web3Provider.request({method: 'eth_requestAccounts'})
} else if (throwIfNoInjectedProvider) { } else if (throwIfNoInjectedProvider) {
throw new Error(this.notFound()) throw new Error(this.notFound())
} }
} }
body(): JSX.Element { body(): JSX.Element {
return ( return <div></div>
<div></div>
)
} }
async init() { async init() {
@ -80,7 +78,11 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
const web3Provider = this.getInjectedProvider() const web3Provider = this.getInjectedProvider()
if (!web3Provider) { if (!web3Provider) {
this.call('notification', 'toast', 'No injected provider (e.g Metamask) has been found.') this.call('notification', 'toast', 'No injected provider (e.g Metamask) has been found.')
return resolve({ jsonrpc: '2.0', error: 'no injected provider found', id: data.id }) return resolve({
jsonrpc: '2.0',
error: 'no injected provider found',
id: data.id
})
} }
try { try {
let resultData let resultData
@ -99,7 +101,11 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
resolve({jsonrpc: '2.0', error: 'no return data provided', id: data.id}) resolve({jsonrpc: '2.0', error: 'no return data provided', id: data.id})
} }
} catch (error) { } catch (error) {
resolve({ jsonrpc: '2.0', error: error.data && error.data.message ? error.data.message : error.message, id: data.id }) resolve({
jsonrpc: '2.0',
error: error.data && error.data.message ? error.data.message : error.message,
id: data.id
})
} }
} }
} }

@ -5,14 +5,17 @@ export class MainnetForkVMProvider extends BasicVMProvider {
nodeUrl: string nodeUrl: string
blockNumber: number | 'latest' blockNumber: number | 'latest'
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-mainnet-fork', name: 'vm-mainnet-fork',
displayName: 'Mainet fork -Remix VM (London)', displayName: 'Mainet fork -Remix VM (London)',
kind: 'provider', kind: 'provider',
description: 'Remix VM (London)', description: 'Remix VM (London)',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = 'shanghai' this.fork = 'shanghai'
this.nodeUrl = 'https://mainnet.infura.io/v3/08b2a484451e4635a28b3d8234f24332' this.nodeUrl = 'https://mainnet.infura.io/v3/08b2a484451e4635a28b3d8234f24332'
@ -21,9 +24,9 @@ export class MainnetForkVMProvider extends BasicVMProvider {
async init() { async init() {
return { return {
'fork': this.fork, fork: this.fork,
'nodeUrl': this.nodeUrl, nodeUrl: this.nodeUrl,
'blockNumber': this.blockNumber blockNumber: this.blockNumber
} }
} }
} }

@ -5,14 +5,17 @@ export class SepoliaForkVMProvider extends BasicVMProvider {
nodeUrl: string nodeUrl: string
blockNumber: number | 'latest' blockNumber: number | 'latest'
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-sepolia-fork', name: 'vm-sepolia-fork',
displayName: 'Sepolia fork - Remix VM (London)', displayName: 'Sepolia fork - Remix VM (London)',
kind: 'provider', kind: 'provider',
description: 'Remix VM (London)', description: 'Remix VM (London)',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = 'shanghai' this.fork = 'shanghai'
this.nodeUrl = 'https://remix-sepolia.ethdevops.io' this.nodeUrl = 'https://remix-sepolia.ethdevops.io'
@ -21,9 +24,9 @@ export class SepoliaForkVMProvider extends BasicVMProvider {
async init() { async init() {
return { return {
'fork': this.fork, fork: this.fork,
'nodeUrl': this.nodeUrl, nodeUrl: this.nodeUrl,
'blockNumber': this.blockNumber blockNumber: this.blockNumber
} }
} }
} }

@ -14,12 +14,12 @@ export class BasicVMProvider extends Plugin implements IProvider {
this.fork = '' this.fork = ''
} }
async init (): Promise<{ [id: string] : any }> { return {} } async init(): Promise<{[id: string]: any}> {
return {}
}
body(): JSX.Element { body(): JSX.Element {
return ( return <div></div>
<div></div>
)
} }
sendAsync(data: JsonDataRequest): Promise<any> { sendAsync(data: JsonDataRequest): Promise<any> {
@ -44,14 +44,17 @@ export class BasicVMProvider extends Plugin implements IProvider {
export class MergeVMProvider extends BasicVMProvider { export class MergeVMProvider extends BasicVMProvider {
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-merge', name: 'vm-merge',
displayName: 'Remix VM (Merge)', displayName: 'Remix VM (Merge)',
kind: 'provider', kind: 'provider',
description: 'Remix VM (Merge)', description: 'Remix VM (Merge)',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = 'merge' this.fork = 'merge'
} }
@ -59,14 +62,17 @@ export class MergeVMProvider extends BasicVMProvider {
export class LondonVMProvider extends BasicVMProvider { export class LondonVMProvider extends BasicVMProvider {
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-london', name: 'vm-london',
displayName: 'Remix VM (London)', displayName: 'Remix VM (London)',
kind: 'provider', kind: 'provider',
description: 'Remix VM (London)', description: 'Remix VM (London)',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = 'london' this.fork = 'london'
} }
@ -74,14 +80,17 @@ export class LondonVMProvider extends BasicVMProvider {
export class BerlinVMProvider extends BasicVMProvider { export class BerlinVMProvider extends BasicVMProvider {
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-berlin', name: 'vm-berlin',
displayName: 'Remix VM (Berlin)', displayName: 'Remix VM (Berlin)',
kind: 'provider', kind: 'provider',
description: 'Remix VM (Berlin)', description: 'Remix VM (Berlin)',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = 'berlin' this.fork = 'berlin'
} }
@ -89,14 +98,17 @@ export class BerlinVMProvider extends BasicVMProvider {
export class ShanghaiVMProvider extends BasicVMProvider { export class ShanghaiVMProvider extends BasicVMProvider {
constructor(blockchain) { constructor(blockchain) {
super({ super(
{
name: 'vm-shanghai', name: 'vm-shanghai',
displayName: 'Remix VM (Shanghai)', displayName: 'Remix VM (Shanghai)',
kind: 'provider', kind: 'provider',
description: 'Remix VM (Shanghai)', description: 'Remix VM (Shanghai)',
methods: ['sendAsync', 'init'], methods: ['sendAsync', 'init'],
version: packageJson.version version: packageJson.version
}, blockchain) },
blockchain
)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = 'shanghai' this.fork = 'shanghai'
} }

@ -17,17 +17,15 @@ const profile = {
} }
export class SearchPlugin extends ViewPlugin { export class SearchPlugin extends ViewPlugin {
constructor() { constructor() {
super(profile) super(profile)
} }
render() { render() {
return ( return (
<div id='searchTab'> <div id="searchTab">
<SearchTab plugin={this}></SearchTab> <SearchTab plugin={this}></SearchTab>
</div> </div>
); )
} }
} }

@ -18,7 +18,7 @@ const profile = {
documentation: 'https://remix-ide.readthedocs.io/en/latest/settings.html', documentation: 'https://remix-ide.readthedocs.io/en/latest/settings.html',
version: packageJson.version, version: packageJson.version,
permission: true, permission: true,
maintainedBy: "Remix" maintainedBy: 'Remix'
} }
module.exports = class SettingsTab extends ViewPlugin { module.exports = class SettingsTab extends ViewPlugin {
@ -53,13 +53,16 @@ module.exports = class SettingsTab extends ViewPlugin {
} }
render() { render() {
return <div id='settingsTab'> return (
<div id="settingsTab">
<PluginViewWrapper plugin={this} /> <PluginViewWrapper plugin={this} />
</div> </div>
)
} }
updateComponent(state: any) { updateComponent(state: any) {
return <RemixUiSettings return (
<RemixUiSettings
config={state.config} config={state.config}
editor={state.editor} editor={state.editor}
_deps={state._deps} _deps={state._deps}
@ -67,6 +70,7 @@ module.exports = class SettingsTab extends ViewPlugin {
themeModule={state._deps.themeModule} themeModule={state._deps.themeModule}
localeModule={state._deps.localeModule} localeModule={state._deps.localeModule}
/> />
)
} }
renderComponent() { renderComponent() {

@ -11,13 +11,13 @@ import { InjectedProvider } from './providers/injected'
import {NodeProvider} from './providers/node' import {NodeProvider} from './providers/node'
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, addressToString } 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} = helpers const {txResultHelper} = helpers
const {resultToRemixTx} = txResultHelper const {resultToRemixTx} = txResultHelper
import * as packageJson from '../../../../package.json' import * as packageJson from '../../../../package.json'
const _paq = window._paq = window._paq || [] //eslint-disable-line const _paq = (window._paq = window._paq || []) //eslint-disable-line
const profile = { const profile = {
name: 'blockchain', name: 'blockchain',
@ -28,19 +28,19 @@ const profile = {
} }
export type TransactionContextAPI = { export type TransactionContextAPI = {
getAddress: (cb: (error: Error, result: string) => void) => void, getAddress: (cb: (error: Error, result: string) => void) => void
getValue: (cb: (error: Error, result: string) => void) => void, getValue: (cb: (error: Error, result: string) => void) => void
getGasLimit: (cb: (error: Error, result: string) => void) => void getGasLimit: (cb: (error: Error, result: string) => void) => void
} }
// see TxRunner.ts in remix-lib // see TxRunner.ts in remix-lib
export type Transaction = { export type Transaction = {
from: string, from: string
to: string, to: string
value: string, value: string
data: string, data: string
gasLimit: number, gasLimit: number
useCall: boolean, useCall: boolean
timestamp?: number timestamp?: number
} }
@ -54,7 +54,7 @@ export class Blockchain extends Plugin {
networkcallid: number networkcallid: number
networkStatus: { networkStatus: {
network: { network: {
name: string, name: string
id: string id: string
} }
error?: string error?: string
@ -71,16 +71,22 @@ export class Blockchain extends Plugin {
this.events = new EventEmitter() this.events = new EventEmitter()
this.config = config this.config = config
const web3Runner = new TxRunnerWeb3({ const web3Runner = new TxRunnerWeb3(
{
config: this.config, config: this.config,
detectNetwork: (cb) => { detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb) this.executionContext.detectNetwork(cb)
}, },
isVM: () => { return this.executionContext.isVM() }, isVM: () => {
return this.executionContext.isVM()
},
personalMode: () => { personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
} }
}, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) },
(_) => this.executionContext.web3(),
(_) => this.executionContext.currentblockGasLimit()
)
this.txRunner = new TxRunner(web3Runner, {}) this.txRunner = new TxRunner(web3Runner, {})
this.networkcallid = 0 this.networkcallid = 0
@ -193,30 +199,47 @@ export class Blockchain extends Plugin {
deployContractAndLibraries(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { deployContractAndLibraries(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) {
const {continueCb, promptCb, statusCb, finalCb} = callbacks const {continueCb, promptCb, statusCb, finalCb} = callbacks
const constructor = selectedContract.getConstructorInterface() const constructor = selectedContract.getConstructorInterface()
txFormat.buildData(selectedContract.name, selectedContract.object, compilerContracts, true, constructor, args, (error, data) => { txFormat.buildData(
selectedContract.name,
selectedContract.object,
compilerContracts,
true,
constructor,
args,
(error, data) => {
if (error) { if (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
} }
statusCb(`creation of ${selectedContract.name} pending...`) statusCb(`creation of ${selectedContract.name} pending...`)
this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb)
}, statusCb, (data, runTxCallback) => { },
statusCb,
(data, runTxCallback) => {
// called for libraries deployment // called for libraries deployment
this.runTx(data, confirmationCb, continueCb, promptCb, runTxCallback) this.runTx(data, confirmationCb, continueCb, promptCb, runTxCallback)
}) }
)
} }
deployContractWithLibrary(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) { deployContractWithLibrary(selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) {
const {continueCb, promptCb, statusCb, finalCb} = callbacks const {continueCb, promptCb, statusCb, finalCb} = callbacks
const constructor = selectedContract.getConstructorInterface() const constructor = selectedContract.getConstructorInterface()
txFormat.encodeConstructorCallAndLinkLibraries(selectedContract.object, args, constructor, contractMetadata.linkReferences, selectedContract.bytecodeLinkReferences, (error, data) => { txFormat.encodeConstructorCallAndLinkLibraries(
selectedContract.object,
args,
constructor,
contractMetadata.linkReferences,
selectedContract.bytecodeLinkReferences,
(error, data) => {
if (error) { if (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
} }
statusCb(`creation of ${selectedContract.name} pending...`) statusCb(`creation of ${selectedContract.name} pending...`)
this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb)
}) }
)
} }
async deployProxy(proxyData, implementationContractObject) { async deployProxy(proxyData, implementationContractObject) {
@ -249,8 +272,12 @@ export class Blockchain extends Plugin {
// continue using original authorization given by user // continue using original authorization given by user
continueTxExecution(null) continueTxExecution(null)
} }
const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } const continueCb = (error, continueTxExecution, cancelCb) => {
const promptCb = (okCb, cancelCb) => { okCb() } continueTxExecution()
}
const promptCb = (okCb, cancelCb) => {
okCb()
}
const finalCb = async (error, txResult, address, returnValue) => { const finalCb = async (error, txResult, address, returnValue) => {
if (error) { if (error) {
const log = logBuilder(error) const log = logBuilder(error)
@ -296,8 +323,12 @@ export class Blockchain extends Plugin {
networkInfo = network networkInfo = network
continueTxExecution(null) continueTxExecution(null)
} }
const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() } const continueCb = (error, continueTxExecution, cancelCb) => {
const promptCb = (okCb, cancelCb) => { okCb() } continueTxExecution()
}
const promptCb = (okCb, cancelCb) => {
okCb()
}
const finalCb = async (error, txResult, address, returnValue) => { const finalCb = async (error, txResult, address, returnValue) => {
if (error) { if (error) {
const log = logBuilder(error) const log = logBuilder(error)
@ -336,17 +367,40 @@ export class Blockchain extends Plugin {
solcOutput: contractObject.compiler.data, solcOutput: contractObject.compiler.data,
solcInput: contractObject.compiler.source solcInput: contractObject.compiler.source
} }
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ await this.call(
'fileManager',
'writeFile',
`.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`,
JSON.stringify(
{
solcInput: contractObject.compiler.source, solcInput: contractObject.compiler.source,
solcOutput: contractObject.compiler.data solcOutput: contractObject.compiler.data
}, null, 2)) },
null,
2
)
)
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2)) await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2))
} else { } else {
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({ await this.call(
'fileManager',
'writeFile',
`.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`,
JSON.stringify(
{
solcInput: contractObject.compiler.source, solcInput: contractObject.compiler.source,
solcOutput: contractObject.compiler.data solcOutput: contractObject.compiler.data
}, null, 2)) },
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify({ null,
2
)
)
await this.call(
'fileManager',
'writeFile',
`.deploys/upgradeable-contracts/${networkName}/UUPS.json`,
JSON.stringify(
{
id: networkInfo.id, id: networkInfo.id,
network: networkInfo.name, network: networkInfo.name,
deployments: { deployments: {
@ -357,7 +411,11 @@ export class Blockchain extends Plugin {
implementationAddress: implementationAddress implementationAddress: implementationAddress
} }
} }
}, null, 2)) },
null,
2
)
)
} }
} }
@ -386,8 +444,7 @@ export class Blockchain extends Plugin {
data.contractABI = selectedContract.abi data.contractABI = selectedContract.abi
} }
this.runTx({ data: data, useCall: false }, confirmationCb, continueCb, promptCb, this.runTx({data: data, useCall: false}, confirmationCb, continueCb, promptCb, (error, txResult, address) => {
(error, txResult, address) => {
if (error) { if (error) {
return finalCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`) return finalCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
} }
@ -395,8 +452,7 @@ export class Blockchain extends Plugin {
return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`) return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`)
} }
finalCb(null, selectedContract, address) finalCb(null, selectedContract, address)
} })
)
} }
determineGasPrice(cb) { determineGasPrice(cb) {
@ -482,7 +538,7 @@ export class Blockchain extends Plugin {
isWeb3Provider() { isWeb3Provider() {
const isVM = this.executionContext.isVM() const isVM = this.executionContext.isVM()
const isInjected = this.getProvider() === 'injected' const isInjected = this.getProvider() === 'injected'
return (!isVM && !isInjected) return !isVM && !isInjected
} }
isInjectedWeb3() { isInjectedWeb3() {
@ -517,7 +573,14 @@ export class Blockchain extends Plugin {
runOrCallContractMethod(contractName, contractAbi, funABI, contract, value, address, callType, lookupOnly, logMsg, logCallback, outputCb, confirmationCb, continueCb, promptCb) { runOrCallContractMethod(contractName, contractAbi, funABI, contract, value, address, callType, lookupOnly, logMsg, logCallback, outputCb, confirmationCb, continueCb, promptCb) {
// contractsDetails is used to resolve libraries // contractsDetails is used to resolve libraries
txFormat.buildData(contractName, contractAbi, {}, false, funABI, callType, (error, data) => { txFormat.buildData(
contractName,
contractAbi,
{},
false,
funABI,
callType,
(error, data) => {
if (error) { if (error) {
return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`) return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`)
} }
@ -548,12 +611,15 @@ export class Blockchain extends Plugin {
}, },
(data, runTxCallback) => { (data, runTxCallback) => {
// called for libraries deployment // called for libraries deployment
this.runTx(data, confirmationCb, runTxCallback, promptCb, () => { /* Do nothing. */ }) this.runTx(data, confirmationCb, runTxCallback, promptCb, () => {
/* Do nothing. */
}) })
} }
)
}
context() { context() {
return (this.executionContext.isVM() ? 'memory' : 'blockchain') return this.executionContext.isVM() ? 'memory' : 'blockchain'
} }
// NOTE: the config is only needed because exectuionContext.init does // NOTE: the config is only needed because exectuionContext.init does
@ -583,16 +649,29 @@ export class Blockchain extends Plugin {
async resetEnvironment() { async resetEnvironment() {
await this.getCurrentProvider().resetEnvironment() await this.getCurrentProvider().resetEnvironment()
// TODO: most params here can be refactored away in txRunner // TODO: most params here can be refactored away in txRunner
const web3Runner = new TxRunnerWeb3({ const web3Runner = new TxRunnerWeb3(
{
config: this.config, config: this.config,
detectNetwork: (cb) => { detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb) this.executionContext.detectNetwork(cb)
}, },
isVM: () => { return this.executionContext.isVM() }, isVM: () => {
return this.executionContext.isVM()
},
personalMode: () => { personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
} }
}, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit()) },
// isVM: () => { return this.executionContext.isVM() },
// personalMode: () => {
// return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
// }
// }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit())
(_) => this.executionContext.web3(),
(_) => this.executionContext.currentblockGasLimit()
)
web3Runner.event.register('transactionBroadcasted', (txhash) => { web3Runner.event.register('transactionBroadcasted', (txhash) => {
this.executionContext.detectNetwork((error, network) => { this.executionContext.detectNetwork((error, network) => {
@ -601,10 +680,13 @@ export class Blockchain extends Plugin {
const viewEtherScanLink = etherScanLink(network.name, txhash) const viewEtherScanLink = etherScanLink(network.name, txhash)
if (viewEtherScanLink) { if (viewEtherScanLink) {
this.call('terminal', 'logHtml', this.call(
(<a href={etherScanLink(network.name, txhash)} target="_blank"> 'terminal',
'logHtml',
<a href={etherScanLink(network.name, txhash)} target="_blank">
view on etherscan view on etherscan
</a>)) </a>
)
} }
}) })
}) })
@ -659,17 +741,26 @@ export class Blockchain extends Plugin {
this.txRunner.rawRun( this.txRunner.rawRun(
tx, tx,
(network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() }, (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
(error, continueTxExecution, cancelCb) => { if (error) { reject(error) } else { continueTxExecution() } }, continueTxExecution()
(okCb, cancelCb) => { okCb() }, },
(error, continueTxExecution, cancelCb) => {
if (error) {
reject(error)
} else {
continueTxExecution()
}
},
(okCb, cancelCb) => {
okCb()
},
async (error, result) => { async (error, result) => {
if (error) return reject(error) if (error) return reject(error)
try { try {
if (this.executionContext.isVM()) { if (this.executionContext.isVM()) {
const execResult = await this.web3().testPlugin.getExecutionResultFromSimulator(result.transactionHash) const execResult = await this.web3().testPlugin.getExecutionResultFromSimulator(result.transactionHash)
resolve(resultToRemixTx(result, execResult)) resolve(resultToRemixTx(result, execResult))
} else } else resolve(resultToRemixTx(result))
resolve(resultToRemixTx(result))
} catch (e) { } catch (e) {
reject(e) reject(e)
} }
@ -713,7 +804,10 @@ export class Blockchain extends Plugin {
if (this.transactionContextAPI.getAddress) { if (this.transactionContextAPI.getAddress) {
return this.transactionContextAPI.getAddress(function (err, address) { return this.transactionContextAPI.getAddress(function (err, address) {
if (err) return reject(err) if (err) return reject(err)
if (!address) return reject('"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.') if (!address)
return reject(
'"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.'
)
return resolve(address) return resolve(address)
}) })
} }
@ -744,21 +838,39 @@ export class Blockchain extends Plugin {
return return
} }
const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp } const tx = {
const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences } to: args.to,
data: args.data.dataHex,
useCall: args.useCall,
from: fromAddress,
value: value,
gasLimit: gasLimit,
timestamp: args.data.timestamp
}
const payLoad = {
funAbi: args.data.funAbi,
funArgs: args.data.funArgs,
contractBytecode: args.data.contractBytecode,
contractName: args.data.contractName,
contractABI: args.data.contractABI,
linkReferences: args.data.linkReferences
}
if (!tx.timestamp) tx.timestamp = Date.now() if (!tx.timestamp) tx.timestamp = Date.now()
const timestamp = tx.timestamp const timestamp = tx.timestamp
this._triggerEvent('initiatingTransaction', [timestamp, tx, payLoad]) this._triggerEvent('initiatingTransaction', [timestamp, tx, payLoad])
try { try {
this.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, this.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, async (error, result) => {
async (error, result) => {
if (error) { if (error) {
if (typeof (error) !== 'string') { if (typeof error !== 'string') {
if (error.message) error = error.message if (error.message) error = error.message
else { else {
try { error = 'error: ' + JSON.stringify(error) } catch (e) { console.log(e) } try {
error = 'error: ' + JSON.stringify(error)
} catch (e) {
console.log(e)
}
} }
} }
return reject(error) return reject(error)
@ -767,23 +879,26 @@ export class Blockchain extends Plugin {
const isVM = this.executionContext.isVM() const isVM = this.executionContext.isVM()
if (isVM && tx.useCall) { if (isVM && tx.useCall) {
try { try {
result.transactionHash = await this.web3().testPlugin.getHashFromTagBySimulator(timestamp) result.transactionHash = await this.web3().eth.getHashFromTagBySimulator(timestamp)
} catch (e) { } catch (e) {
console.log('unable to retrieve back the "call" hash', e) console.log('unable to retrieve back the "call" hash', e)
} }
} }
const eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted') const eventName = tx.useCall ? 'callExecuted' : 'transactionExecuted'
this._triggerEvent(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad]) this._triggerEvent(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad])
return resolve({result, tx}) return resolve({result, tx})
} })
)
} catch (err) { } catch (err) {
let error = err let error = err
if (error && (typeof (error) !== 'string')) { if (error && typeof error !== 'string') {
if (error.message) error = error.message if (error.message) error = error.message
else { else {
try { error = 'error: ' + JSON.stringify(error) } catch (e) { console.log(e) } try {
error = 'error: ' + JSON.stringify(error)
} catch (e) {
console.log(e)
}
} }
} }
return reject(error) return reject(error)
@ -808,9 +923,12 @@ export class Blockchain extends Plugin {
const hhlogs = await this.web3().testPlugin.getHHLogsForTx(txResult.transactionHash) const hhlogs = await this.web3().testPlugin.getHHLogsForTx(txResult.transactionHash)
if (hhlogs && hhlogs.length) { if (hhlogs && hhlogs.length) {
const finalLogs = <div><div><b>console.log:</b></div> const finalLogs = (
{ <div>
hhlogs.map((log) => { <div>
<b>console.log:</b>
</div>
{hhlogs.map((log) => {
let formattedLog let formattedLog
// Hardhat implements the same formatting options that can be found in Node.js' console.log, // Hardhat implements the same formatting options that can be found in Node.js' console.log,
// which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args // which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args
@ -824,13 +942,16 @@ export class Blockchain extends Plugin {
return <div>{formattedLog}</div> return <div>{formattedLog}</div>
})} })}
</div> </div>
)
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log']) _paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'logHtml', finalLogs) this.call('terminal', 'logHtml', finalLogs)
} }
execResult = await this.web3().testPlugin.getExecutionResultFromSimulator(txResult.transactionHash) execResult = await this.web3().testPlugin.getExecutionResultFromSimulator(txResult.transactionHash)
if (execResult) { if (execResult) {
// if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value. // if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value.
returnValue = execResult ? toBuffer(execResult.returnValue) : toBuffer(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000') returnValue = execResult
? toBuffer(execResult.returnValue)
: toBuffer(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000')
const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas') const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas')
const vmError = txExecution.checkVMError(execResult, compiledContracts) const vmError = txExecution.checkVMError(execResult, compiledContracts)
if (vmError.error) { if (vmError.error) {

@ -7,11 +7,10 @@ import { Preload } from './app/components/preload'
import Config from './config' import Config from './config'
import Registry from './app/state/registry' import Registry from './app/state/registry'
import {Storage} from '@remix-project/remix-lib' import {Storage} from '@remix-project/remix-lib'
;(async function () {
(async function () {
try { try {
const configStorage = new Storage('config-v0.8:') const configStorage = new Storage('config-v0.8:')
const config = new Config(configStorage); const config = new Config(configStorage)
Registry.getInstance().put({api: config, name: 'config'}) Registry.getInstance().put({api: config, name: 'config'})
} catch (e) {} } catch (e) {}
const theme = new ThemeModule() const theme = new ThemeModule()
@ -24,5 +23,3 @@ import { Storage } from '@remix-project/remix-lib'
document.getElementById('root') document.getElementById('root')
) )
})() })()

@ -1,11 +1,11 @@
const {composePlugins, withNx} = require('@nrwl/webpack') const {composePlugins, withNx} = require('@nrwl/webpack')
const {withReact} = require('@nrwl/react') const {withReact} = require('@nrwl/react')
const webpack = require('webpack') const webpack = require('webpack')
const CopyPlugin = require("copy-webpack-plugin") const CopyPlugin = require('copy-webpack-plugin')
const version = require('../../package.json').version const version = require('../../package.json').version
const fs = require('fs') const fs = require('fs')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const axios = require('axios') const axios = require('axios')
const versionData = { const versionData = {
@ -33,8 +33,7 @@ const copyPatterns = implicitDependencies.map((dep) => {
try { try {
fs.statSync(__dirname + `/../../dist/apps/${dep}`).isDirectory() fs.statSync(__dirname + `/../../dist/apps/${dep}`).isDirectory()
return {from: `../../dist/apps/${dep}`, to: `plugins/${dep}`} return {from: `../../dist/apps/${dep}`, to: `plugins/${dep}`}
} } catch (e) {
catch (e) {
console.log('error', e) console.log('error', e)
return false return false
} }
@ -50,30 +49,29 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// add fallback for node modules // add fallback for node modules
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"), crypto: require.resolve('crypto-browserify'),
"stream": require.resolve("stream-browserify"), stream: require.resolve('stream-browserify'),
"path": require.resolve("path-browserify"), path: require.resolve('path-browserify'),
"http": require.resolve("stream-http"), http: require.resolve('stream-http'),
"https": require.resolve("https-browserify"), https: require.resolve('https-browserify'),
"constants": require.resolve("constants-browserify"), constants: require.resolve('constants-browserify'),
"os": false, //require.resolve("os-browserify/browser"), os: false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"), timers: false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"), zlib: require.resolve('browserify-zlib'),
"fs": false, fs: false,
"module": false, module: false,
"tls": false, tls: false,
"net": false, net: false,
"readline": false, readline: false,
"child_process": false, child_process: false,
"buffer": require.resolve("buffer/"), buffer: require.resolve('buffer/'),
"vm": require.resolve('vm-browserify'), vm: require.resolve('vm-browserify')
} }
// add externals // add externals
config.externals = { config.externals = {
...config.externals, ...config.externals,
solc: 'solc', solc: 'solc'
} }
// add public path // add public path
@ -83,12 +81,14 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
config.output.filename = `[name].${versionData.version}.${versionData.timestamp}.js` config.output.filename = `[name].${versionData.version}.${versionData.timestamp}.js`
config.output.chunkFilename = `[name].${versionData.version}.${versionData.timestamp}.js` config.output.chunkFilename = `[name].${versionData.version}.${versionData.timestamp}.js`
// add copy & provide plugin // add copy & provide plugin
config.plugins.push( config.plugins.push(
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ from: '../../node_modules/monaco-editor/min/vs', to: 'assets/js/monaco-editor/min/vs' }, {
from: '../../node_modules/monaco-editor/min/vs',
to: 'assets/js/monaco-editor/min/vs'
},
...copyPatterns ...copyPatterns
].filter(Boolean) ].filter(Boolean)
}), }),
@ -96,15 +96,15 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}) })
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/, /require function/] // ignore source-map-loader warnings & AST warnings config.ignoreWarnings = [/Failed to parse source map/, /require function/] // ignore source-map-loader warnings & AST warnings
@ -118,20 +118,20 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
config.watchOptions = { config.watchOptions = {
ignored: /node_modules/ ignored: /node_modules/
} }
return config; return config
}); })
class CopyFileAfterBuild { class CopyFileAfterBuild {
apply(compiler) { apply(compiler) {
@ -142,7 +142,7 @@ class CopyFileAfterBuild {
// This is needed because by default the etherscan resources are served from the /plugins/etherscan/ folder, // This is needed because by default the etherscan resources are served from the /plugins/etherscan/ folder,
// but the raw-loader try to access the resources from the root folder. // but the raw-loader try to access the resources from the root folder.
const files = fs.readdirSync('./dist/apps/etherscan') const files = fs.readdirSync('./dist/apps/etherscan')
files.forEach(file => { files.forEach((file) => {
if (file.includes('plugin-etherscan')) { if (file.includes('plugin-etherscan')) {
fs.copyFileSync('./dist/apps/etherscan/' + file, './dist/apps/remix-ide/' + file) fs.copyFileSync('./dist/apps/etherscan/' + file, './dist/apps/remix-ide/' + file)
} }
@ -151,8 +151,6 @@ class CopyFileAfterBuild {
console.error('running CopyFileAfterBuild failed with error: ' + e.message) console.error('running CopyFileAfterBuild failed with error: ' + e.message)
} }
} }
compiler.hooks.afterEmit.tapPromise('FileManagerPlugin', onEnd); compiler.hooks.afterEmit.tapPromise('FileManagerPlugin', onEnd)
} }
} }

@ -1,8 +1,8 @@
import React, { useEffect, useState } from "react"; import React, {useEffect, useState} from 'react'
import { SolHint } from "./SolhintPluginClient"; import {SolHint} from './SolhintPluginClient'
const client = new SolHint(); const client = new SolHint()
export default function App() { export default function App() {
return <></>; return <></>
} }

@ -1,18 +1,18 @@
const {composePlugins, withNx} = require('@nrwl/webpack') const {composePlugins, withNx} = require('@nrwl/webpack')
const {withReact} = require('@nrwl/react') const {withReact} = require('@nrwl/react')
const webpack = require('webpack') const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack. // Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => { module.exports = composePlugins(withNx(), withReact(), (config) => {
// Update the webpack config as needed here. // Update the webpack config as needed here.
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"path": false, path: false,
"os": false, os: false,
"fs": false, fs: false,
"module": false, module: false
} }
// add public path // add public path
@ -23,18 +23,18 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}), }),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
"BROWSER": true, BROWSER: true
}), })
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
@ -48,13 +48,13 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
return config; return config
}) })

@ -3,8 +3,8 @@ const { withReact } = require('@nrwl/react')
const webpack = require('webpack') const webpack = require('webpack')
const version = require('../../package.json').version const version = require('../../package.json').version
const fs = require('fs') const fs = require('fs')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const versionData = { const versionData = {
version: version, version: version,
@ -22,56 +22,52 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// add fallback for node modules // add fallback for node modules
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"), crypto: require.resolve('crypto-browserify'),
"stream": require.resolve("stream-browserify"), stream: require.resolve('stream-browserify'),
"path": require.resolve("path-browserify"), path: require.resolve('path-browserify'),
"http": require.resolve("stream-http"), http: require.resolve('stream-http'),
"https": require.resolve("https-browserify"), https: require.resolve('https-browserify'),
"constants": require.resolve("constants-browserify"), constants: require.resolve('constants-browserify'),
"os": false, //require.resolve("os-browserify/browser"), os: false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"), timers: false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"), zlib: require.resolve('browserify-zlib'),
"fs": false, fs: false,
"module": false, module: false,
"tls": false, tls: false,
"net": false, net: false,
"readline": false, readline: false,
"child_process": false, child_process: false,
"buffer": require.resolve("buffer/"), buffer: require.resolve('buffer/'),
"vm": require.resolve('vm-browserify'), vm: require.resolve('vm-browserify')
} }
// add externals // add externals
config.externals = { config.externals = {
...config.externals, ...config.externals,
solc: 'solc', solc: 'solc'
} }
// add public path // add public path
config.output.publicPath = '/' config.output.publicPath = '/'
// add copy & provide plugin // add copy & provide plugin
config.plugins.push( config.plugins.push(
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}) })
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
// set minimizer // set minimizer
config.optimization.minimizer = [ config.optimization.minimizer = [
new TerserPlugin({ new TerserPlugin({
@ -81,17 +77,17 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
config.watchOptions = { config.watchOptions = {
ignored: /node_modules/ ignored: /node_modules/
} }
return config; return config
}); })

@ -38,7 +38,7 @@ const App: React.FC = () => {
async function start() { async function start() {
try { try {
await remixClient.loaded() await remixClient.loaded()
remixClient.onFileChange(name => setContract(name)) remixClient.onFileChange((name) => setContract(name))
remixClient.onNoFileSelected(() => setContract('')) remixClient.onNoFileSelected(() => setContract(''))
} catch (err) { } catch (err) {
console.log(err) console.log(err)
@ -61,9 +61,7 @@ const App: React.FC = () => {
} }
function compilerUrl() { function compilerUrl() {
return state.environment === 'remote' return state.environment === 'remote' ? 'https://vyper.remixproject.org/compile' : state.localUrl
? 'https://vyper.remixproject.org/compile'
: state.localUrl
} }
return ( return (
@ -73,11 +71,7 @@ const App: React.FC = () => {
<img src={'assets/logo.svg'} alt="Vyper logo" /> <img src={'assets/logo.svg'} alt="Vyper logo" />
<h4>yper Compiler</h4> <h4>yper Compiler</h4>
</div> </div>
<a <a rel="noopener noreferrer" href="https://github.com/ethereum/remix-project/tree/master/apps/vyper" target="_blank">
rel="noopener noreferrer"
href="https://github.com/ethereum/remix-project/tree/master/apps/vyper"
target="_blank"
>
<i className="fab fa-github"></i> <i className="fab fa-github"></i>
</a> </a>
</header> </header>
@ -87,12 +81,7 @@ const App: React.FC = () => {
Clone Vyper examples repository Clone Vyper examples repository
</Button> </Button>
</div> </div>
<ToggleButtonGroup <ToggleButtonGroup name="remote" onChange={setEnvironment} type="radio" value={state.environment}>
name="remote"
onChange={setEnvironment}
type="radio"
value={state.environment}
>
<ToggleButton data-id="remote-compiler" variant="secondary" name="remote" value="remote"> <ToggleButton data-id="remote-compiler" variant="secondary" name="remote" value="remote">
Remote Compiler v0.2.16 Remote Compiler v0.2.16
</ToggleButton> </ToggleButton>
@ -100,20 +89,10 @@ const App: React.FC = () => {
Local Compiler Local Compiler
</ToggleButton> </ToggleButton>
</ToggleButtonGroup> </ToggleButtonGroup>
<LocalUrlInput <LocalUrlInput url={state.localUrl} setUrl={setLocalUrl} environment={state.environment} />
url={state.localUrl}
setUrl={setLocalUrl}
environment={state.environment}
/>
<WarnRemote environment={state.environment} /> <WarnRemote environment={state.environment} />
<div className="px-4" id="compile-btn"> <div className="px-4" id="compile-btn">
<CompilerButton <CompilerButton compilerUrl={compilerUrl()} contract={contract} setOutput={(name, update) => setOutput({...output, [name]: update})} />
compilerUrl={compilerUrl()}
contract={contract}
setOutput={(name, update) =>
setOutput({ ...output, [name]: update })
}
/>
</div> </div>
<article id="result" className="px-2"> <article id="result" className="px-2">
<VyperResult output={contract ? output[contract] : undefined} /> <VyperResult output={contract ? output[contract] : undefined} />

@ -1,22 +1,14 @@
import React from 'react' import React from 'react'
import { import {isVyper, compile, toStandardOutput, VyperCompilationOutput, isCompilationError, remixClient} from '../utils'
isVyper,
compile,
toStandardOutput,
VyperCompilationOutput,
isCompilationError,
remixClient
} from '../utils'
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button'
interface Props { interface Props {
compilerUrl: string compilerUrl: string
contract?: string, contract?: string
setOutput: (name: string, output: VyperCompilationOutput) => void setOutput: (name: string, output: VyperCompilationOutput) => void
} }
function CompilerButton({contract, setOutput, compilerUrl}: Props) { function CompilerButton({contract, setOutput, compilerUrl}: Props) {
if (!contract || !contract) { if (!contract || !contract) {
return <Button disabled>No contract selected</Button> return <Button disabled>No contract selected</Button>
} }

@ -3,12 +3,11 @@ import Form from 'react-bootstrap/Form'
interface Props { interface Props {
url: string url: string
setUrl: (url: string) => void, setUrl: (url: string) => void
environment: 'remote' | 'local' environment: 'remote' | 'local'
} }
function LocalUrlInput({url, setUrl, environment}: Props) { function LocalUrlInput({url, setUrl, environment}: Props) {
if (environment === 'remote') { if (environment === 'remote') {
return <></> return <></>
} }
@ -20,17 +19,13 @@ function LocalUrlInput({ url, setUrl, environment }: Props) {
return ( return (
<Form id="local-url"> <Form id="local-url">
<Form.Group controlId="localUrl"> <Form.Group controlId="localUrl">
<Form.Text className="text-warning pb-2">Currently we support vyper version > 0.2.16</Form.Text> <Form.Text className="text-warning pb-2">{'Currently we support vyper version > 0.2.16'}</Form.Text>
<Form.Label>Local Compiler Url</Form.Label> <Form.Label>Local Compiler Url</Form.Label>
<Form.Control onBlur={updateUrl} <Form.Control onBlur={updateUrl} defaultValue={url} type="email" placeholder="eg http://localhost:8000/compile" />
defaultValue={url} <Form.Text className="text-muted"></Form.Text>
type="email"
placeholder="eg http://localhost:8000/compile" />
<Form.Text className="text-muted">
</Form.Text>
</Form.Group> </Form.Group>
</Form> </Form>
) )
} }
export default LocalUrlInput; export default LocalUrlInput

@ -1,29 +1,25 @@
import React, { useState } from 'react'; import React, {useState} from 'react'
import { import {VyperCompilationResult, VyperCompilationOutput, isCompilationError} from '../utils'
VyperCompilationResult,
VyperCompilationOutput,
isCompilationError,
} from '../utils';
import Tabs from 'react-bootstrap/Tabs' import Tabs from 'react-bootstrap/Tabs'
import Tab from 'react-bootstrap/Tab' import Tab from 'react-bootstrap/Tab'
import Button from 'react-bootstrap/Button'; import Button from 'react-bootstrap/Button'
import JSONTree from 'react-json-view' import JSONTree from 'react-json-view'
import {CopyToClipboard} from '@remix-ui/clipboard' import {CopyToClipboard} from '@remix-ui/clipboard'
interface VyperResultProps { interface VyperResultProps {
output?: VyperCompilationOutput; output?: VyperCompilationOutput
} }
export type ExampleContract = { export type ExampleContract = {
name: string, name: string
address: string address: string
} }
function VyperResult({output}: VyperResultProps) { function VyperResult({output}: VyperResultProps) {
const [active, setActive] = useState<keyof VyperCompilationResult>('abi') const [active, setActive] = useState<keyof VyperCompilationResult>('abi')
if (!output) return ( if (!output)
return (
<div id="result"> <div id="result">
<p className="my-3">No contract compiled yet.</p> <p className="my-3">No contract compiled yet.</p>
</div> </div>
@ -33,11 +29,17 @@ function VyperResult({ output }: VyperResultProps) {
return ( return (
<div id="result" className="error" title={output.message}> <div id="result" className="error" title={output.message}>
<i className="fas fa-exclamation-circle text-danger"></i> <i className="fas fa-exclamation-circle text-danger"></i>
<pre data-id="error-message" className="px-2 w-100 alert alert-danger" style={{ <pre
fontSize: "0.5rem", data-id="error-message"
overflowX: "hidden", className="px-2 w-100 alert alert-danger"
textOverflow: "ellipsis" style={{
}}>{output.message}</pre> fontSize: '0.5rem',
overflowX: 'hidden',
textOverflow: 'ellipsis'
}}
>
{output.message}
</pre>
</div> </div>
) )
} }
@ -46,30 +48,38 @@ function VyperResult({ output }: VyperResultProps) {
<Tabs id="result" activeKey={active} onSelect={(key: any) => setActive(key)}> <Tabs id="result" activeKey={active} onSelect={(key: any) => setActive(key)}>
<Tab eventKey="abi" title="ABI"> <Tab eventKey="abi" title="ABI">
<CopyToClipboard getContent={() => JSON.stringify(output.abi)}> <CopyToClipboard getContent={() => JSON.stringify(output.abi)}>
<Button variant="info" className="copy" data-id="copy-abi">Copy ABI</Button> <Button variant="info" className="copy" data-id="copy-abi">
Copy ABI
</Button>
</CopyToClipboard> </CopyToClipboard>
<JSONTree src={output.abi} /> <JSONTree src={output.abi} />
</Tab> </Tab>
<Tab eventKey="bytecode" title="Bytecode"> <Tab eventKey="bytecode" title="Bytecode">
<CopyToClipboard getContent={() => output.bytecode}> <CopyToClipboard getContent={() => output.bytecode}>
<Button variant="info" className="copy">Copy Bytecode</Button> <Button variant="info" className="copy">
Copy Bytecode
</Button>
</CopyToClipboard> </CopyToClipboard>
<textarea defaultValue={output.bytecode}></textarea> <textarea defaultValue={output.bytecode}></textarea>
</Tab> </Tab>
<Tab eventKey="bytecode_runtime" title="Runtime Bytecode"> <Tab eventKey="bytecode_runtime" title="Runtime Bytecode">
<CopyToClipboard getContent={() => output.bytecode_runtime}> <CopyToClipboard getContent={() => output.bytecode_runtime}>
<Button variant="info" className="copy">Copy Runtime Bytecode</Button> <Button variant="info" className="copy">
Copy Runtime Bytecode
</Button>
</CopyToClipboard> </CopyToClipboard>
<textarea defaultValue={output.bytecode_runtime}></textarea> <textarea defaultValue={output.bytecode_runtime}></textarea>
</Tab> </Tab>
<Tab eventKey="ir" title="LLL"> <Tab eventKey="ir" title="LLL">
<CopyToClipboard getContent={() => output.ir}> <CopyToClipboard getContent={() => output.ir}>
<Button variant="info" className="copy">Copy LLL Code</Button> <Button variant="info" className="copy">
Copy LLL Code
</Button>
</CopyToClipboard> </CopyToClipboard>
<textarea defaultValue={output.ir}></textarea> <textarea defaultValue={output.ir}></textarea>
</Tab> </Tab>
</Tabs> </Tabs>
); )
} }
export default VyperResult; export default VyperResult

@ -5,7 +5,6 @@ interface Props {
} }
function WarnRemoteLabel({environment}: Props) { function WarnRemoteLabel({environment}: Props) {
if (environment === 'local') { if (environment === 'local') {
return <></> return <></>
} }
@ -15,4 +14,4 @@ function WarnRemoteLabel({ environment }: Props) {
) )
} }
export default WarnRemoteLabel; export default WarnRemoteLabel

@ -1,16 +1,16 @@
import { CompilationResult, ABIDescription } from "@remixproject/plugin-api"; import {CompilationResult, ABIDescription} from '@remixproject/plugin-api'
export interface Contract { export interface Contract {
name: string; name: string
content: string; content: string
} }
export interface VyperCompilationResult { export interface VyperCompilationResult {
status: 'success', status: 'success'
bytecode: string, bytecode: string
bytecode_runtime: string, bytecode_runtime: string
abi: ABIDescription[], abi: ABIDescription[]
ir: string, ir: string
method_identifiers: { method_identifiers: {
[method: string]: string [method: string]: string
} }
@ -64,8 +64,8 @@ export async function compile(url: string, contract: Contract): Promise<VyperCom
* @param compilationResult Result returned by the compiler * @param compilationResult Result returned by the compiler
*/ */
export function toStandardOutput(fileName: string, compilationResult: VyperCompilationResult): CompilationResult { export function toStandardOutput(fileName: string, compilationResult: VyperCompilationResult): CompilationResult {
const contractName = fileName.split('/').slice(-1)[0].split('.')[0]; const contractName = fileName.split('/').slice(-1)[0].split('.')[0]
const methodIdentifiers = JSON.parse(JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g,'')); const methodIdentifiers = JSON.parse(JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g, ''))
return { return {
sources: { sources: {
[fileName]: { [fileName]: {
@ -85,21 +85,20 @@ export function toStandardOutput(fileName: string, compilationResult: VyperCompi
bytecode: { bytecode: {
linkReferences: {}, linkReferences: {},
object: compilationResult['bytecode'].replace('0x', ''), object: compilationResult['bytecode'].replace('0x', ''),
opcodes: "" opcodes: ''
}, },
deployedBytecode: { deployedBytecode: {
linkReferences: {}, linkReferences: {},
object: compilationResult['bytecode_runtime'].replace('0x', ''), object: compilationResult['bytecode_runtime'].replace('0x', ''),
opcodes: "" opcodes: ''
}, },
methodIdentifiers: methodIdentifiers methodIdentifiers: methodIdentifiers
} }
} }
} as any } as any
} }
};
} }
}
/* /*
export function createCompilationResultMessage(name: string, result: any) { export function createCompilationResultMessage(name: string, result: any) {

@ -1,12 +1,12 @@
import { HighlightPosition, CompilationResult, RemixApi } from '@remixproject/plugin-api'; import {HighlightPosition, CompilationResult, RemixApi} from '@remixproject/plugin-api'
import { Api, Status } from '@remixproject/plugin-utils'; import {Api, Status} from '@remixproject/plugin-utils'
import {createClient} from '@remixproject/plugin-webview' import {createClient} from '@remixproject/plugin-webview'
import { PluginClient } from '@remixproject/plugin'; import {PluginClient} from '@remixproject/plugin'
import { Contract } from './compiler'; import {Contract} from './compiler'
import { ExampleContract } from '../components/VyperResult'; import {ExampleContract} from '../components/VyperResult'
export class RemixClient extends PluginClient { export class RemixClient extends PluginClient {
private client = createClient<Api, Readonly<RemixApi>>(this); private client = createClient<Api, Readonly<RemixApi>>(this)
loaded() { loaded() {
return this.client.onload() return this.client.onload()
@ -42,10 +42,19 @@ export class RemixClient extends PluginClient {
// @ts-ignore // @ts-ignore
this.call('notification', 'toast', 'cloning Vyper repository...') this.call('notification', 'toast', 'cloning Vyper repository...')
await this.call('manager', 'activatePlugin', 'dGitProvider') await this.call('manager', 'activatePlugin', 'dGitProvider')
await this.call(
'dGitProvider',
'clone',
{url: 'https://github.com/vyperlang/vyper', token: null},
// @ts-ignore // @ts-ignore
await this.call('dGitProvider', 'clone', { url: 'https://github.com/vyperlang/vyper', token: null }, 'vyper-lang') 'vyper-lang'
)
this.call(
// @ts-ignore // @ts-ignore
this.call('notification', 'toast', 'Vyper repository cloned, the workspace Vyper has been created.') 'notification',
'toast',
'Vyper repository cloned, the workspace Vyper has been created.'
)
} catch (e) { } catch (e) {
// @ts-ignore // @ts-ignore
this.call('notification', 'toast', e.message) this.call('notification', 'toast', e.message)
@ -54,7 +63,7 @@ export class RemixClient extends PluginClient {
/** Update the status of the plugin in remix */ /** Update the status of the plugin in remix */
changeStatus(status: Status) { changeStatus(status: Status) {
this.client.emit('statusChanged', status); this.client.emit('statusChanged', status)
} }
/** Highlight a part of the editor */ /** Highlight a part of the editor */
@ -94,13 +103,13 @@ export class RemixClient extends PluginClient {
const content = await this.client.call('fileManager', 'getFile', name) const content = await this.client.call('fileManager', 'getFile', name)
return { return {
name, name,
content, content
} }
} }
/** Emit an event to Remix with compilation result */ /** Emit an event to Remix with compilation result */
compilationFinish(title: string, content: string, data: CompilationResult) { compilationFinish(title: string, content: string, data: CompilationResult) {
this.client.emit('compilationFinished', title, content, 'vyper', data); this.client.emit('compilationFinished', title, content, 'vyper', data)
} }
} }

@ -1,7 +1,11 @@
import { StrictMode } from 'react'; import {StrictMode} from 'react'
import * as ReactDOM from 'react-dom'; import * as ReactDOM from 'react-dom'
import App from './app/app'
import App from './app/app'; ReactDOM.render(
<StrictMode>
ReactDOM.render(<StrictMode><App /></StrictMode>, document.getElementById('root')); <App />
</StrictMode>,
document.getElementById('root')
)

@ -1,8 +1,7 @@
const {composePlugins, withNx} = require('@nrwl/webpack') const {composePlugins, withNx} = require('@nrwl/webpack')
const webpack = require('webpack') const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack. // Nx plugins for webpack.
module.exports = composePlugins(withNx(), (config) => { module.exports = composePlugins(withNx(), (config) => {
@ -12,56 +11,52 @@ module.exports = composePlugins(withNx(), (config) => {
// add fallback for node modules // add fallback for node modules
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"), crypto: require.resolve('crypto-browserify'),
"stream": require.resolve("stream-browserify"), stream: require.resolve('stream-browserify'),
"path": require.resolve("path-browserify"), path: require.resolve('path-browserify'),
"http": require.resolve("stream-http"), http: require.resolve('stream-http'),
"https": require.resolve("https-browserify"), https: require.resolve('https-browserify'),
"constants": require.resolve("constants-browserify"), constants: require.resolve('constants-browserify'),
"os": false, //require.resolve("os-browserify/browser"), os: false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"), timers: false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"), zlib: require.resolve('browserify-zlib'),
"fs": false, fs: false,
"module": false, module: false,
"tls": false, tls: false,
"net": false, net: false,
"readline": false, readline: false,
"child_process": false, child_process: false,
"buffer": require.resolve("buffer/"), buffer: require.resolve('buffer/'),
"vm": require.resolve('vm-browserify'), vm: require.resolve('vm-browserify')
} }
// add externals // add externals
config.externals = { config.externals = {
...config.externals, ...config.externals,
solc: 'solc', solc: 'solc'
} }
// add public path // add public path
config.output.publicPath = '/' config.output.publicPath = '/'
// add copy & provide plugin // add copy & provide plugin
config.plugins.push( config.plugins.push(
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}) })
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
// set minimizer // set minimizer
config.optimization.minimizer = [ config.optimization.minimizer = [
new TerserPlugin({ new TerserPlugin({
@ -71,17 +66,17 @@ module.exports = composePlugins(withNx(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
config.watchOptions = { config.watchOptions = {
ignored: /node_modules/ ignored: /node_modules/
} }
return config; return config
}); })

@ -13,7 +13,7 @@ function App() {
const [theme, setTheme] = useState<string>('dark') const [theme, setTheme] = useState<string>('dark')
useEffect(() => { useEffect(() => {
(async () => { ;(async () => {
await remix.initClient() await remix.initClient()
remix.internalEvents.on('themeChanged', (theme: string) => { remix.internalEvents.on('themeChanged', (theme: string) => {
setTheme(theme) setTheme(theme)
@ -26,7 +26,7 @@ function App() {
return ( return (
<div className="App"> <div className="App">
<h4 className='mt-1'>WalletConnect</h4> <h4 className="mt-1">WalletConnect</h4>
{ethereumClient && wagmiConfig && <WalletConnectUI wagmiConfig={wagmiConfig} ethereumClient={ethereumClient} theme={theme} />} {ethereumClient && wagmiConfig && <WalletConnectUI wagmiConfig={wagmiConfig} ethereumClient={ethereumClient} theme={theme} />}
</div> </div>
) )

@ -1,14 +1,13 @@
import { Web3Button, Web3Modal } from "@web3modal/react" import {Web3Button, Web3Modal} from '@web3modal/react'
import { WagmiConfig } from "wagmi" import {WagmiConfig} from 'wagmi'
import { PROJECT_ID } from "../services/constant" import {PROJECT_ID} from '../services/constant'
export function WalletConnectUI({ethereumClient, wagmiConfig, theme}) { export function WalletConnectUI({ethereumClient, wagmiConfig, theme}) {
return ( return (
<div> <div>
<div style={{display: 'inline-block'}}> <div style={{display: 'inline-block'}}>
<WagmiConfig config={wagmiConfig}> <WagmiConfig config={wagmiConfig}>
<Web3Button label='Connect to a wallet' /> <Web3Button label="Connect to a wallet" />
</WagmiConfig> </WagmiConfig>
</div> </div>
<Web3Modal projectId={PROJECT_ID} ethereumClient={ethereumClient} themeMode={theme} /> <Web3Modal projectId={PROJECT_ID} ethereumClient={ethereumClient} themeMode={theme} />

@ -2,7 +2,4 @@ import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import App from './app/app' import App from './app/app'
ReactDOM.render( ReactDOM.render(<App />, document.getElementById('root'))
<App />,
document.getElementById('root')
)

@ -1,7 +1,7 @@
const {composePlugins, withNx} = require('@nrwl/webpack') const {composePlugins, withNx} = require('@nrwl/webpack')
const webpack = require('webpack') const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin") const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack. // Nx plugins for webpack.
module.exports = composePlugins(withNx(), (config) => { module.exports = composePlugins(withNx(), (config) => {
@ -10,30 +10,29 @@ module.exports = composePlugins(withNx(), (config) => {
// add fallback for node modules // add fallback for node modules
config.resolve.fallback = { config.resolve.fallback = {
...config.resolve.fallback, ...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"), crypto: require.resolve('crypto-browserify'),
"stream": require.resolve("stream-browserify"), stream: require.resolve('stream-browserify'),
"path": require.resolve("path-browserify"), path: require.resolve('path-browserify'),
"http": require.resolve("stream-http"), http: require.resolve('stream-http'),
"https": require.resolve("https-browserify"), https: require.resolve('https-browserify'),
"constants": require.resolve("constants-browserify"), constants: require.resolve('constants-browserify'),
"os": false, //require.resolve("os-browserify/browser"), os: false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"), timers: false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"), zlib: require.resolve('browserify-zlib'),
"fs": false, fs: false,
"module": false, module: false,
"tls": false, tls: false,
"net": false, net: false,
"readline": false, readline: false,
"child_process": false, child_process: false,
"buffer": require.resolve("buffer/"), buffer: require.resolve('buffer/'),
"vm": require.resolve('vm-browserify'), vm: require.resolve('vm-browserify')
} }
// add externals // add externals
config.externals = { config.externals = {
...config.externals, ...config.externals,
solc: 'solc', solc: 'solc'
} }
// add public path // add public path
@ -44,27 +43,26 @@ module.exports = composePlugins(withNx(), (config) => {
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'], url: ['url', 'URL'],
process: 'process/browser', process: 'process/browser'
}) })
) )
// set the define plugin to load the WALLET_CONNECT_PROJECT_ID // set the define plugin to load the WALLET_CONNECT_PROJECT_ID
config.plugins.push( config.plugins.push(
new webpack.DefinePlugin({ new webpack.DefinePlugin({
WALLET_CONNECT_PROJECT_ID: JSON.stringify(process.env.WALLET_CONNECT_PROJECT_ID), WALLET_CONNECT_PROJECT_ID: JSON.stringify(process.env.WALLET_CONNECT_PROJECT_ID)
}) })
) )
// souce-map loader // souce-map loader
config.module.rules.push({ config.module.rules.push({
test: /\.js$/, test: /\.js$/,
use: ["source-map-loader"], use: ['source-map-loader'],
enforce: "pre" enforce: 'pre'
}) })
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
// set minimizer // set minimizer
config.optimization.minimizer = [ config.optimization.minimizer = [
new TerserPlugin({ new TerserPlugin({
@ -74,17 +72,17 @@ module.exports = composePlugins(withNx(), (config) => {
compress: false, compress: false,
mangle: false, mangle: false,
format: { format: {
comments: false, comments: false
}, }
}, },
extractComments: false, extractComments: false
}), }),
new CssMinimizerPlugin(), new CssMinimizerPlugin()
]; ]
config.watchOptions = { config.watchOptions = {
ignored: /node_modules/ ignored: /node_modules/
} }
return config; return config
}); })

@ -10,8 +10,7 @@ const shortFilename = 'simple_storage.sol'
const inputJson = { const inputJson = {
language: 'Solidity', language: 'Solidity',
sources: { sources: {},
},
settings: { settings: {
optimizer: { optimizer: {
enabled: true, enabled: true,
@ -26,7 +25,9 @@ const inputJson = {
} }
} }
inputJson.sources[shortFilename] = {content: fs.readFileSync(filename).toString()} inputJson.sources[shortFilename] = {
content: fs.readFileSync(filename).toString()
}
console.dir(inputJson) console.dir(inputJson)
@ -106,4 +107,3 @@ repl.start({
}) })
module.exports = cmdLine module.exports = cmdLine

@ -3,8 +3,8 @@ import Draggable from 'react-draggable'
import './dragbar.css' import './dragbar.css'
interface IRemixDragBarUi { interface IRemixDragBarUi {
refObject: React.MutableRefObject<any>; refObject: React.MutableRefObject<any>
setHideStatus: (hide: boolean) => void; setHideStatus: (hide: boolean) => void
hidden: boolean hidden: boolean
minWidth: number minWidth: number
maximiseTrigger: number maximiseTrigger: number
@ -53,8 +53,7 @@ const DragBar = (props: IRemixDragBarUi) => {
useEffect(() => { useEffect(() => {
window.addEventListener('resize', handleResize) window.addEventListener('resize', handleResize)
// TODO: not a good way to wait on the ref doms element to be rendered of course // TODO: not a good way to wait on the ref doms element to be rendered of course
setTimeout(() => setTimeout(() => handleResize(), 2000)
handleResize(), 2000)
return () => window.removeEventListener('resize', handleResize) return () => window.removeEventListener('resize', handleResize)
}, []) }, [])
@ -64,7 +63,7 @@ const DragBar = (props: IRemixDragBarUi) => {
setDragBarPosX(offset) setDragBarPosX(offset)
props.setHideStatus(true) props.setHideStatus(true)
} else { } else {
props.refObject.current.style.width = (data.x - offset) + 'px' props.refObject.current.style.width = data.x - offset + 'px'
setTimeout(() => { setTimeout(() => {
props.setHideStatus(false) props.setHideStatus(false)
setDragBarPosX(offset + props.refObject.current.offsetWidth) setDragBarPosX(offset + props.refObject.current.offsetWidth)
@ -75,12 +74,14 @@ const DragBar = (props: IRemixDragBarUi) => {
function startDrag() { function startDrag() {
setDragState(true) setDragState(true)
} }
return <> return (
<>
<div className={`overlay ${dragState ? '' : 'd-none'}`}></div> <div className={`overlay ${dragState ? '' : 'd-none'}`}></div>
<Draggable nodeRef={nodeRef} position={{x: dragBarPosX, y: 0}} onStart={startDrag} onStop={stopDrag} axis="x"> <Draggable nodeRef={nodeRef} position={{x: dragBarPosX, y: 0}} onStart={startDrag} onStop={stopDrag} axis="x">
<div ref={nodeRef} className={`dragbar ${dragState ? 'ondrag' : ''}`}></div> <div ref={nodeRef} className={`dragbar ${dragState ? 'ondrag' : ''}`}></div>
</Draggable> </Draggable>
</> </>
)
} }
export default DragBar export default DragBar

@ -11,6 +11,7 @@ const AppDialogs = () => {
<> <>
<ModalWrapper {...focusModal} handleHide={handleHideModal}></ModalWrapper> <ModalWrapper {...focusModal} handleHide={handleHideModal}></ModalWrapper>
<Toaster message={focusToaster.message} timestamp={focusToaster.timestamp} handleHide={handleToaster} /> <Toaster message={focusToaster.message} timestamp={focusToaster.timestamp} handleHide={handleToaster} />
</>) </>
)
} }
export default AppDialogs export default AppDialogs

@ -6,7 +6,7 @@ declare global {
_paq: any _paq: any
} }
} }
const _paq = window._paq = window._paq || [] const _paq = (window._paq = window._paq || [])
const MatomoDialog = (props) => { const MatomoDialog = (props) => {
const {settings, showMatamo, appManager} = useContext(AppContext) const {settings, showMatamo, appManager} = useContext(AppContext)
@ -14,17 +14,47 @@ const MatomoDialog = (props) => {
const [visible, setVisible] = useState<boolean>(props.hide) const [visible, setVisible] = useState<boolean>(props.hide)
const message = () => { const message = () => {
return (<><p>An Opt-in version of <a href="https://matomo.org" target="_blank" rel="noreferrer">Matomo</a>, an open source data analytics platform is being used to improve Remix IDE.</p> return (
<>
<p>
An Opt-in version of{' '}
<a href="https://matomo.org" target="_blank" rel="noreferrer">
Matomo
</a>
, an open source data analytics platform is being used to improve Remix IDE.
</p>
<p>We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.</p> <p>We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.</p>
<p>All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public: <a href="https://matomo.ethereum.org/index.php?module=MultiSites&action=index&idSite=23&period=day&date=yesterday" target="_blank" rel="noreferrer">take a look</a>.</p> <p>
All data collected through Matomo is stored on our own server - no data is ever given to third parties. Our analytics reports are public:{' '}
<a href="https://matomo.ethereum.org/index.php?module=MultiSites&action=index&idSite=23&period=day&date=yesterday" target="_blank" rel="noreferrer">
take a look
</a>
.
</p>
<p>We do not collect nor store any personally identifiable information (PII).</p> <p>We do not collect nor store any personally identifiable information (PII).</p>
<p>For more info, see: <a href="https://medium.com/p/66ef69e14931/" target="_blank" rel="noreferrer">Matomo Analyitcs on Remix iDE</a>.</p> <p>
<p>You can change your choice in the Settings panel anytime.</p></>) For more info, see:{' '}
<a href="https://medium.com/p/66ef69e14931/" target="_blank" rel="noreferrer">
Matomo Analyitcs on Remix iDE
</a>
.
</p>
<p>You can change your choice in the Settings panel anytime.</p>
</>
)
} }
useEffect(() => { useEffect(() => {
if (visible && showMatamo) { if (visible && showMatamo) {
modal({ id: 'matomoModal', title: 'Help us to improve Remix IDE', message: message(), okLabel: 'Accept', okFn: handleModalOkClick, cancelLabel: 'Decline', cancelFn: declineModal }) modal({
id: 'matomoModal',
title: 'Help us to improve Remix IDE',
message: message(),
okLabel: 'Accept',
okFn: handleModalOkClick,
cancelLabel: 'Decline',
cancelFn: declineModal
})
} }
}, [visible]) }, [visible])
@ -44,7 +74,7 @@ const MatomoDialog = (props) => {
setVisible(false) setVisible(false)
} }
return (<></>) return <></>
} }
export default MatomoDialog export default MatomoDialog

@ -28,26 +28,33 @@ const ModalWrapper = (props: ModalWrapperProps) => {
if (ref.current === undefined && formRef.current === undefined) { if (ref.current === undefined && formRef.current === undefined) {
onOkFn() onOkFn()
} else if (formRef.current) { } else if (formRef.current) {
(props.okFn) ? props.okFn(getFormData()) : props.resolve(getFormData()) props.okFn ? props.okFn(getFormData()) : props.resolve(getFormData())
} else if (ref.current) { } else if (ref.current) {
// @ts-ignore: Object is possibly 'null'. props.okFn
(props.okFn) ? props.okFn(ref.current.value) : props.resolve(ref.current.value) ? // @ts-ignore: Object is possibly 'null'.
props.okFn(ref.current.value)
: // @ts-ignore: Object is possibly 'null'.
props.resolve(ref.current.value)
} }
} }
const onOkFn = async () => { const onOkFn = async () => {
(props.okFn) ? props.okFn(data.current) : props.resolve(data.current || true) props.okFn ? props.okFn(data.current) : props.resolve(data.current || true)
} }
const onCancelFn = async () => { const onCancelFn = async () => {
(props.cancelFn) ? props.cancelFn() : props.resolve(false) props.cancelFn ? props.cancelFn() : props.resolve(false)
} }
const onInputChanged = (event) => { const onInputChanged = (event) => {
if (props.validationFn) { if (props.validationFn) {
const validation = props.validationFn(event.target.value) const validation = props.validationFn(event.target.value)
setState(prevState => { setState((prevState) => {
return { ...prevState, message: createModalMessage(props.defaultValue, validation), validation } return {
...prevState,
message: createModalMessage(props.defaultValue, validation),
validation
}
}) })
} }
} }
@ -56,8 +63,15 @@ const ModalWrapper = (props: ModalWrapperProps) => {
return ( return (
<> <>
{props.message} {props.message}
<input onChange={onInputChanged} type={props.modalType === ModalTypes.password ? 'password' : 'text'} defaultValue={defaultValue} data-id="modalDialogCustomPromp" ref={ref} className="form-control" /> <input
{validation && !validation.valid && <span className='text-warning'>{validation.message}</span>} onChange={onInputChanged}
type={props.modalType === ModalTypes.password ? 'password' : 'text'}
defaultValue={defaultValue}
data-id="modalDialogCustomPromp"
ref={ref}
className="form-control"
/>
{validation && !validation.valid && <span className="text-warning">{validation.message}</span>}
</> </>
) )
} }
@ -65,7 +79,7 @@ const ModalWrapper = (props: ModalWrapperProps) => {
const onFormChanged = () => { const onFormChanged = () => {
if (props.validationFn) { if (props.validationFn) {
const validation = props.validationFn(getFormData()) const validation = props.validationFn(getFormData())
setState(prevState => { setState((prevState) => {
return {...prevState, message: createForm(validation), validation} return {...prevState, message: createForm(validation), validation}
}) })
} }
@ -77,7 +91,7 @@ const ModalWrapper = (props: ModalWrapperProps) => {
<form onChange={onFormChanged} ref={formRef}> <form onChange={onFormChanged} ref={formRef}>
{props.message} {props.message}
</form> </form>
{validation && !validation.valid && <span className='text-warning'>{validation.message}</span>} {validation && !validation.valid && <span className="text-warning">{validation.message}</span>}
</> </>
) )
} }
@ -122,14 +136,12 @@ const ModalWrapper = (props: ModalWrapperProps) => {
// reset the message and input if any, so when the modal is shown again it doesn't show the previous value. // reset the message and input if any, so when the modal is shown again it doesn't show the previous value.
const handleHide = () => { const handleHide = () => {
setState(prevState => { setState((prevState) => {
return {...prevState, message: ''} return {...prevState, message: ''}
}) })
props.handleHide() props.handleHide()
} }
return ( return <ModalDialog id={props.id} {...state} handleHide={handleHide} />
<ModalDialog id={props.id} {...state} handleHide={handleHide} />
)
} }
export default ModalWrapper export default ModalWrapper

@ -10,13 +10,17 @@ const OriginWarning = () => {
// check the origin and warn message // check the origin and warn message
if (window.location.hostname === 'yann300.github.io') { if (window.location.hostname === 'yann300.github.io') {
setContent('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.') setContent('This UNSTABLE ALPHA branch of Remix has been moved to http://ethereum.github.io/remix-live-alpha.')
} else if (window.location.hostname === 'remix-alpha.ethereum.org' || } else if (
(window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)) { window.location.hostname === 'remix-alpha.ethereum.org' ||
(window.location.hostname === 'ethereum.github.io' && window.location.pathname.indexOf('/remix-live-alpha') === 0)
) {
setContent('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.') setContent('Welcome to the Remix alpha instance. Please use it to try out latest features. But use preferably https://remix.ethereum.org for any production work.')
} else if (window.location.protocol.indexOf('http') === 0 && } else if (
window.location.protocol.indexOf('http') === 0 &&
window.location.hostname !== 'remix.ethereum.org' && window.location.hostname !== 'remix.ethereum.org' &&
window.location.hostname !== 'localhost' && window.location.hostname !== 'localhost' &&
window.location.hostname !== '127.0.0.1') { window.location.hostname !== '127.0.0.1'
) {
setContent(`The Remix IDE has moved to http://remix.ethereum.org.\n setContent(`The Remix IDE has moved to http://remix.ethereum.org.\n
This instance of Remix you are visiting WILL NOT BE UPDATED.\n This instance of Remix you are visiting WILL NOT BE UPDATED.\n
Please make a backup of your contracts and start using http://remix.ethereum.org`) Please make a backup of your contracts and start using http://remix.ethereum.org`)
@ -29,7 +33,7 @@ const OriginWarning = () => {
} }
}, [content]) }, [content])
return (<></>) return <></>
} }
export default OriginWarning export default OriginWarning

@ -1,16 +1,19 @@
import React from 'react' import React from 'react'
const RemixSplashScreen = (props) => { const RemixSplashScreen = (props) => {
return (<> <div style={{ display: props.hide ? 'none' : 'block' }} className='centered'> return (
<>
{' '}
<div style={{display: props.hide ? 'none' : 'block'}} className="centered">
<svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100"> <svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100">
<path d="M91.84,35a.09.09,0,0,1-.1-.07,41,41,0,0,0-79.48,0,.09.09,0,0,1-.1.07C9.45,35,1,35.35,1,42.53c0,8.56,1,16,6,20.32,2.16,1.85,5.81,2.3,9.27,2.22a44.4,44.4,0,0,0,6.45-.68.09.09,0,0,0,.06-.15A34.81,34.81,0,0,1,17,45c0-.1,0-.21,0-.31a35,35,0,0,1,70,0c0,.1,0,.21,0,.31a34.81,34.81,0,0,1-5.78,19.24.09.09,0,0,0,.06.15,44.4,44.4,0,0,0,6.45.68c3.46.08,7.11-.37,9.27-2.22,5-4.27,6-11.76,6-20.32C103,35.35,94.55,35,91.84,35Z" /> <path d="M91.84,35a.09.09,0,0,1-.1-.07,41,41,0,0,0-79.48,0,.09.09,0,0,1-.1.07C9.45,35,1,35.35,1,42.53c0,8.56,1,16,6,20.32,2.16,1.85,5.81,2.3,9.27,2.22a44.4,44.4,0,0,0,6.45-.68.09.09,0,0,0,.06-.15A34.81,34.81,0,0,1,17,45c0-.1,0-.21,0-.31a35,35,0,0,1,70,0c0,.1,0,.21,0,.31a34.81,34.81,0,0,1-5.78,19.24.09.09,0,0,0,.06.15,44.4,44.4,0,0,0,6.45.68c3.46.08,7.11-.37,9.27-2.22,5-4.27,6-11.76,6-20.32C103,35.35,94.55,35,91.84,35Z" />
<path d="M52,74,25.4,65.13a.1.1,0,0,0-.1.17L51.93,91.93a.1.1,0,0,0,.14,0L78.7,65.3a.1.1,0,0,0-.1-.17L52,74A.06.06,0,0,1,52,74Z" /> <path d="M52,74,25.4,65.13a.1.1,0,0,0-.1.17L51.93,91.93a.1.1,0,0,0,.14,0L78.7,65.3a.1.1,0,0,0-.1-.17L52,74A.06.06,0,0,1,52,74Z" />
<path d="M75.68,46.9,82,45a.09.09,0,0,0,.08-.09,29.91,29.91,0,0,0-.87-6.94.11.11,0,0,0-.09-.08l-6.43-.58a.1.1,0,0,1-.06-.18l4.78-4.18a.13.13,0,0,0,0-.12,30.19,30.19,0,0,0-3.65-6.07.09.09,0,0,0-.11,0l-5.91,2a.1.1,0,0,1-.12-.14L72.19,23a.11.11,0,0,0,0-.12,29.86,29.86,0,0,0-5.84-4.13.09.09,0,0,0-.11,0l-4.47,4.13a.1.1,0,0,1-.17-.07l.09-6a.1.1,0,0,0-.07-.1,30.54,30.54,0,0,0-7-1.47.1.1,0,0,0-.1.07l-2.38,5.54a.1.1,0,0,1-.18,0l-2.37-5.54a.11.11,0,0,0-.11-.06,30,30,0,0,0-7,1.48.12.12,0,0,0-.07.1l.08,6.05a.09.09,0,0,1-.16.07L37.8,18.76a.11.11,0,0,0-.12,0,29.75,29.75,0,0,0-5.83,4.13.11.11,0,0,0,0,.12l2.59,5.6a.11.11,0,0,1-.13.14l-5.9-2a.11.11,0,0,0-.12,0,30.23,30.23,0,0,0-3.62,6.08.11.11,0,0,0,0,.12l4.79,4.19a.1.1,0,0,1-.06.17L23,37.91a.1.1,0,0,0-.09.07A29.9,29.9,0,0,0,22,44.92a.1.1,0,0,0,.07.1L28.4,47a.1.1,0,0,1,0,.18l-5.84,3.26a.16.16,0,0,0,0,.11,30.17,30.17,0,0,0,2.1,6.76c.32.71.67,1.4,1,2.08a.1.1,0,0,0,.06,0L52,68.16H52l26.34-8.78a.1.1,0,0,0,.06-.05,30.48,30.48,0,0,0,3.11-8.88.1.1,0,0,0-.05-.11l-5.83-3.26A.1.1,0,0,1,75.68,46.9Z" /> <path d="M75.68,46.9,82,45a.09.09,0,0,0,.08-.09,29.91,29.91,0,0,0-.87-6.94.11.11,0,0,0-.09-.08l-6.43-.58a.1.1,0,0,1-.06-.18l4.78-4.18a.13.13,0,0,0,0-.12,30.19,30.19,0,0,0-3.65-6.07.09.09,0,0,0-.11,0l-5.91,2a.1.1,0,0,1-.12-.14L72.19,23a.11.11,0,0,0,0-.12,29.86,29.86,0,0,0-5.84-4.13.09.09,0,0,0-.11,0l-4.47,4.13a.1.1,0,0,1-.17-.07l.09-6a.1.1,0,0,0-.07-.1,30.54,30.54,0,0,0-7-1.47.1.1,0,0,0-.1.07l-2.38,5.54a.1.1,0,0,1-.18,0l-2.37-5.54a.11.11,0,0,0-.11-.06,30,30,0,0,0-7,1.48.12.12,0,0,0-.07.1l.08,6.05a.09.09,0,0,1-.16.07L37.8,18.76a.11.11,0,0,0-.12,0,29.75,29.75,0,0,0-5.83,4.13.11.11,0,0,0,0,.12l2.59,5.6a.11.11,0,0,1-.13.14l-5.9-2a.11.11,0,0,0-.12,0,30.23,30.23,0,0,0-3.62,6.08.11.11,0,0,0,0,.12l4.79,4.19a.1.1,0,0,1-.06.17L23,37.91a.1.1,0,0,0-.09.07A29.9,29.9,0,0,0,22,44.92a.1.1,0,0,0,.07.1L28.4,47a.1.1,0,0,1,0,.18l-5.84,3.26a.16.16,0,0,0,0,.11,30.17,30.17,0,0,0,2.1,6.76c.32.71.67,1.4,1,2.08a.1.1,0,0,0,.06,0L52,68.16H52l26.34-8.78a.1.1,0,0,0,.06-.05,30.48,30.48,0,0,0,3.11-8.88.1.1,0,0,0-.05-.11l-5.83-3.26A.1.1,0,0,1,75.68,46.9Z" />
</svg> </svg>
<div className="info-secondary splash"> <div className="info-secondary splash">REMIX IDE</div>
REMIX IDE
</div> </div>
</div></>) </>
)
} }
export default RemixSplashScreen export default RemixSplashScreen

@ -8,7 +8,7 @@ export interface dispatchModalInterface {
modal: (data: AppModal) => void modal: (data: AppModal) => void
toast: (message: string | JSX.Element) => void toast: (message: string | JSX.Element) => void
alert: (data: AlertModal) => void alert: (data: AlertModal) => void
handleHideModal: () => void, handleHideModal: () => void
handleToaster: () => void handleToaster: () => void
} }

@ -20,13 +20,36 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
dispatch({ dispatch({
type: modalActionTypes.setModal, type: modalActionTypes.setModal,
payload: { id, title, message, okLabel, validationFn, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data } payload: {
id,
title,
message,
okLabel,
validationFn,
okFn,
cancelLabel,
cancelFn,
modalType: modalType || ModalTypes.default,
defaultValue: defaultValue,
hideFn,
resolve,
next: onNextFn,
data
}
}) })
}) })
} }
const alert = (modalData: AlertModal) => { const alert = (modalData: AlertModal) => {
return modal({ id: modalData.id, title: modalData.title || 'Alert', message: modalData.message || modalData.title, okLabel: 'OK', okFn: (value?:any) => {}, cancelLabel: '', cancelFn: () => {} }) return modal({
id: modalData.id,
title: modalData.title || 'Alert',
message: modalData.message || modalData.title,
okLabel: 'OK',
okFn: (value?: any) => {},
cancelLabel: '',
cancelFn: () => {}
})
} }
const handleHideModal = () => { const handleHideModal = () => {
@ -50,17 +73,19 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
}) })
} }
return (<dispatchModalContext.Provider value={{ modal, toast, alert, handleHideModal, handleToaster }}> return (
<modalContext.Provider value={{ modals, toasters, focusModal, focusToaster }}> <dispatchModalContext.Provider value={{modal, toast, alert, handleHideModal, handleToaster}}>
{children} <modalContext.Provider value={{modals, toasters, focusModal, focusToaster}}>{children}</modalContext.Provider>
</modalContext.Provider> </dispatchModalContext.Provider>
</dispatchModalContext.Provider>) )
} }
export const AppProvider = ({children = [], value = {}} = {}) => { export const AppProvider = ({children = [], value = {}} = {}) => {
return <AppContext.Provider value={value}> return (
<AppContext.Provider value={value}>
<ModalProvider>{children}</ModalProvider> <ModalProvider>{children}</ModalProvider>
</AppContext.Provider> </AppContext.Provider>
)
} }
export const useDialogs = () => { export const useDialogs = () => {

@ -9,7 +9,7 @@ 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 {IntlProvider} from 'react-intl' import {IntlProvider} from 'react-intl'
import { CustomTooltip } from '@remix-ui/helper'; import {CustomTooltip} from '@remix-ui/helper'
interface IRemixAppUi { interface IRemixAppUi {
app: any app: any
@ -20,7 +20,10 @@ const RemixApp = (props: IRemixAppUi) => {
const [hideSidePanel, setHideSidePanel] = useState<boolean>(false) const [hideSidePanel, setHideSidePanel] = useState<boolean>(false)
const [maximiseTrigger, setMaximiseTrigger] = useState<number>(0) const [maximiseTrigger, setMaximiseTrigger] = useState<number>(0)
const [resetTrigger, setResetTrigger] = useState<number>(0) const [resetTrigger, setResetTrigger] = useState<number>(0)
const [locale, setLocale] = useState<{ code:string; messages:any }>({ code:'en', messages:{} }); const [locale, setLocale] = useState<{code: string; messages: any}>({
code: 'en',
messages: {}
})
const sidePanelRef = useRef(null) const sidePanelRef = useRef(null)
useEffect(() => { useEffect(() => {
@ -39,7 +42,7 @@ const RemixApp = (props: IRemixAppUi) => {
function setListeners() { function setListeners() {
props.app.sidePanel.events.on('toggle', () => { props.app.sidePanel.events.on('toggle', () => {
setHideSidePanel(prev => { setHideSidePanel((prev) => {
return !prev return !prev
}) })
}) })
@ -55,13 +58,13 @@ const RemixApp = (props: IRemixAppUi) => {
}) })
props.app.layout.event.on('maximisesidepanel', () => { props.app.layout.event.on('maximisesidepanel', () => {
setMaximiseTrigger(prev => { setMaximiseTrigger((prev) => {
return prev + 1 return prev + 1
}) })
}) })
props.app.layout.event.on('resetsidepanel', () => { props.app.layout.event.on('resetsidepanel', () => {
setResetTrigger(prev => { setResetTrigger((prev) => {
return prev + 1 return prev + 1
}) })
}) })
@ -85,17 +88,24 @@ const RemixApp = (props: IRemixAppUi) => {
<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="custom_icon_panel iconpanel bg-light">{props.app.menuicons.render()}</div> <div id="icon-panel" data-id="remixIdeIconPanel" className="custom_icon_panel iconpanel bg-light">
<div ref={sidePanelRef} id="side-panel" data-id="remixIdeSidePanel" className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}>{props.app.sidePanel.render()}</div> {props.app.menuicons.render()}
<DragBar resetTrigger={resetTrigger} maximiseTrigger={maximiseTrigger} minWidth={285} refObject={sidePanelRef} hidden={hideSidePanel} setHideStatus={setHideSidePanel}></DragBar> </div>
<div id="main-panel" data-id="remixIdeMainPanel" className='mainpanel d-flex'> <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={285}
refObject={sidePanelRef}
hidden={hideSidePanel}
setHideStatus={setHideSidePanel}
></DragBar>
<div id="main-panel" data-id="remixIdeMainPanel" className="mainpanel d-flex">
<RemixUIMainPanel Context={AppContext}></RemixUIMainPanel> <RemixUIMainPanel Context={AppContext}></RemixUIMainPanel>
<CustomTooltip <CustomTooltip placement="bottom" tooltipId="overlay-tooltip-all-tabs" tooltipText="Scroll to see all tabs">
placement="bottom" <div className="remix-ui-tabs_end remix-bg-opacity position-absolute position-fixed"></div>
tooltipId="overlay-tooltip-all-tabs"
tooltipText="Scroll to see all tabs"
>
<div className='remix-ui-tabs_end remix-bg-opacity position-absolute position-fixed'></div>
</CustomTooltip> </CustomTooltip>
</div> </div>
</div> </div>

@ -1,8 +1,7 @@
import { CustomTooltip } from '@remix-ui/helper'; import {CustomTooltip} from '@remix-ui/helper'
import React, {CSSProperties} from 'react' //eslint-disable-line import React, {CSSProperties} from 'react' //eslint-disable-line
import './remix-ui-checkbox.css' import './remix-ui-checkbox.css'
type Placement = import('react-overlays/usePopper').Placement; type Placement = import('react-overlays/usePopper').Placement
/* eslint-disable-next-line */ /* eslint-disable-next-line */
export interface RemixUiCheckboxProps { export interface RemixUiCheckboxProps {
@ -38,26 +37,22 @@ export const RemixUiCheckbox = ({
optionalClassName = '', optionalClassName = '',
display = 'flex', display = 'flex',
disabled, disabled,
tooltipPlacement = 'right', tooltipPlacement = 'right'
}: RemixUiCheckboxProps) => { }: RemixUiCheckboxProps) => {
const childJSXWithTooltip = ( const childJSXWithTooltip = (
<CustomTooltip <CustomTooltip tooltipText={title} tooltipId={`${name}Tooltip`} placement={tooltipPlacement}>
tooltipText={title} <div
tooltipId={`${name}Tooltip`} className={`listenOnNetwork_2A0YE0 custom-control custom-checkbox ${optionalClassName}`}
placement={tooltipPlacement} style={
{
display: display,
alignItems: 'center',
visibility: visibility
} as CSSProperties
}
onClick={onClick}
> >
<div className={`listenOnNetwork_2A0YE0 custom-control custom-checkbox ${optionalClassName}`} 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} disabled={disabled} />
<input
id={id}
type={inputType}
onChange={onChange}
style={{ verticalAlign: 'bottom' }}
name={name}
className="custom-control-input"
checked={checked}
disabled={disabled}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{paddingTop: '0.15rem'}} aria-disabled={disabled}> <label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{paddingTop: '0.15rem'}} aria-disabled={disabled}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''} {name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label} {label}
@ -66,25 +61,25 @@ export const RemixUiCheckbox = ({
</CustomTooltip> </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
<input className="listenOnNetwork_2A0YE0 custom-control custom-checkbox"
id={id} style={
type={inputType} {
onChange={onChange} display: display,
style={{ verticalAlign: 'bottom' }} alignItems: 'center',
name={name} visibility: visibility
className="custom-control-input" } as CSSProperties
checked={checked} }
/> 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'}}> <label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{paddingTop: '0.15rem'}}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''} {name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label} {label}
</label> </label>
</div> </div>
) )
return ( return title ? childJSXWithTooltip : childJSX
title ? (childJSXWithTooltip) : (childJSX)
)
} }
export default RemixUiCheckbox export default RemixUiCheckbox

@ -6,13 +6,13 @@ import './copy-to-clipboard.css'
import {CustomTooltip} from '@remix-ui/helper' import {CustomTooltip} from '@remix-ui/helper'
interface ICopyToClipboard { interface ICopyToClipboard {
content?: any, content?: any
tip?: string, tip?: string
icon?: string, icon?: string
direction?: Placement, direction?: Placement
className?: string, className?: string
title?: string, title?: string
children?: JSX.Element, children?: JSX.Element
getContent?: () => any getContent?: () => any
} }
export const CopyToClipboard = (props: ICopyToClipboard) => { export const CopyToClipboard = (props: ICopyToClipboard) => {
@ -37,7 +37,8 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
} }
const handleClick = (e: any) => { const handleClick = (e: any) => {
if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory if (content) {
// module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory
copyData() copyData()
} else { } else {
content = getContent && getContent() content = getContent && getContent()
@ -50,19 +51,11 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
setTimeout(() => setMessage(tip), 500) setTimeout(() => setMessage(tip), 500)
} }
const childJSX = ( const childJSX = children || <i className={`far ${icon} ml-1 p-2`} aria-hidden="true" {...otherProps}></i>
children || (<i className={`far ${icon} ml-1 p-2`} aria-hidden="true"
{...otherProps}
></i>)
)
return ( return (
<a href='#' onClick={handleClick} onMouseLeave={reset}> <a href="#" onClick={handleClick} onMouseLeave={reset}>
<CustomTooltip <CustomTooltip tooltipText={message} tooltipId="overlay-tooltip" placement={direction}>
tooltipText={message}
tooltipId="overlay-tooltip"
placement={direction}
>
{childJSX} {childJSX}
</CustomTooltip> </CustomTooltip>
</a> </a>

@ -1,11 +1,11 @@
import React, {useState, useEffect} from 'react' // eslint-disable-line import React, {useState, useEffect} from 'react' // eslint-disable-line
import {ExtractData, ExtractFunc} from '../types' // eslint-disable-line import {ExtractData, ExtractFunc} from '../types' // eslint-disable-line
export const useExtractData = (json, extractFunc?: ExtractFunc): Array<{ key: string, data: ExtractData }> => { export const useExtractData = (json, extractFunc?: ExtractFunc): Array<{key: string; data: ExtractData}> => {
const [data, setData] = useState([]) const [data, setData] = useState([])
useEffect(() => { useEffect(() => {
const data: Array<{ key: string, data: ExtractData }> = Object.keys(json).map((innerKey) => { const data: Array<{key: string; data: ExtractData}> = Object.keys(json).map((innerKey) => {
if (extractFunc) { if (extractFunc) {
return { return {
key: innerKey, key: innerKey,

@ -2,7 +2,19 @@ import { CustomTooltip } from '@remix-ui/helper'
import React, {useState, useEffect} from 'react' // eslint-disable-line import React, {useState, useEffect} from 'react' // eslint-disable-line
import './button-navigator.css' import './button-navigator.css'
export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward, stepOverForward, jumpOut, jumpPreviousBreakpoint, jumpNextBreakpoint, jumpToException, revertedReason, stepState, jumpOutDisabled }) => { export const ButtonNavigation = ({
stepOverBack,
stepIntoBack,
stepIntoForward,
stepOverForward,
jumpOut,
jumpPreviousBreakpoint,
jumpNextBreakpoint,
jumpToException,
revertedReason,
stepState,
jumpOutDisabled
}) => {
const [state, setState] = useState({ const [state, setState] = useState({
intoBackDisabled: true, intoBackDisabled: true,
overBackDisabled: true, overBackDisabled: true,
@ -46,7 +58,7 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
intoForwardDisabled: stepState === 'end', intoForwardDisabled: stepState === 'end',
overForwardDisabled: stepState === 'end', overForwardDisabled: stepState === 'end',
jumpNextBreakpointDisabled: stepState === 'end', jumpNextBreakpointDisabled: stepState === 'end',
jumpOutDisabled: (jumpOutDisabled !== null) && (jumpOutDisabled !== undefined) ? jumpOutDisabled : true jumpOutDisabled: jumpOutDisabled !== null && jumpOutDisabled !== undefined ? jumpOutDisabled : true
} }
}) })
} }
@ -56,19 +68,50 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
const disableJumpBtnStyle = 'jumpButtonDisabled' const disableJumpBtnStyle = 'jumpButtonDisabled'
const stepMarkupStructure = { const stepMarkupStructure = {
stepOverBackJSX: { stepOverBackJSX: {
markup: (<div className={state.overBackDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}`: `${stepBtnStyle}`} onClick={() => { stepOverBack && stepOverBack() }}> markup: (
<button id='overback' className='btn btn-link btn-sm stepButton m-0 p-0' onClick={() => { stepOverBack && stepOverBack() }} disabled={state.overBackDisabled} style={{ pointerEvents: 'none', color: 'white' }}> <div
className={state.overBackDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}` : `${stepBtnStyle}`}
onClick={() => {
stepOverBack && stepOverBack()
}}
>
<button
id="overback"
className="btn btn-link btn-sm stepButton m-0 p-0"
onClick={() => {
stepOverBack && stepOverBack()
}}
disabled={state.overBackDisabled}
style={{pointerEvents: 'none', color: 'white'}}
>
<span className="fas fa-reply"></span> <span className="fas fa-reply"></span>
</button> </button>
</div>), </div>
),
placement: 'top-start', placement: 'top-start',
tagId: 'overbackTooltip', tagId: 'overbackTooltip',
tooltipMsg: 'Step over back' tooltipMsg: 'Step over back'
}, },
stepBackJSX: { stepBackJSX: {
markup: ( markup: (
<div className={state.intoBackDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}`: `${stepBtnStyle}`} onClick={() => { stepIntoBack && stepIntoBack() }} data-id="buttonNavigatorIntoBack" id="buttonNavigatorIntoBackContainer"> <div
<button id='intoback' data-id="buttonNavigatorIntoBack" className='btn btn-link btn-sm stepButton m-0 p-0' onClick={() => { stepIntoBack && stepIntoBack() }} disabled={state.intoBackDisabled} style={{ pointerEvents: 'none', color: 'white' }}> className={state.intoBackDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}` : `${stepBtnStyle}`}
onClick={() => {
stepIntoBack && stepIntoBack()
}}
data-id="buttonNavigatorIntoBack"
id="buttonNavigatorIntoBackContainer"
>
<button
id="intoback"
data-id="buttonNavigatorIntoBack"
className="btn btn-link btn-sm stepButton m-0 p-0"
onClick={() => {
stepIntoBack && stepIntoBack()
}}
disabled={state.intoBackDisabled}
style={{pointerEvents: 'none', color: 'white'}}
>
<span className="fas fa-level-up-alt"></span> <span className="fas fa-level-up-alt"></span>
</button> </button>
</div> </div>
@ -80,8 +123,22 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
stepIntoJSX: { stepIntoJSX: {
markup: ( markup: (
<div className={state.intoForwardDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}`: `${stepBtnStyle}`} onClick={() => { stepIntoForward && stepIntoForward() }} data-id="buttonNavigatorIntoForward" id="buttonNavigatorIntoFowardContainer"> <div
<button id='intoforward' data-id="buttonNavigatorIntoForward" className='btn btn-link btn-sm stepButton m-0 p-0' onClick={() => { stepIntoForward && stepIntoForward() }} disabled={state.intoForwardDisabled} className={state.intoForwardDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}` : `${stepBtnStyle}`}
onClick={() => {
stepIntoForward && stepIntoForward()
}}
data-id="buttonNavigatorIntoForward"
id="buttonNavigatorIntoFowardContainer"
>
<button
id="intoforward"
data-id="buttonNavigatorIntoForward"
className="btn btn-link btn-sm stepButton m-0 p-0"
onClick={() => {
stepIntoForward && stepIntoForward()
}}
disabled={state.intoForwardDisabled}
style={{pointerEvents: 'none', color: 'white'}} style={{pointerEvents: 'none', color: 'white'}}
> >
<span className="fas fa-level-down-alt"></span> <span className="fas fa-level-down-alt"></span>
@ -94,22 +151,57 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
}, },
stepOverForwardJSX: { stepOverForwardJSX: {
markup: ( markup: (
<div className={state.overForwardDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}`: `${stepBtnStyle}`} onClick={() => { stepOverForward && stepOverForward() }} data-id="buttonNavigatorOverForward" id="buttonNavigatorOverForwardContainer"> <div
<button id='overforward' className='btn btn-link btn-sm stepButton m-0 p-0' onClick={() => { stepOverForward && stepOverForward() }} disabled={state.overForwardDisabled} style={{ pointerEvents: 'none', color: 'white' }}> className={state.overForwardDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}` : `${stepBtnStyle}`}
onClick={() => {
stepOverForward && stepOverForward()
}}
data-id="buttonNavigatorOverForward"
id="buttonNavigatorOverForwardContainer"
>
<button
id="overforward"
className="btn btn-link btn-sm stepButton m-0 p-0"
onClick={() => {
stepOverForward && stepOverForward()
}}
disabled={state.overForwardDisabled}
style={{pointerEvents: 'none', color: 'white'}}
>
<span className="fas fa-share"></span> <span className="fas fa-share"></span>
</button> </button>
</div> </div>
), ),
placement: 'top-end', placement: 'top-end',
tagId: 'overbackTooltip', tagId: 'overbackTooltip',
tooltipMsg: 'Step over forward', tooltipMsg: 'Step over forward'
} }
} }
const jumpMarkupStructure = { const jumpMarkupStructure = {
jumpPreviousBreakpointJSX: { jumpPreviousBreakpointJSX: {
markup: ( markup: (
<div className={state.jumpPreviousBreakpointDisabled ? `${stepBtnStyle} ${disableJumpBtnStyle}`: `${stepBtnStyle}`} id="buttonNavigatorJumpPreviousBreakpointContainer" onClick={() => { jumpPreviousBreakpoint && jumpPreviousBreakpoint() }} data-id="buttonNavigatorJumpPreviousBreakpoint"> <div
<button className='btn btn-link btn-sm jumpButton m-0 p-0' id='jumppreviousbreakpoint' data-id="buttonNavigatorJumpPreviousBreakpoint" onClick={() => { jumpPreviousBreakpoint && jumpPreviousBreakpoint() }} disabled={state.jumpPreviousBreakpointDisabled} style={{ pointerEvents: 'none', backgroundColor: 'inherit', color: 'white' }}> className={state.jumpPreviousBreakpointDisabled ? `${stepBtnStyle} ${disableJumpBtnStyle}` : `${stepBtnStyle}`}
id="buttonNavigatorJumpPreviousBreakpointContainer"
onClick={() => {
jumpPreviousBreakpoint && jumpPreviousBreakpoint()
}}
data-id="buttonNavigatorJumpPreviousBreakpoint"
>
<button
className="btn btn-link btn-sm jumpButton m-0 p-0"
id="jumppreviousbreakpoint"
data-id="buttonNavigatorJumpPreviousBreakpoint"
onClick={() => {
jumpPreviousBreakpoint && jumpPreviousBreakpoint()
}}
disabled={state.jumpPreviousBreakpointDisabled}
style={{
pointerEvents: 'none',
backgroundColor: 'inherit',
color: 'white'
}}
>
<span className="fas fa-step-backward"></span> <span className="fas fa-step-backward"></span>
</button> </button>
</div> </div>
@ -120,8 +212,28 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
}, },
jumpOutJSX: { jumpOutJSX: {
markup: ( markup: (
<div className={state.jumpOutDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}`: `${stepBtnStyle}`} onClick={() => { jumpOut && jumpOut() }} data-id="buttonNavigatorJumpOut" id="buttonNavigatorJumpOutContainer"> <div
<button className='btn btn-link btn-sm jumpButton m-0 p-0' id='jumpout' onClick={() => { jumpOut && jumpOut() }} disabled={state.jumpOutDisabled} style={{ pointerEvents: 'none', backgroundColor: 'inherit', color: 'white' }} data-id="buttonNavigatorJumpOut"> className={state.jumpOutDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}` : `${stepBtnStyle}`}
onClick={() => {
jumpOut && jumpOut()
}}
data-id="buttonNavigatorJumpOut"
id="buttonNavigatorJumpOutContainer"
>
<button
className="btn btn-link btn-sm jumpButton m-0 p-0"
id="jumpout"
onClick={() => {
jumpOut && jumpOut()
}}
disabled={state.jumpOutDisabled}
style={{
pointerEvents: 'none',
backgroundColor: 'inherit',
color: 'white'
}}
data-id="buttonNavigatorJumpOut"
>
<span className="fas fa-eject"></span> <span className="fas fa-eject"></span>
</button> </button>
</div> </div>
@ -132,8 +244,24 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
}, },
jumpNextBreakpointJSX: { jumpNextBreakpointJSX: {
markup: ( markup: (
<div className={state.jumpNextBreakpointDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}`: `${stepBtnStyle}`} onClick={() => { jumpNextBreakpoint && jumpNextBreakpoint() }} data-id="buttonNavigatorJumpNextBreakpoint" id="buttonNavigatorJumpNextBreakpointContainer"> <div
<button className='btn btn-link btn-sm jumpButton m-0 p-0' id='jumpnextbreakpoint' data-id="buttonNavigatorJumpNextBreakpoint" onClick={() => { jumpNextBreakpoint && jumpNextBreakpoint() }} disabled={state.jumpNextBreakpointDisabled} style={{ pointerEvents: 'none', color: 'white' }}> className={state.jumpNextBreakpointDisabled ? `${stepBtnStyle} ${disableStepBtnStyle}` : `${stepBtnStyle}`}
onClick={() => {
jumpNextBreakpoint && jumpNextBreakpoint()
}}
data-id="buttonNavigatorJumpNextBreakpoint"
id="buttonNavigatorJumpNextBreakpointContainer"
>
<button
className="btn btn-link btn-sm jumpButton m-0 p-0"
id="jumpnextbreakpoint"
data-id="buttonNavigatorJumpNextBreakpoint"
onClick={() => {
jumpNextBreakpoint && jumpNextBreakpoint()
}}
disabled={state.jumpNextBreakpointDisabled}
style={{pointerEvents: 'none', color: 'white'}}
>
<span className="fas fa-step-forward"></span> <span className="fas fa-step-forward"></span>
</button> </button>
</div> </div>
@ -147,8 +275,7 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
return ( return (
<div className="buttons"> <div className="buttons">
<div className="stepButtons btn-group py-1"> <div className="stepButtons btn-group py-1">
{ {Object.keys(stepMarkupStructure).map((x) => (
Object.keys(stepMarkupStructure).map(x => (
<CustomTooltip <CustomTooltip
placement={stepMarkupStructure[x].placement} placement={stepMarkupStructure[x].placement}
tooltipId={stepMarkupStructure[x].tagId} tooltipId={stepMarkupStructure[x].tagId}
@ -157,13 +284,11 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
> >
{stepMarkupStructure[x].markup} {stepMarkupStructure[x].markup}
</CustomTooltip> </CustomTooltip>
)) ))}
}
</div> </div>
<div className="jumpButtons btn-group py-1"> <div className="jumpButtons btn-group py-1">
{ {Object.keys(jumpMarkupStructure).map((x) => (
Object.keys(jumpMarkupStructure).map(x => (
<CustomTooltip <CustomTooltip
placement={jumpMarkupStructure[x].placement} placement={jumpMarkupStructure[x].placement}
tooltipText={jumpMarkupStructure[x].tooltipMsg} tooltipText={jumpMarkupStructure[x].tooltipMsg}
@ -172,14 +297,36 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
> >
{jumpMarkupStructure[x].markup} {jumpMarkupStructure[x].markup}
</CustomTooltip> </CustomTooltip>
)) ))}
} </div>
<div id="reverted" style={{display: revertedReason === '' ? 'none' : 'block'}}>
<span className="text-warning">This call has reverted, state changes made during the call will be reverted.</span>
<span className="text-warning" id="outofgas" style={{display: revertedReason === 'outofgas' ? 'inline' : 'none'}}>
This call will run out of gas.
</span>
<span
className="text-warning"
id="parenthasthrown"
style={{
display: revertedReason === 'parenthasthrown' ? 'inline' : 'none'
}}
>
The parent call will throw an exception
</span>
<div className="text-warning">
Click{' '}
<u
data-id="debugGoToRevert"
className="cursorPointerRemixDebugger"
role="button"
onClick={() => {
jumpToException && jumpToException()
}}
>
here
</u>{' '}
to jump where the call reverted.
</div> </div>
<div id='reverted' style={{ display: revertedReason === '' ? 'none' : 'block' }}>
<span className='text-warning'>This call has reverted, state changes made during the call will be reverted.</span>
<span className='text-warning' id='outofgas' style={{ display: revertedReason === 'outofgas' ? 'inline' : 'none' }}>This call will run out of gas.</span>
<span className='text-warning' id='parenthasthrown' style={{ display: revertedReason === 'parenthasthrown' ? 'inline' : 'none' }}>The parent call will throw an exception</span>
<div className='text-warning'>Click <u data-id="debugGoToRevert" className="cursorPointerRemixDebugger" role="button" onClick={() => { jumpToException && jumpToException() }}>here</u> to jump where the call reverted.</div>
</div> </div>
</div> </div>
) )

@ -10,7 +10,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import {CustomTooltip, isValidHash} from '@remix-ui/helper' import {CustomTooltip, isValidHash} from '@remix-ui/helper'
/* eslint-disable-next-line */ /* eslint-disable-next-line */
import './debugger-ui.css' import './debugger-ui.css'
const _paq = (window as any)._paq = (window as any)._paq || [] const _paq = ((window as any)._paq = (window as any)._paq || [])
export const DebuggerUI = (props: DebuggerUIProps) => { export const DebuggerUI = (props: DebuggerUIProps) => {
const debuggerModule = props.debuggerAPI const debuggerModule = props.debuggerAPI
@ -54,7 +54,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const handleResize = () => { const handleResize = () => {
if (panelsRef.current && debuggerTopRef.current) { if (panelsRef.current && debuggerTopRef.current) {
panelsRef.current.style.height = (window.innerHeight - debuggerTopRef.current.clientHeight) - debuggerTopRef.current.offsetTop - 7 +'px' panelsRef.current.style.height = window.innerHeight - debuggerTopRef.current.clientHeight - debuggerTopRef.current.offsetTop - 7 + 'px'
} }
} }
@ -65,8 +65,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
useEffect(() => { useEffect(() => {
window.addEventListener('resize', handleResize) window.addEventListener('resize', handleResize)
// TODO: not a good way to wait on the ref doms element to be rendered of course // TODO: not a good way to wait on the ref doms element to be rendered of course
setTimeout(() => setTimeout(() => handleResize(), 2000)
handleResize(), 2000)
return () => window.removeEventListener('resize', handleResize) return () => window.removeEventListener('resize', handleResize)
}, [state.debugging, state.isActive]) }, [state.debugging, state.isActive])
@ -85,7 +84,11 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
useEffect(() => { useEffect(() => {
const setEditor = () => { const setEditor = () => {
debuggerModule.onBreakpointCleared((fileName, row) => { debuggerModule.onBreakpointCleared((fileName, row) => {
if (state.debugger) state.debugger.breakPointManager.remove({ fileName: fileName, row: row }) if (state.debugger)
state.debugger.breakPointManager.remove({
fileName: fileName,
row: row
})
}) })
debuggerModule.onBreakpointAdded((fileName, row) => { debuggerModule.onBreakpointAdded((fileName, row) => {
@ -101,7 +104,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const providerChanged = () => { const providerChanged = () => {
debuggerModule.onEnvChanged((provider) => { debuggerModule.onEnvChanged((provider) => {
setState(prevState => { setState((prevState) => {
const isLocalNodeUsed = !provider.startsWith('vm') && provider !== 'injected' const isLocalNodeUsed = !provider.startsWith('vm') && provider !== 'injected'
return {...prevState, isLocalNodeUsed: isLocalNodeUsed} return {...prevState, isLocalNodeUsed: isLocalNodeUsed}
}) })
@ -116,19 +119,22 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
debuggerInstance.event.register('debuggerStatus', async (isActive) => { debuggerInstance.event.register('debuggerStatus', async (isActive) => {
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
setState(prevState => { setState((prevState) => {
return {...prevState, isActive} return {...prevState, isActive}
}) })
}) })
debuggerInstance.event.register('locatingBreakpoint', async (isActive) => { debuggerInstance.event.register('locatingBreakpoint', async (isActive) => {
setState(prevState => { setState((prevState) => {
return { ...prevState, sourceLocationStatus: 'Locating breakpoint, this might take a while...' } return {
...prevState,
sourceLocationStatus: 'Locating breakpoint, this might take a while...'
}
}) })
}) })
debuggerInstance.event.register('noBreakpointHit', async (isActive) => { debuggerInstance.event.register('noBreakpointHit', async (isActive) => {
setState(prevState => { setState((prevState) => {
return {...prevState, sourceLocationStatus: ''} return {...prevState, sourceLocationStatus: ''}
}) })
}) })
@ -136,14 +142,15 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address, stepDetail, lineGasCost) => { 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) => {
return { ...prevState, sourceLocationStatus: 'Source location not available, neither in Sourcify nor in Etherscan. Please make sure the Etherscan api key is provided in the settings.' } return {
...prevState,
sourceLocationStatus: 'Source location not available, neither in Sourcify nor in Etherscan. Please make sure the Etherscan api key is provided in the settings.'
}
}) })
return return
} }
const contracts = await debuggerModule.fetchContractAndCompile( const contracts = await debuggerModule.fetchContractAndCompile(address || currentReceipt.contractAddress || currentReceipt.to, currentReceipt)
address || currentReceipt.contractAddress || currentReceipt.to,
currentReceipt)
if (contracts) { if (contracts) {
let path = contracts.getSourceName(rawLocation.file) let path = contracts.getSourceName(rawLocation.file)
if (!path) { if (!path) {
@ -155,7 +162,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
try { try {
content = await debuggerModule.getFile(path) content = await debuggerModule.getFile(path)
} catch (e) { } catch (e) {
const message = 'Unable to fetch generated sources, the file probably doesn\'t exist yet.' const message = "Unable to fetch generated sources, the file probably doesn't exist yet."
console.log(message, ' ', e) console.log(message, ' ', e)
} }
if (content !== source.contents) { if (content !== source.contents) {
@ -166,7 +173,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} }
} }
if (path) { if (path) {
setState(prevState => { setState((prevState) => {
return {...prevState, sourceLocationStatus: ''} return {...prevState, sourceLocationStatus: ''}
}) })
await debuggerModule.discardHighlight() await debuggerModule.discardHighlight()
@ -183,7 +190,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} }
const updateTxNumberFlag = (empty: boolean) => { const updateTxNumberFlag = (empty: boolean) => {
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
txNumberIsEmpty: empty, txNumberIsEmpty: empty,
@ -194,7 +201,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const unloadRequested = (blockNumber, txIndex, tx) => { const unloadRequested = (blockNumber, txIndex, tx) => {
unLoad() unLoad()
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
sourceLocationStatus: '' sourceLocationStatus: ''
@ -205,7 +212,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const unLoad = () => { const unLoad = () => {
debuggerModule.onStopDebugging() debuggerModule.onStopDebugging()
if (state.debugger) state.debugger.unload() if (state.debugger) state.debugger.unload()
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
isActive: false, isActive: false,
@ -228,10 +235,10 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const startDebugging = async (blockNumber, txNumber, tx, optWeb3?) => { const startDebugging = async (blockNumber, txNumber, tx, optWeb3?) => {
if (state.debugger) { if (state.debugger) {
unLoad() unLoad()
await (new Promise((resolve) => setTimeout(() => resolve({}), 1000))) await new Promise((resolve) => setTimeout(() => resolve({}), 1000))
} }
if (!txNumber) return if (!txNumber) return
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
txNumber: txNumber, txNumber: txNumber,
@ -239,7 +246,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} }
}) })
if (!isValidHash(txNumber)) { if (!isValidHash(txNumber)) {
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
validationError: 'Invalid transaction hash.' validationError: 'Invalid transaction hash.'
@ -253,7 +260,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const networkId = await web3.eth.net.getId() const networkId = await web3.eth.net.getId()
_paq.push(['trackEvent', 'debugger', 'startDebugging', networkId]) _paq.push(['trackEvent', 'debugger', 'startDebugging', networkId])
if (networkId === 42) { if (networkId === 42) {
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
validationError: 'Unfortunately, the Kovan network is not supported.' validationError: 'Unfortunately, the Kovan network is not supported.'
@ -272,7 +279,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
currentBlock = await web3.eth.getBlock(currentReceipt.blockHash) currentBlock = await web3.eth.getBlock(currentReceipt.blockHash)
currentTransaction = await web3.eth.getTransaction(txNumber) currentTransaction = await web3.eth.getTransaction(txNumber)
} catch (e) { } catch (e) {
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
validationError: e.message validationError: e.message
@ -303,7 +310,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
try { try {
await debuggerInstance.debug(blockNumber, txNumber, tx, () => { await debuggerInstance.debug(blockNumber, txNumber, tx, () => {
listenToEvents(debuggerInstance, currentReceipt) listenToEvents(debuggerInstance, currentReceipt)
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
blockNumber, blockNumber,
@ -320,7 +327,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}) })
} catch (error) { } catch (error) {
unLoad() unLoad()
setState(prevState => { setState((prevState) => {
let errorMsg = error.message || error let errorMsg = error.message || error
if (typeof errorMsg !== 'string') { if (typeof errorMsg !== 'string') {
errorMsg = JSON.stringify(errorMsg) + '. Possible error: the current endpoint does not support retrieving the trace of a transaction.' errorMsg = JSON.stringify(errorMsg) + '. Possible error: the current endpoint does not support retrieving the trace of a transaction.'
@ -338,7 +345,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
} }
const debug = (txHash, web3?) => { const debug = (txHash, web3?) => {
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
validationError: '', validationError: '',
@ -370,16 +377,22 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const customJSX = ( const customJSX = (
<span className="p-0 m-0"> <span className="p-0 m-0">
<input className="custom-control-input" id="debugGeneratedSourcesInput" onChange={({ target: { checked } }) => { <input
setState(prevState => { className="custom-control-input"
return { ...prevState, opt: { ...prevState.opt, debugWithGeneratedSources: checked } } id="debugGeneratedSourcesInput"
}) onChange={({target: {checked}}) => {
}} type="checkbox" /> setState((prevState) => {
<label return {
data-id="debugGeneratedSourcesLabel" ...prevState,
className="form-check-label custom-control-label" opt: {...prevState.opt, debugWithGeneratedSources: checked}
htmlFor="debugGeneratedSourcesInput"> }
<FormattedMessage id='debugger.useGeneratedSources' />(Solidity {'>='} v0.7.2) })
}}
type="checkbox"
/>
<label data-id="debugGeneratedSourcesLabel" className="form-check-label custom-control-label" htmlFor="debugGeneratedSourcesInput">
<FormattedMessage id="debugger.useGeneratedSources" />
(Solidity {'>='} v0.7.2)
</label> </label>
</span> </span>
) )
@ -389,50 +402,74 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
<div className="px-2" ref={debuggerTopRef}> <div className="px-2" ref={debuggerTopRef}>
<div> <div>
<div className="mt-2 mb-2 debuggerConfig custom-control custom-checkbox"> <div className="mt-2 mb-2 debuggerConfig custom-control custom-checkbox">
<CustomTooltip <CustomTooltip tooltipId="debuggerGenSourceCheckbox" tooltipText={<FormattedMessage id="debugger.debugWithGeneratedSources" />} placement="bottom-start">
tooltipId="debuggerGenSourceCheckbox"
tooltipText={<FormattedMessage id='debugger.debugWithGeneratedSources' />}
placement="bottom-start"
>
{customJSX} {customJSX}
</CustomTooltip> </CustomTooltip>
</div> </div>
{ state.isLocalNodeUsed && <div className="mb-2 debuggerConfig custom-control custom-checkbox"> {state.isLocalNodeUsed && (
<CustomTooltip <div className="mb-2 debuggerConfig custom-control custom-checkbox">
tooltipId="debuggerGenSourceInput" <CustomTooltip tooltipId="debuggerGenSourceInput" tooltipText="Force the debugger to use the current local node" placement="right">
tooltipText="Force the debugger to use the current local node"
placement='right'
>
<input <input
className="custom-control-input" className="custom-control-input"
id="debugWithLocalNodeInput" id="debugWithLocalNodeInput"
onChange={({target: {checked}}) => { onChange={({target: {checked}}) => {
setState(prevState => { setState((prevState) => {
return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } } return {
...prevState,
opt: {...prevState.opt, debugWithLocalNode: checked}
}
}) })
}} }}
type="checkbox" type="checkbox"
/> />
</CustomTooltip> </CustomTooltip>
<label data-id="debugLocaNodeLabel" className="form-check-label custom-control-label" htmlFor="debugWithLocalNodeInput"><FormattedMessage id='debugger.debugLocaNodeLabel' /></label> <label data-id="debugLocaNodeLabel" className="form-check-label custom-control-label" htmlFor="debugWithLocalNodeInput">
<FormattedMessage id="debugger.debugLocaNodeLabel" />
</label>
</div> </div>
} )}
{state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span>} {state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span>}
</div> </div>
<TxBrowser requestDebug={ requestDebug } unloadRequested={ unloadRequested } updateTxNumberFlag={ updateTxNumberFlag } transactionNumber={ state.txNumber } debugging={ state.debugging } /> <TxBrowser
{ state.debugging && state.sourceLocationStatus && <div className="text-warning"><i className="fas fa-exclamation-triangle" aria-hidden="true"></i> {state.sourceLocationStatus}</div> } requestDebug={requestDebug}
{ !state.debugging && unloadRequested={unloadRequested}
updateTxNumberFlag={updateTxNumberFlag}
transactionNumber={state.txNumber}
debugging={state.debugging}
/>
{state.debugging && state.sourceLocationStatus && (
<div className="text-warning">
<i className="fas fa-exclamation-triangle" aria-hidden="true"></i> {state.sourceLocationStatus}
</div>
)}
{!state.debugging && (
<div> <div>
<i className="fas fa-info-triangle" aria-hidden="true"></i> <i className="fas fa-info-triangle" aria-hidden="true"></i>
<span> <span>
<FormattedMessage id='debugger.introduction' />: <a href="https://docs.sourcify.dev/docs/chains/" target="__blank" >Sourcify docs</a> & <a href="https://etherscan.io/contractsVerified" target="__blank">https://etherscan.io/contractsVerified</a> <FormattedMessage id="debugger.introduction" />:{' '}
<a href="https://docs.sourcify.dev/docs/chains/" target="__blank">
Sourcify docs
</a>{' '}
&{' '}
<a href="https://etherscan.io/contractsVerified" target="__blank">
https://etherscan.io/contractsVerified
</a>
</span> </span>
</div> } </div>
)}
{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 debugging={state.debugging} vmDebugger={vmDebugger} />} {state.debugging && <VmDebuggerHead debugging={state.debugging} vmDebugger={vmDebugger} />}
{ state.debugging && <VmDebugger debugging={state.debugging} 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>
) )

@ -15,7 +15,7 @@ export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
if (onChangeId.current) { if (onChangeId.current) {
clearTimeout(onChangeId.current) clearTimeout(onChangeId.current)
} }
((value) => { ;((value) => {
onChangeId.current = setTimeout(() => { onChangeId.current = setTimeout(() => {
jumpTo && jumpTo(value) jumpTo && jumpTo(value)
}, 100) }, 100)
@ -30,11 +30,12 @@ export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
return ( return (
<div> <div>
<input id='slider' <input
id="slider"
data-id="slider" data-id="slider"
className='w-100 my-0' className="w-100 my-0"
ref={slider} ref={slider}
type='range' type="range"
min={0} min={0}
max={traceLength ? traceLength - 1 : 0} max={traceLength ? traceLength - 1 : 0}
onMouseUp={handleChange} onMouseUp={handleChange}

@ -2,7 +2,21 @@ import React, { useState, useEffect } from 'react' // eslint-disable-line
import Slider from '../slider/slider' // eslint-disable-line import Slider from '../slider/slider' // eslint-disable-line
import ButtonNavigator from '../button-navigator/button-navigator' // eslint-disable-line import ButtonNavigator from '../button-navigator/button-navigator' // eslint-disable-line
export const StepManager = ({ stepManager: { jumpTo, traceLength, stepIntoBack, stepIntoForward, stepOverBack, stepOverForward, jumpOut, jumpNextBreakpoint, jumpPreviousBreakpoint, jumpToException, registerEvent } }) => { export const StepManager = ({
stepManager: {
jumpTo,
traceLength,
stepIntoBack,
stepIntoForward,
stepOverBack,
stepOverForward,
jumpOut,
jumpNextBreakpoint,
jumpPreviousBreakpoint,
jumpToException,
registerEvent
}
}) => {
const [state, setState] = useState({ const [state, setState] = useState({
sliderValue: -1, sliderValue: -1,
revertWarning: '', revertWarning: '',
@ -16,13 +30,13 @@ export const StepManager = ({ stepManager: { jumpTo, traceLength, stepIntoBack,
}, [registerEvent]) }, [registerEvent])
const setRevertWarning = (warning) => { const setRevertWarning = (warning) => {
setState(prevState => { setState((prevState) => {
return {...prevState, revertWarning: warning} return {...prevState, revertWarning: warning}
}) })
} }
const updateStep = (step, stepState, jumpOutDisabled) => { const updateStep = (step, stepState, jumpOutDisabled) => {
setState(prevState => { setState((prevState) => {
return {...prevState, sliderValue: step, stepState, jumpOutDisabled} return {...prevState, sliderValue: step, stepState, jumpOutDisabled}
}) })
} }

@ -13,7 +13,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
const intl = useIntl() const intl = useIntl()
useEffect(() => { useEffect(() => {
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
txNumber: transactionNumber txNumber: transactionNumber
@ -39,7 +39,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
// oninvalid="setCustomValidity('Please provide a valid transaction number, must start with 0x and have length of 22')" // oninvalid="setCustomValidity('Please provide a valid transaction number, must start with 0x and have length of 22')"
// pattern="^0[x,X]+[0-9a-fA-F]{22}" // pattern="^0[x,X]+[0-9a-fA-F]{22}"
// this.state.txNumberInput.setCustomValidity('') // this.state.txNumberInput.setCustomValidity('')
setState(prevState => { setState((prevState) => {
return { return {
...prevState, ...prevState,
txNumber: value txNumber: value
@ -51,37 +51,44 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
updateTxNumberFlag(!inputValue.current.value) updateTxNumberFlag(!inputValue.current.value)
} }
const customJSX = ( const customJSX = (
<div id="debuggerTransactionStartButtonContainer" data-id="debuggerTransactionStartButton" onClick={handleSubmit} className="btn btn-primary btn-sm btn-block text-decoration-none"> <div
id="debuggerTransactionStartButtonContainer"
data-id="debuggerTransactionStartButton"
onClick={handleSubmit}
className="btn btn-primary btn-sm btn-block text-decoration-none"
>
<button <button
className='btn btn-link btn-sm btn-block h-75 p-0 m-0 text-decoration-none' className="btn btn-link btn-sm btn-block h-75 p-0 m-0 text-decoration-none"
id='load' id="load"
onClick={handleSubmit} onClick={handleSubmit}
data-id='debuggerTransactionStartButton' data-id="debuggerTransactionStartButton"
disabled={!state.txNumber} disabled={!state.txNumber}
style={{pointerEvents: 'none', color: 'white'}} style={{pointerEvents: 'none', color: 'white'}}
> >
<span><FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} /></span> <span>
<FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} />
</span>
</button> </button>
</div> </div>
) )
return ( return (
<div className='pb-2 container px-0'> <div className="pb-2 container px-0">
<div className='txContainer'> <div className="txContainer">
<div className='py-1 d-flex justify-content-center w-100 input-group'> <div className="py-1 d-flex justify-content-center w-100 input-group">
<input <input
ref={inputValue} ref={inputValue}
value={state.txNumber} value={state.txNumber}
className='form-control m-0 txinput' className="form-control m-0 txinput"
id='txinput' id="txinput"
type='text' type="text"
onChange={({target: {value}}) => txInputChanged(value)} onChange={({target: {value}}) => txInputChanged(value)}
onInput={txInputOnInput} onInput={txInputOnInput}
placeholder={intl.formatMessage({id: 'debugger.placeholder'})} placeholder={intl.formatMessage({id: 'debugger.placeholder'})}
data-id='debuggerTransactionInput' data-id="debuggerTransactionInput"
disabled={debugging} disabled={debugging}
/> />
</div> </div>
<div className='d-flex justify-content-center w-100 btn-group py-1'> <div className="d-flex justify-content-center w-100 btn-group py-1">
<CustomTooltip <CustomTooltip
placement="bottom" placement="bottom"
tooltipText={<FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} />} tooltipText={<FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} />}
@ -92,7 +99,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
</CustomTooltip> </CustomTooltip>
</div> </div>
</div> </div>
<span id='error' /> <span id="error" />
</div> </div>
) )
} }

@ -14,12 +14,27 @@ export const AssemblyItems = ({ registerEvent }) => {
const asmItemsRef = useRef(null) const asmItemsRef = useRef(null)
useEffect(() => { useEffect(() => {
registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => { registerEvent &&
dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes } }) registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => {
dispatch({
type: 'FETCH_OPCODES_SUCCESS',
payload: {
code,
address,
index,
nextIndexes,
returnInstructionIndexes,
outOfGasInstructionIndexes
}
})
}) })
registerEvent && registerEvent('lineGasCostChanged', (instructionsIndexes: number[], line: []) => { registerEvent &&
dispatch({ type: 'FETCH_INDEXES_FOR_NEW_LINE', payload: { currentLineIndexes: instructionsIndexes || [], line } }) registerEvent('lineGasCostChanged', (instructionsIndexes: number[], line: []) => {
dispatch({
type: 'FETCH_INDEXES_FOR_NEW_LINE',
payload: {currentLineIndexes: instructionsIndexes || [], line}
})
}) })
}, []) }, [])
@ -128,17 +143,32 @@ export const AssemblyItems = ({ registerEvent }) => {
return ( return (
<div className="h-100 border rounded px-1 mt-1 bg-light"> <div className="h-100 border rounded px-1 mt-1 bg-light">
<div className='dropdownpanel'> <div className="dropdownpanel">
<div className='dropdowncontent pb-2'> <div className="dropdowncontent pb-2">
{assemblyItems.display.length == 0 && <div>No data available</div>} {assemblyItems.display.length == 0 && <div>No data available</div>}
<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 (
return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}> <div
<span>{item}</span>{assemblyItems.currentLineIndexes.includes(i) ? <span><i><b> - LINE {assemblyItems.line + 1}</b></i></span> : ' - '} 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> </div>
</div> </div>
</div> </div>

@ -1,10 +1,10 @@
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import DropdownPanel from './dropdown-panel' // eslint-disable-line import DropdownPanel from './dropdown-panel' // eslint-disable-line
export const CalldataPanel = ({ calldata, className = "" }) => { export const CalldataPanel = ({calldata, className = ''}) => {
return ( return (
<div id='calldatapanel' className={className}> <div id="calldatapanel" className={className}>
<DropdownPanel dropdownName='Call Data' calldata={calldata || {}} /> <DropdownPanel dropdownName="Call Data" calldata={calldata || {}} />
</div> </div>
) )
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save