use prettier to format all code files in repo. adjust eslint rules. fix text in vyper

pull/4018/head
Joseph Izang 1 year ago
parent aa7bb9aeaf
commit 665eb5389f
  1. 5
      .eslintrc.json
  2. 3
      .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. 64
      apps/etherscan/src/app/RemixPlugin.tsx
  16. 71
      apps/etherscan/src/app/app.tsx
  17. 71
      apps/etherscan/src/app/components/HeaderWithSettings.tsx
  18. 10
      apps/etherscan/src/app/components/SubmitButton.tsx
  19. 2
      apps/etherscan/src/app/hooks/useLocalStorage.tsx
  20. 4
      apps/etherscan/src/app/layouts/Default.tsx
  21. 16
      apps/etherscan/src/app/routes.tsx
  22. 30
      apps/etherscan/src/app/views/CaptureKeyView.tsx
  23. 4
      apps/etherscan/src/app/views/ErrorView.tsx
  24. 15
      apps/etherscan/src/app/views/HomeView.tsx
  25. 114
      apps/etherscan/src/app/views/ReceiptsView.tsx
  26. 191
      apps/etherscan/src/app/views/VerifyView.tsx
  27. 14
      apps/etherscan/src/main.tsx
  28. 62
      apps/etherscan/webpack.config.js
  29. 69
      apps/remix-ide-e2e/nightwatch.ts
  30. 73
      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. 7
      apps/remix-ide/background.js
  33. 8
      apps/remix-ide/src/app/components/hidden-panel.tsx
  34. 9
      apps/remix-ide/src/app/components/main-panel.tsx
  35. 185
      apps/remix-ide/src/app/components/preload.tsx
  36. 19
      apps/remix-ide/src/app/components/side-panel.tsx
  37. 55
      apps/remix-ide/src/app/components/vertical-icons.tsx
  38. 26
      apps/remix-ide/src/app/plugins/contractFlattener.tsx
  39. 13
      apps/remix-ide/src/app/plugins/notification.tsx
  40. 334
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  41. 75
      apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx
  42. 128
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  43. 28
      apps/remix-ide/src/app/plugins/solidity-script.tsx
  44. 75
      apps/remix-ide/src/app/plugins/solidity-umlgen.tsx
  45. 32
      apps/remix-ide/src/app/providers/abstract-provider.tsx
  46. 53
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  47. 44
      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. 17
      apps/remix-ide/src/app/providers/hardhat-provider.tsx
  52. 35
      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. 13
      apps/remix-ide/src/app/providers/injected-provider-default.tsx
  56. 60
      apps/remix-ide/src/app/providers/injected-provider.tsx
  57. 16
      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. 55
      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. 640
      apps/remix-ide/src/blockchain/blockchain.tsx
  63. 7
      apps/remix-ide/src/index.tsx
  64. 97
      apps/remix-ide/webpack.config.js
  65. 8
      apps/solhint/src/app/App.tsx
  66. 34
      apps/solhint/webpack.config.js
  67. 69
      apps/solidity-compiler/webpack.config.js
  68. 26
      apps/vyper/src/app/app.tsx
  69. 24
      apps/vyper/src/app/components/CompilerButton.tsx
  70. 18
      apps/vyper/src/app/components/LocalUrl.tsx
  71. 58
      apps/vyper/src/app/components/VyperResult.tsx
  72. 8
      apps/vyper/src/app/components/WarnRemote.tsx
  73. 47
      apps/vyper/src/app/utils/compiler.tsx
  74. 62
      apps/vyper/src/app/utils/remix-client.tsx
  75. 14
      apps/vyper/src/main.tsx
  76. 65
      apps/vyper/webpack.config.js
  77. 12
      apps/walletconnect/src/app/app.tsx
  78. 15
      apps/walletconnect/src/app/walletConnectUI.tsx
  79. 5
      apps/walletconnect/src/main.tsx
  80. 66
      apps/walletconnect/webpack.config.js
  81. 24
      libs/remix-debug/test.ts
  82. 34
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  83. 9
      libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx
  84. 67
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  85. 54
      libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx
  86. 23
      libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx
  87. 21
      libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx
  88. 8
      libs/remix-ui/app/src/lib/remix-app/context/context.tsx
  89. 69
      libs/remix-ui/app/src/lib/remix-app/context/provider.tsx
  90. 54
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  91. 49
      libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx
  92. 36
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  93. 11
      libs/remix-ui/debugger-ui/src/hooks/extract-data.tsx
  94. 253
      libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx
  95. 299
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  96. 9
      libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx
  97. 26
      libs/remix-ui/debugger-ui/src/lib/step-manager/step-manager.tsx
  98. 57
      libs/remix-ui/debugger-ui/src/lib/tx-browser/tx-browser.tsx
  99. 78
      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",
"jsx-a11y/anchor-is-valid": "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]
}
},

@ -9,6 +9,5 @@
"trailingComma": "none",
"jsxBracketSameLine": false,
"arrowParens": "always",
"singleAttributePerLine": false,
"ignorePath": ".prettierignore"
"singleAttributePerLine": false
}

@ -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
@ -11,7 +11,7 @@ export const App = () => {
<div className="debugger">
<DebuggerUI debuggerAPI={remix} />
</div>
);
};
)
}
export default App;
export default App

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

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

@ -8,9 +8,9 @@ export const client = new DocGenClient()
const App = () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [themeType, setThemeType] = useState<string>('dark');
const [hasBuild, setHasBuild] = useState<boolean>(false);
const [fileName, setFileName] = useState<string>('');
const [themeType, setThemeType] = useState<string>('dark')
const [hasBuild, setHasBuild] = useState<boolean>(false)
const [fileName, setFileName] = useState<string>('')
useEffect(() => {
const watchThemeSwitch = async () => {
@ -21,19 +21,27 @@ const App = () => {
setHasBuild(true)
setFileName(fileName)
})
client.eventEmitter.on('docsGenerated', (docs: string[]) => {
})
client.eventEmitter.on('docsGenerated', (docs: string[]) => {})
}
watchThemeSwitch()
}, [])
return (
<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>
{fileName && <div className="border-bottom border-top px-2 py-3 justify-center align-items-center d-flex">
<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>
{fileName && (
<div className="border-bottom border-top px-2 py-3 justify-center align-items-center d-flex">
<h6>File: {fileName}</h6>
</div>}
{hasBuild && <button className="btn btn-primary btn-block mt-4" onClick={() => client.generateDocs()}>Generate Docs</button>}
</div>
)}
{hasBuild && (
<button className="btn btn-primary btn-block mt-4" onClick={() => client.generateDocs()}>
Generate Docs
</button>
)}
</div>
)
}

@ -1,4 +1,4 @@
import { useState } from "react";
import { useState } from 'react'
export function useLocalStorage(key: string, initialValue: any) {
// State to store our value
@ -6,32 +6,31 @@ export function useLocalStorage(key: string, initialValue: any) {
const [storedValue, setStoredValue] = useState(() => {
try {
// 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
return item ? JSON.parse(item) : initialValue;
return item ? JSON.parse(item) : initialValue
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
console.log(error)
return initialValue
}
});
})
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value: any) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
const valueToStore = value instanceof Function ? value(storedValue) : value
// Save state
setStoredValue(valueToStore);
setStoredValue(valueToStore)
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) {
// 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 = () => {
return (
<div
style={{
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<img
style={{ paddingBottom: "2em" }}
style={{ paddingBottom: '2em' }}
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>
Please raise an issue:{" "}
<a
style={{ color: "red" }}
href="https://github.com/Machinalabs/remix-ethdoc-plugin/issues"
>
Please raise an issue:{' '}
<a style={{ color: 'red' }} href="https://github.com/Machinalabs/remix-ethdoc-plugin/issues">
Here
</a>
</h5>
</div>
);
};
)
}

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

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

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

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

@ -1,8 +1,8 @@
const {composePlugins, withNx} = require('@nrwl/webpack')
const {withReact} = require('@nrwl/react')
const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => {
@ -12,7 +12,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// add externals
config.externals = {
...config.externals,
solc: 'solc',
solc: 'solc'
}
// add public path
@ -23,18 +23,16 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'],
process: 'process/browser',
}),
new webpack.DefinePlugin({
process: 'process/browser'
}),
new webpack.DefinePlugin({})
)
// souce-map loader
config.module.rules.push({
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
use: ['source-map-loader'],
enforce: 'pre'
})
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
@ -48,13 +46,13 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false,
mangle: false,
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 { PluginClient } from "@remixproject/plugin"
import React from 'react'
import {PluginClient} from '@remixproject/plugin'
import { Receipt, ThemeType } from "./types"
import {Receipt, ThemeType} from './types'
export const AppContext = React.createContext({
apiKey: "",
apiKey: '',
setAPIKey: (value: string) => {
console.log("Set API Key from Context")
console.log('Set API Key from Context')
},
clientInstance: {} as PluginClient,
receipts: [] as Receipt[],
setReceipts: (receipts: Receipt[]) => {
console.log("Calling Set Receipts")
console.log('Calling Set Receipts')
},
contracts: [] as 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) => {
console.log("Calling Set Theme Type")
},
console.log('Calling Set Theme Type')
}
})

@ -1,30 +1,70 @@
import { PluginClient } from '@remixproject/plugin';
import { verify, EtherScanReturn } from './utils/verify';
import { getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus } from './utils';
import {PluginClient} from '@remixproject/plugin'
import {verify, EtherScanReturn} from './utils/verify'
import {
getReceiptStatus,
getEtherScanApi,
getNetworkName,
getProxyContractReceiptStatus
} from './utils'
export class RemixClient extends PluginClient {
loaded() {
return this.onload()
}
async verify (apiKey: 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) => {})
async verify(
apiKey: 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
}
async receiptStatus (receiptGuid: string, apiKey: string, isProxyContract: boolean) {
async receiptStatus(
receiptGuid: string,
apiKey: string,
isProxyContract: boolean
) {
try {
const {network, networkId} = await getNetworkName(this)
if (network === "vm") {
throw new Error("Cannot check the receipt status in the selected network")
if (network === 'vm') {
throw new Error(
'Cannot check the receipt status in the selected network'
)
}
const etherscanApi = getEtherScanApi(networkId)
let receiptStatus
if (isProxyContract) receiptStatus = await getProxyContractReceiptStatus(receiptGuid, apiKey, etherscanApi)
else receiptStatus = await getReceiptStatus(receiptGuid, apiKey, etherscanApi)
if (isProxyContract)
receiptStatus = await getProxyContractReceiptStatus(
receiptGuid,
apiKey,
etherscanApi
)
else
receiptStatus = await getReceiptStatus(
receiptGuid,
apiKey,
etherscanApi
)
return {
message: receiptStatus.result,
succeed: receiptStatus.status === '0' ? false : true

@ -1,22 +1,27 @@
import React, { useState, useEffect, useRef } from "react"
import React, {useState, useEffect, useRef} from 'react'
import {
CompilationFileSources,
CompilationResult,
} from "@remixproject/plugin-api"
CompilationResult
} from '@remixproject/plugin-api'
import { RemixClient } from "./RemixPlugin";
import { createClient } from "@remixproject/plugin-webview";
import {RemixClient} from './RemixPlugin'
import {createClient} from '@remixproject/plugin-webview'
import { AppContext } from "./AppContext"
import { DisplayRoutes } from "./routes"
import {AppContext} from './AppContext'
import {DisplayRoutes} from './routes'
import { useLocalStorage } from "./hooks/useLocalStorage"
import {useLocalStorage} from './hooks/useLocalStorage'
import { getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus } from "./utils"
import { Receipt, ThemeType } from "./types"
import {
getReceiptStatus,
getEtherScanApi,
getNetworkName,
getProxyContractReceiptStatus
} from './utils'
import {Receipt, ThemeType} from './types'
import "./App.css"
import './App.css'
export const getNewContractNames = (compilationResult: CompilationResult) => {
const compiledContracts = compilationResult.contracts
@ -31,11 +36,11 @@ export const getNewContractNames = (compilationResult: CompilationResult) => {
}
const App = () => {
const [apiKey, setAPIKey] = useLocalStorage("apiKey", "")
const [apiKey, setAPIKey] = useLocalStorage('apiKey', '')
const [clientInstance, setClientInstance] = useState(undefined as any)
const [receipts, setReceipts] = useLocalStorage("receipts", [])
const [receipts, setReceipts] = useLocalStorage('receipts', [])
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 clientInstanceRef = useRef(clientInstance)
@ -49,8 +54,9 @@ const App = () => {
const loadClient = async () => {
await client.onload()
setClientInstance(client)
client.on("solidity",
"compilationFinished",
client.on(
'solidity',
'compilationFinished',
(
fileName: string,
source: CompilationFileSources,
@ -61,7 +67,7 @@ const App = () => {
const newContractsToSave: string[] = [
...contractsRef.current,
...newContractsNames,
...newContractsNames
]
const uniqueContracts: string[] = [...new Set(newContractsToSave)]
@ -82,7 +88,10 @@ const App = () => {
useEffect(() => {
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) {
@ -91,17 +100,19 @@ const App = () => {
timer.current = null
}
timer.current = setInterval(async () => {
const { network, networkId } = await getNetworkName(clientInstanceRef.current)
const {network, networkId} = await getNetworkName(
clientInstanceRef.current
)
if (!clientInstanceRef.current) {
return
}
if (network === "vm") {
if (network === 'vm') {
return
}
let newReceipts = receipts
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
if (item.isProxyContract) {
status = await getProxyContractReceiptStatus(
@ -119,13 +130,16 @@ const App = () => {
apiKey,
getEtherScanApi(networkId)
)
if (status.result === "Pass - Verified" || status.result === "Already Verified" ||
status.result === "Successfully Updated") {
if (
status.result === 'Pass - Verified' ||
status.result === 'Already Verified' ||
status.result === 'Successfully Updated'
) {
newReceipts = newReceipts.map((currentReceipt: Receipt) => {
if (currentReceipt.guid === item.guid) {
let res = {
const res = {
...currentReceipt,
status: status.result,
status: status.result
}
if (currentReceipt.isProxyContract) res.message = status.message
return res
@ -135,7 +149,10 @@ const App = () => {
}
}
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) {
clearInterval(timer.current)
@ -157,7 +174,7 @@ const App = () => {
contracts,
setContracts,
themeType,
setThemeType,
setThemeType
}}
>
<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 { AppContext } from "../AppContext"
import {AppContext} from '../AppContext'
interface Props {
title?: string
@ -18,16 +18,24 @@ const HomeIcon: React.FC<IconProps> = ({ from }: IconProps) => {
<NavLink
data-id="home"
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"}
style={ ({ isActive }) => !isActive ? { width: "1.8rem", filter: "contrast(0.5)"} : {width: "1.8rem"}}
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'}
}
state={from}
>
<CustomTooltip
tooltipText='Home'
tooltipId='etherscan-nav-home'
placement='bottom'
tooltipText="Home"
tooltipId="etherscan-nav-home"
placement="bottom"
>
<i className="fas fa-home"></i>
</CustomTooltip>
@ -40,16 +48,24 @@ const ReceiptsIcon: React.FC<IconProps> = ({ from }: IconProps) => {
<NavLink
data-id="receipts"
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"}
style={ ({ isActive }) => !isActive ? { width: "1.8rem", filter: "contrast(0.5)"} : {width: "1.8rem"}}
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'}
}
state={from}
>
<CustomTooltip
tooltipText='Receipts'
tooltipId='etherscan-nav-receipts'
placement='bottom'
tooltipText="Receipts"
tooltipId="etherscan-nav-receipts"
placement="bottom"
>
<i className="fas fa-receipt"></i>
</CustomTooltip>
@ -62,16 +78,24 @@ const SettingsIcon: React.FC<IconProps> = ({ from }: IconProps) => {
<NavLink
data-id="settings"
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"}
style={ ({ isActive }) => !isActive ? { width: "1.8rem", filter: "contrast(0.5)"} : {width: "1.8rem"}}
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'}
}
state={from}
>
<CustomTooltip
tooltipText='Settings'
tooltipId='etherscan-nav-settings'
placement='bottom'
tooltipText="Settings"
tooltipId="etherscan-nav-settings"
placement="bottom"
>
<i className="fas fa-cog"></i>
</CustomTooltip>
@ -79,10 +103,7 @@ const SettingsIcon: React.FC<IconProps> = ({ from }: IconProps) => {
)
}
export const HeaderWithSettings: React.FC<Props> = ({
title = "",
from,
}) => {
export const HeaderWithSettings: React.FC<Props> = ({title = '', from}) => {
return (
<AppContext.Consumer>
{() => (

@ -1,4 +1,4 @@
import React from "react"
import React from 'react'
import {CustomTooltip} from '@remix-ui/helper'
interface Props {
@ -23,10 +23,14 @@ export const SubmitButton: React.FC<Props> = ({
disabled={disable}
>
<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}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
placement='bottom'
placement="bottom"
>
<div>
{!isSubmitting && text}

@ -1,4 +1,4 @@
import { useState } from "react"
import {useState} from 'react'
export function useLocalStorage(key: string, initialValue: any) {
// State to store our value

@ -1,6 +1,6 @@
import React, { PropsWithChildren } from "react"
import React, {PropsWithChildren} from 'react'
import { HeaderWithSettings } from "../components"
import {HeaderWithSettings} from '../components'
interface Props {
from: string

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

@ -1,15 +1,15 @@
import React, { useState } from "react"
import React, {useState} from 'react'
import { Formik, ErrorMessage, Field } from "formik"
import { useNavigate, useLocation } from "react-router-dom"
import {Formik, ErrorMessage, Field} from 'formik'
import {useNavigate, useLocation} from 'react-router-dom'
import { AppContext } from "../AppContext"
import { SubmitButton } from "../components"
import {AppContext} from '../AppContext'
import {SubmitButton} from '../components'
export const CaptureKeyView: React.FC = () => {
const location = useLocation()
const navigate = useNavigate()
const [msg, setMsg] = useState("")
const [msg, setMsg] = useState('')
return (
<AppContext.Consumer>
{({apiKey, clientInstance, setAPIKey}) => {
@ -21,9 +21,9 @@ export const CaptureKeyView: React.FC = () => {
validate={(values) => {
const errors = {} as any
if (!values.apiKey) {
errors.apiKey = "Required"
errors.apiKey = 'Required'
} 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
}}
@ -31,7 +31,7 @@ export const CaptureKeyView: React.FC = () => {
const apiKey = values.apiKey
if (apiKey.length === 34) {
setAPIKey(values.apiKey)
navigate((location && location.state ? location.state : '/'))
navigate(location && location.state ? location.state : '/')
}
}}
>
@ -42,8 +42,8 @@ export const CaptureKeyView: React.FC = () => {
<Field
className={
errors.apiKey && touched.apiKey
? "form-control form-control-sm is-invalid"
: "form-control form-control-sm"
? 'form-control form-control-sm is-invalid'
: 'form-control form-control-sm'
}
type="password"
name="apiKey"
@ -57,7 +57,11 @@ export const CaptureKeyView: React.FC = () => {
</div>
<div>
<SubmitButton text="Save" dataId="save-api-key" disable={errors && errors.apiKey ? true : false } />
<SubmitButton
text="Save"
dataId="save-api-key"
disable={errors && errors.apiKey ? true : false}
/>
</div>
</form>
)}
@ -66,7 +70,7 @@ export const CaptureKeyView: React.FC = () => {
<div
data-id="api-key-result"
className="text-primary mt-4 text-center"
style={{fontSize: "0.8em"}}
style={{fontSize: '0.8em'}}
dangerouslySetInnerHTML={{__html: msg}}
/>
</div>

@ -1,4 +1,4 @@
import React from "react"
import React from 'react'
export const ErrorView: React.FC = () => {
return (
@ -11,7 +11,7 @@ export const ErrorView: React.FC = () => {
/>
<h5>Sorry, something unexpected happened.</h5>
<h5>
Please raise an issue:{" "}
Please raise an issue:{' '}
<a
className="text-danger"
href="https://github.com/ethereum/remix-project/issues"

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

@ -1,12 +1,17 @@
import React, { useState } from "react"
import React, {useState} from 'react'
import { Formik, ErrorMessage, Field } from "formik"
import { getEtherScanApi, getNetworkName, getReceiptStatus, getProxyContractReceiptStatus } from "../utils"
import { Receipt } from "../types"
import { AppContext } from "../AppContext"
import { SubmitButton } from "../components"
import { Navigate } from "react-router-dom"
import { Button } from "react-bootstrap"
import {Formik, ErrorMessage, Field} from 'formik'
import {
getEtherScanApi,
getNetworkName,
getReceiptStatus,
getProxyContractReceiptStatus
} from '../utils'
import {Receipt} from '../types'
import {AppContext} from '../AppContext'
import {SubmitButton} from '../components'
import {Navigate} from 'react-router-dom'
import {Button} from 'react-bootstrap'
import {CustomTooltip} from '@remix-ui/helper'
interface FormValues {
@ -24,10 +29,10 @@ export const ReceiptsView: React.FC = () => {
) => {
try {
const {network, networkId} = await getNetworkName(clientInstance)
if (network === "vm") {
if (network === 'vm') {
setResults({
succeed: false,
message: "Cannot verify in the selected network"
message: 'Cannot verify in the selected network'
})
return
}
@ -51,7 +56,9 @@ export const ReceiptsView: React.FC = () => {
)
setResults({
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)
})
} catch (error: any) {
setResults({
@ -67,17 +74,17 @@ export const ReceiptsView: React.FC = () => {
return !apiKey ? (
<Navigate
to={{
pathname: "/settings"
pathname: '/settings'
}}
/>
) : (
<div>
<Formik
initialValues={{ receiptGuid: "" }}
initialValues={{receiptGuid: ''}}
validate={(values) => {
const errors = {} as any
if (!values.receiptGuid) {
errors.receiptGuid = "Required"
errors.receiptGuid = 'Required'
}
return errors
}}
@ -92,8 +99,8 @@ export const ReceiptsView: React.FC = () => {
<Field
className={
errors.receiptGuid && touched.receiptGuid
? "form-control form-control-sm is-invalid"
: "form-control form-control-sm"
? 'form-control form-control-sm is-invalid'
: 'form-control form-control-sm'
}
type="text"
name="receiptGuid"
@ -117,30 +124,56 @@ export const ReceiptsView: React.FC = () => {
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>
<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>
)}
</Formik>
<div
className={results['succeed'] ? "text-success mt-3 text-center" : "text-danger mt-3 text-center"}
dangerouslySetInnerHTML={{ __html: results.message ? results.message : '' }}
className={
results['succeed']
? 'text-success mt-3 text-center'
: 'text-danger mt-3 text-center'
}
dangerouslySetInnerHTML={{
__html: results.message ? results.message : ''
}}
/>
<ReceiptsTable receipts={receipts} /><br/>
<ReceiptsTable receipts={receipts} />
<br />
<CustomTooltip
tooltipText="Clear the list of receipts"
tooltipId='etherscan-clear-receipts'
placement='bottom'
tooltipId="etherscan-clear-receipts"
placement="bottom"
>
<Button
className="btn-sm"
onClick={() => {
setReceipts([])
}}
>
<Button className="btn-sm" onClick={() => { setReceipts([]) }} >Clear</Button>
Clear
</Button>
</CustomTooltip>
</div>
)
}
}
}}
</AppContext.Consumer>
)
}
@ -162,20 +195,35 @@ const ReceiptsTable: React.FC<{ receipts: Receipt[] }> = ({ receipts }) => {
receipts.map((item: Receipt, index) => {
return (
<tr key={item.guid}>
<td className={(item.status === 'Pass - Verified' || item.status === 'Successfully Updated')
? 'text-success' : (item.status === 'Pending in queue'
? 'text-warning' : (item.status === 'Already Verified'
? 'text-info': 'text-secondary'))}>
<td
className={
item.status === 'Pass - Verified' ||
item.status === 'Successfully Updated'
? 'text-success'
: item.status === 'Pending in queue'
? 'text-warning'
: item.status === 'Already Verified'
? 'text-info'
: 'text-secondary'
}
>
{item.status}
{item.status === 'Successfully Updated' && <CustomTooltip
{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>
</CustomTooltip>
<i
style={{fontSize: 'small'}}
className={
'ml-1 fal fa-info-circle align-self-center'
}
aria-hidden="true"
></i>
</CustomTooltip>
)}
</td>
<td>{item.guid}</td>
</tr>

@ -1,16 +1,14 @@
import React, { useEffect, useRef, useState } from "react"
import React, {useEffect, useRef, useState} from 'react'
import Web3 from 'web3'
import {
PluginClient,
} from "@remixproject/plugin"
import {PluginClient} from '@remixproject/plugin'
import {CustomTooltip} from '@remix-ui/helper'
import { Formik, ErrorMessage, Field } from "formik"
import {Formik, ErrorMessage, Field} from 'formik'
import { SubmitButton } from "../components"
import { Receipt } from "../types"
import { verify } from "../utils/verify"
import { etherscanScripts } from "@remix-project/remix-ws-templates"
import {SubmitButton} from '../components'
import {Receipt} from '../types'
import {verify} from '../utils/verify'
import {etherscanScripts} from '@remix-project/remix-ws-templates'
interface Props {
client: PluginClient
@ -29,11 +27,11 @@ export const VerifyView: React.FC<Props> = ({
apiKey,
client,
contracts,
onVerifiedContract,
onVerifiedContract
}) => {
const [results, setResults] = useState("")
const [networkName, setNetworkName] = useState("Loading...")
const [selectedContract, setSelectedContract] = useState("")
const [results, setResults] = useState('')
const [networkName, setNetworkName] = useState('Loading...')
const [selectedContract, setSelectedContract] = useState('')
const [showConstructorArgs, setShowConstructorArgs] = useState(false)
const [isProxyContract, setIsProxyContract] = useState(false)
const [constructorInputs, setConstructorInputs] = useState([])
@ -41,13 +39,19 @@ export const VerifyView: React.FC<Props> = ({
useEffect(() => {
if (client && client.on) {
client.on("blockchain" as any, 'networkStatus', (result) => {
setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`)
client.on('blockchain' as any, 'networkStatus', (result) => {
setNetworkName(
`${result.network.name} ${
result.network.id !== '-'
? `(Chain id: ${result.network.id})`
: '(Not supported)'
}`
)
})
}
return () => {
// 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])
@ -56,9 +60,22 @@ export const VerifyView: React.FC<Props> = ({
}, [contracts])
const updateConsFields = (contractName) => {
client.call("compilerArtefacts" as any, "getArtefactsByContractName", contractName).then((result) => {
client
.call(
'compilerArtefacts' as any,
'getArtefactsByContractName',
contractName
)
.then((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)
setShowConstructorArgs(true)
} else {
@ -70,22 +87,26 @@ export const VerifyView: React.FC<Props> = ({
const onVerifyContract = async (values: FormValues) => {
const compilationResult = (await client.call(
"solidity",
"getCompilationResult"
'solidity',
'getCompilationResult'
)) as any
if (!compilationResult) {
throw new Error("no compilation result available")
throw new Error('no compilation result available')
}
const constructorValues = []
for (const key in values) {
if (key.startsWith('contractArgValue')) constructorValues.push(values[key])
if (key.startsWith('contractArgValue'))
constructorValues.push(values[key])
}
const web3 = new Web3()
const constructorTypes = constructorInputs.map(e => e.type)
let contractArguments = web3.eth.abi.encodeParameters(constructorTypes, constructorValues)
contractArguments = contractArguments.replace("0x", "")
const constructorTypes = constructorInputs.map((e) => e.type)
let contractArguments = web3.eth.abi.encodeParameters(
constructorTypes,
constructorValues
)
contractArguments = contractArguments.replace('0x', '')
verificationResult.current = await verify(
apiKey,
@ -98,7 +119,7 @@ export const VerifyView: React.FC<Props> = ({
values.expectedImplAddress,
client,
onVerifiedContract,
setResults,
setResults
)
setResults(verificationResult.current['message'])
}
@ -107,33 +128,37 @@ export const VerifyView: React.FC<Props> = ({
<div>
<Formik
initialValues={{
contractName: "",
contractAddress: ""
contractName: '',
contractAddress: ''
}}
validate={(values) => {
const errors = {} as any
if (!values.contractName) {
errors.contractName = "Required"
errors.contractName = 'Required'
}
if (!values.contractAddress) {
errors.contractAddress = "Required"
errors.contractAddress = 'Required'
}
if (values.contractAddress.trim() === "" || !values.contractAddress.startsWith('0x')
|| values.contractAddress.length !== 42) {
errors.contractAddress = "Please enter a valid contract address"
if (
values.contractAddress.trim() === '' ||
!values.contractAddress.startsWith('0x') ||
values.contractAddress.length !== 42
) {
errors.contractAddress = 'Please enter a valid contract address'
}
return errors
}}
onSubmit={(values) => onVerifyContract(values)}
>
{({errors, touched, handleSubmit, handleChange, isSubmitting}) => {
return (<form onSubmit={handleSubmit}>
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="network">Selected Network</label>
<CustomTooltip
tooltipText="Network is fetched from 'Deploy and Run Transactions' plugin's ENVIRONMENT field"
tooltipId='etherscan-impl-address2'
placement='bottom'
tooltipId="etherscan-impl-address2"
placement="bottom"
>
<Field
className="form-control"
@ -149,9 +174,11 @@ export const VerifyView: React.FC<Props> = ({
<Field
as="select"
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"
onChange={async (e) => {
@ -161,7 +188,9 @@ export const VerifyView: React.FC<Props> = ({
}}
>
<option disabled={true} value="">
{ contracts.length ? 'Select a contract' : `--- No compiled contracts ---` }
{contracts.length
? 'Select a contract'
: `--- No compiled contracts ---`}
</option>
{contracts.map((item) => (
<option key={item} value={item}>
@ -175,7 +204,13 @@ export const VerifyView: React.FC<Props> = ({
component="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>
{constructorInputs.map((item, index) => {
return (
@ -191,7 +226,7 @@ export const VerifyView: React.FC<Props> = ({
<CustomTooltip
tooltipText={`value of ${item.name}`}
tooltipId={`etherscan-constructor-value${index}`}
placement='top'
placement="top"
>
<Field
className="form-control m-1"
@ -202,17 +237,16 @@ export const VerifyView: React.FC<Props> = ({
/>
</CustomTooltip>
</div>
)}
)}
)
})}
</div>
<div className="form-group">
<label htmlFor="contractAddress">Contract Address</label>
<Field
className={
errors.contractAddress && touched.contractAddress
? "form-control is-invalid"
: "form-control"
? 'form-control is-invalid'
: 'form-control'
}
type="text"
name="contractAddress"
@ -235,15 +269,26 @@ export const VerifyView: React.FC<Props> = ({
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 className={ isProxyContract ? 'form-group d-block': 'form-group d-none' }>
<label htmlFor="expectedImplAddress">Expected Implementation Address</label>
<div
className={
isProxyContract ? 'form-group d-block' : 'form-group d-none'
}
>
<label htmlFor="expectedImplAddress">
Expected Implementation Address
</label>
<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'
tooltipId='etherscan-impl-address'
placement='bottom'
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"
placement="bottom"
>
<Field
className="form-control"
@ -252,25 +297,36 @@ export const VerifyView: React.FC<Props> = ({
placeholder="verified implementation contract address"
/>
</CustomTooltip>
<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>
<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>
</div>
<SubmitButton dataId="verify-contract" text="Verify"
<SubmitButton
dataId="verify-contract"
text="Verify"
isSubmitting={isSubmitting}
disable={ !contracts.length ||
disable={
!contracts.length ||
!touched.contractName ||
!touched.contractAddress ||
(touched.contractName && errors.contractName) ||
(touched.contractAddress && errors.contractAddress) ||
(networkName === 'VM (Not supported)')
networkName === 'VM (Not supported)'
? true
: false}
: false
}
/>
<br />
<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
type="button"
@ -282,13 +338,18 @@ export const VerifyView: React.FC<Props> = ({
Generate Verification Scripts
</button>
</CustomTooltip>
</form>)
</form>
)
}}
</Formik>
<div
data-id="verify-result"
className={verificationResult.current['succeed'] ? "text-success mt-4 text-center" : "text-danger mt-4 text-center"}
style={{fontSize: "0.8em"}}
className={
verificationResult.current['succeed']
? 'text-success mt-4 text-center'
: 'text-danger mt-4 text-center'
}
style={{fontSize: '0.8em'}}
dangerouslySetInnerHTML={{__html: results}}
/>
{/* <div style={{ display: "block", textAlign: "center", marginTop: "1em" }}>

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

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

@ -7,7 +7,7 @@ module.exports = {
globals_path: '',
test_settings: {
default: {
'default': {
selenium_port: 4444,
selenium_host: 'localhost',
globals: {
@ -20,31 +20,35 @@ module.exports = {
on_failure: true,
on_error: true
},
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: {
browserName: 'chrome',
javascriptEnabled: true,
acceptSslCerts: true,
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
'goog:chromeOptions': {
args: ['window-size=2560,1440',
args: [
'window-size=2560,1440',
'start-fullscreen',
'--no-sandbox',
'--headless',
'--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: {
browserName: 'chrome',
javascriptEnabled: true,
acceptSslCerts: true,
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
'goog:chromeOptions': {
args: ['window-size=2560,1440', 'start-fullscreen', '--no-sandbox']
}
@ -53,40 +57,39 @@ module.exports = {
'chrome-runAndDeploy': {
desiredCapabilities: {
browserName: 'chrome',
javascriptEnabled: true,
acceptSslCerts: true,
'browserName': 'chrome',
'javascriptEnabled': true,
'acceptSslCerts': true,
'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: {
browserName: 'firefox',
javascriptEnabled: true,
acceptSslCerts: true,
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true,
'moz:firefoxOptions': {
args: [
'-width=2560',
'-height=1440'
]
args: ['-width=2560', '-height=1440']
}
}
},
firefox: {
'firefox': {
desiredCapabilities: {
browserName: 'firefox',
javascriptEnabled: true,
acceptSslCerts: true,
'browserName': 'firefox',
'javascriptEnabled': true,
'acceptSslCerts': true,
'moz:firefoxOptions': {
args: [
'-headless',
'-width=2560',
'-height=1440'
]
args: ['-headless', '-width=2560', '-height=1440']
}
}
}

@ -1,4 +1,3 @@
import React, {useEffect, useState} from 'react'
import {RemixPlugin} from './Client'
import {Logger} from './logger'
@ -24,7 +23,19 @@ function App () {
const [log, setLog] = useState<any>()
const [started, setStarted] = useState<boolean>(false)
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) => {
setPayload(target.value)
@ -32,7 +43,13 @@ function App () {
useEffect(() => {
client.onload(async () => {
const customProfiles = ['menuicons', 'tabs', 'solidityUnitTesting', 'hardhat-provider', 'notification']
const customProfiles = [
'menuicons',
'tabs',
'solidityUnitTesting',
'hardhat-provider',
'notification'
]
client.testCommand = async (data: any) => {
console.log(data)
@ -44,7 +61,7 @@ function App () {
const p = await client.call('manager', 'getProfile', name)
addProfiles = [...addProfiles, p]
}
setProfiles(profiles => [...profiles, ...addProfiles])
setProfiles((profiles) => [...profiles, ...addProfiles])
profiles.map((profile: Profile) => {
if (profile.events) {
@ -77,7 +94,9 @@ function App () {
let ob: any = null
try {
ob = JSON.parse(payload)
if (ob && !Array.isArray(ob)) { ob = [ob] }
if (ob && !Array.isArray(ob)) {
ob = [ob]
}
} catch (e) {}
const args = ob || [payload]
setStarted(true)
@ -97,13 +116,14 @@ function App () {
return (
<div className="App container-fluid">
<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>
<Logger id='methods' log={log}></Logger>
<Logger id="methods" log={log}></Logger>
<label>events</label>
<Logger id='events' log={events}></Logger>
<Logger id="events" log={events}></Logger>
<input
className='form-control w-100'
className="form-control w-100"
type="text"
id="payload"
placeholder="Enter payload here..."
@ -113,14 +133,37 @@ function App () {
/>
{profiles.map((profile: Profile) => {
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) => {
return <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>
const events = profile.events
? profile.events.map((event: string) => {
return (
<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>
)
}

@ -1,9 +1,13 @@
import React from 'react'
interface loggerProps {
log: any,
log: any
id: string
}
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,10 @@
chrome.browserAction.onClicked.addListener(function (tab) {
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
})
}
)
})

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

@ -59,7 +59,14 @@ export class MainPanel extends AbstractPanel {
}
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) {

@ -5,12 +5,14 @@ import * as packageJson from '../../../../../package.json'
import {fileSystem, fileSystems} from '../files/fileSystem'
import {indexedDBFileSystem} from '../files/filesystems/indexedDB'
import {localStorageFS} from '../files/filesystems/localStorage'
import { fileSystemUtility, migrationTestData } from '../files/filesystems/fileSystemUtility'
import {
fileSystemUtility,
migrationTestData
} from '../files/filesystems/fileSystemUtility'
import './styles/preload.css'
const _paq = window._paq = window._paq || []
const _paq = (window._paq = window._paq || [])
export const Preload = () => {
const [supported, setSupported] = useState<boolean>(true)
const [error, setError] = useState<boolean>(false)
const [showDownloader, setShowDownloader] = useState<boolean>(false)
@ -18,12 +20,25 @@ export const Preload = () => {
const remixIndexedDB = useRef<fileSystem>(new indexedDBFileSystem())
const localStorageFileSystem = useRef<fileSystem>(new localStorageFS())
// 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 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:')
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 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() {
import('../../app').then((AppComponent) => {
import('../../app')
.then((AppComponent) => {
const appComponent = new AppComponent.default()
appComponent.run().then(() => {
render(
@ -33,7 +48,8 @@ export const Preload = () => {
document.getElementById('root')
)
})
}).catch(err => {
})
.catch((err) => {
_paq.push(['trackEvent', 'Preload', 'error', err && err.message])
console.error('Error loading Remix:', err)
setError(true)
@ -43,20 +59,35 @@ export const Preload = () => {
const downloadBackup = async () => {
setShowDownloader(false)
const fsUtility = new fileSystemUtility()
await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage'])
await fsUtility.downloadBackup(
remixFileSystems.current.fileSystems['localstorage']
)
await migrateAndLoad()
}
const migrateAndLoad = async () => {
setShowDownloader(false)
const fsUtility = new fileSystemUtility()
const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current)
_paq.push(['trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail'])
const migrationResult = await fsUtility.migrate(
localStorageFileSystem.current,
remixIndexedDB.current
)
_paq.push([
'trackEvent',
'Migrate',
'result',
migrationResult ? 'success' : 'fail'
])
await setFileSystems()
}
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) {
console.log(fsLoaded.name + ' activated')
_paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name])
@ -70,75 +101,123 @@ export const Preload = () => {
const testmigration = async () => {
if (testmigrationResult.current) {
const fsUtility = new fileSystemUtility()
fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs)
fsUtility.populateWorkspace(
migrationTestData,
remixFileSystems.current.fileSystems['localstorage'].fs
)
}
}
useEffect(() => {
async function loadStorage() {
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(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 testmigration()
remixIndexedDB.current.loaded && await remixIndexedDB.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 && await setFileSystems()
remixIndexedDB.current.loaded &&
(await remixIndexedDB.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 && (await setFileSystems())
}
loadStorage()
}, [])
return <>
<div className='preload-container'>
<div className='preload-logo pb-4'>
return (
<>
<div className="preload-container">
<div className="preload-logo pb-4">
{logo}
<div className="info-secondary splash">
REMIX IDE
<br />
<span className='version'> v{packageJson.version}</span>
<span className="version"> v{packageJson.version}</span>
</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>
{!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> : null}
{error ?
<div className='preload-info-container alert alert-danger text-left'>
An unknown error has occurred while loading the application.<br></br>
) : null}
{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>
<div className='pt-2'>
Windows:<br></br>
- Chrome: CTRL + F5 or CTRL + Reload Button<br></br>
- Firefox: CTRL + SHIFT + R or CTRL + F5<br></br>
<div className="pt-2">
Windows:<br></br>- Chrome: CTRL + F5 or CTRL + Reload Button
<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 className='pt-2'>
MacOS:<br></br>
- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
<div className="pt-2">
Linux:<br></br>- Chrome & FireFox: CTRL + SHIFT + R<br></br>
</div>
<div className='pt-2'>
Linux:<br></br>
- Chrome & FireFox: CTRL + SHIFT + R<br></br>
</div>
</div> : null}
{showDownloader ?
<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.
) : null}
{showDownloader ? (
<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.
<br></br>
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 onClick={async () => { await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup</div>
</div> : null}
{(supported && !error && !showDownloader) ?
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
onClick={async () => {
await migrateAndLoad()
}}
data-id="skipbackup-btn"
className="btn btn-primary mt-1"
>
skip backup
</div>
</div>
) : null}
{supported && !error && !showDownloader ? (
<div>
<i className="fas fa-spinner fa-spin fa-2x"></i>
</div> : null}
</div>
) : null}
</div>
</>
)
}
const logo = <svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100">
const logo = (
<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="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" />
</svg>
)

@ -56,7 +56,8 @@ export class SidePanel extends AbstractPanel {
}
removeView(profile) {
if (this.plugins[profile.name].active) this.call('menuicons', 'select', 'filePanel')
if (this.plugins[profile.name].active)
this.call('menuicons', 'select', 'filePanel')
super.removeView(profile)
this.emit('pluginDisabled', profile.name)
this.call('menuicons', 'unlinkContent', profile)
@ -85,12 +86,22 @@ export class SidePanel extends AbstractPanel {
render() {
return (
<section className='panel plugin-manager'> <PluginViewWrapper plugin={this} /></section>
);
<section className="panel plugin-manager">
{' '}
<PluginViewWrapper plugin={this} />
</section>
)
}
updateComponent(state: any) {
return <RemixPluginPanel header={<RemixUIPanelHeader plugins={state.plugins}></RemixUIPanelHeader>} plugins={state.plugins} />
return (
<RemixPluginPanel
header={
<RemixUIPanelHeader plugins={state.plugins}></RemixUIPanelHeader>
}
plugins={state.plugins}
/>
)
}
renderComponent() {

@ -3,7 +3,10 @@ import React from 'react'
import packageJson from '../../../../../package.json'
import {Plugin} from '@remixproject/engine'
import {EventEmitter} from 'events'
import { IconRecord, RemixUiVerticalIconsPanel } from '@remix-ui/vertical-icons-panel'
import {
IconRecord,
RemixUiVerticalIconsPanel
} from '@remix-ui/vertical-icons-panel'
import {Profile} from '@remixproject/plugin-utils'
import {PluginViewWrapper} from '@remix-ui/helper'
@ -29,29 +32,48 @@ export class VerticalIcons extends Plugin {
}
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,
isRequired: fixedOrder.indexOf(value.profile.name) > -1
}}).sort((a,b) => {
}
})
.sort((a, b) => {
return a.timestamp - b.timestamp
})
const required = divived.filter((value) => value.isRequired).sort((a,b) => {
return fixedOrder.indexOf(a.profile.name) - fixedOrder.indexOf(b.profile.name)
const required = divived
.filter((value) => value.isRequired)
.sort((a, b) => {
return (
fixedOrder.indexOf(a.profile.name) -
fixedOrder.indexOf(b.profile.name)
)
})
const sorted: IconRecord[] = [
...required,
...divived.filter((value) => { return !value.isRequired })
...divived.filter((value) => {
return !value.isRequired
})
]
this.dispatch({
verticalIconsPlugin: this,
icons: sorted
})
}
setDispatch(dispatch: React.Dispatch<any>) {
@ -75,7 +97,12 @@ export class VerticalIcons extends Plugin {
this.icons[profile.name] = {
profile: profile,
active: false,
canbeDeactivated: await this.call('manager', 'canDeactivate', this.profile, profile),
canbeDeactivated: await this.call(
'manager',
'canDeactivate',
this.profile,
profile
),
timestamp: Date.now()
}
this.renderComponent()
@ -112,15 +139,19 @@ export class VerticalIcons extends Plugin {
}
updateComponent(state: any) {
return <RemixUiVerticalIconsPanel
return (
<RemixUiVerticalIconsPanel
verticalIconsPlugin={state.verticalIconsPlugin}
icons={state.icons}
/>
)
}
render() {
return (
<div id='icon-panel'><PluginViewWrapper plugin={this} /></div>
);
<div id="icon-panel">
<PluginViewWrapper plugin={this} />
</div>
)
}
}

@ -1,9 +1,13 @@
import React from 'react'
import {Plugin} from '@remixproject/engine'
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 = {
name: 'contractflattener',
@ -11,25 +15,28 @@ const profile = {
description: 'Flatten solidity contracts',
methods: ['flattenAContract', 'flattenContract'],
events: [],
maintainedBy: 'Remix',
maintainedBy: 'Remix'
}
export class ContractFlattener extends Plugin {
triggerFlattenContract: boolean = false
constructor() {
super(profile)
}
onActivation(): void {
this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => {
this.on(
'solidity',
'compilationFinished',
async (file, source, languageVersion, data, input, version) => {
if (data.sources && Object.keys(data.sources).length > 1) {
if (this.triggerFlattenContract) {
this.triggerFlattenContract = false
await this.flattenContract(source, file, data)
}
}
})
}
)
_paq.push(['trackEvent', 'plugin', 'activated', 'contractFlattener'])
}
@ -48,8 +55,11 @@ export class ContractFlattener extends Plugin {
* Takes the flattened result, writes it to a file and returns the result.
* @returns {Promise<string>}
*/
async flattenContract (source: { sources: any, target: string },
filePath: string, data: { contracts: any, sources: any }): Promise<string> {
async flattenContract(
source: {sources: any; target: string},
filePath: string,
data: {contracts: any; sources: any}
): Promise<string> {
const appendage = '_flattened.sol'
const normalized = normalizeContractPath(filePath)
const path = `${normalized[normalized.length - 2]}${appendage}`

@ -1,11 +1,15 @@
import {Plugin} from '@remixproject/engine'
import { LibraryProfile, MethodApi, StatusEvents } from '@remixproject/plugin-utils'
import {
LibraryProfile,
MethodApi,
StatusEvents
} from '@remixproject/plugin-utils'
import {AppModal} from '@remix-ui/app'
import {AlertModal} from '@remix-ui/app'
import {dispatchModalInterface} from '@remix-ui/app'
interface INotificationApi {
events: StatusEvents,
events: StatusEvents
methods: {
modal: (args: AppModal) => void
alert: (args: AlertModal) => void
@ -20,7 +24,10 @@ const profile:LibraryProfile<INotificationApi> = {
methods: ['modal', 'alert', 'toast']
}
export class NotificationPlugin extends Plugin implements MethodApi<INotificationApi> {
export class NotificationPlugin
extends Plugin
implements MethodApi<INotificationApi>
{
dispatcher: dispatchModalInterface
constructor() {
super(profile)

@ -6,32 +6,80 @@ import { CompilationResult } from '@remix-project/remix-solidity'
import CodeParserGasService from './services/code-parser-gas-service'
import CodeParserCompiler from './services/code-parser-compiler'
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 {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 {antlr} from './types'
import {ParseResult} from './types/antlr-types'
const profile: Profile = {
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: [],
version: '0.0.1'
}
export function isNodeDefinition(node: genericASTNode) {
return node.nodeType === 'ContractDefinition' ||
return (
node.nodeType === 'ContractDefinition' ||
node.nodeType === 'FunctionDefinition' ||
node.nodeType === 'ModifierDefinition' ||
node.nodeType === 'VariableDeclaration' ||
node.nodeType === 'StructDefinition' ||
node.nodeType === 'EventDefinition'
)
}
export type genericASTNode =
ContractDefinitionAstNode
| ContractDefinitionAstNode
| FunctionDefinitionAstNode
| ModifierDefinitionAstNode
| VariableDeclarationAstNode
@ -51,13 +99,12 @@ interface declarationIndexNode {
}
interface codeParserIndex {
declarations: declarationIndexNode,
flatReferences: flatReferenceIndexNode,
declarations: declarationIndexNode
flatReferences: flatReferenceIndexNode
nodesPerFile: any
}
export class CodeParser extends Plugin {
compilerAbstract: CompilerAbstract
currentFile: string
nodeIndex: codeParserIndex
@ -80,7 +127,6 @@ export class CodeParser extends Plugin {
debuggerIsOn: boolean = false
constructor(astWalker: any) {
super(profile)
this.astWalker = astWalker
@ -92,31 +138,52 @@ export class CodeParser extends Plugin {
}
async handleChangeEvents() {
const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion')
const completionSettings = await this.call(
'config',
'getAppParameter',
'auto-completion'
)
if (completionSettings) {
this.antlrService.enableWorker()
} else {
this.antlrService.disableWorker()
}
const showGasSettings = await this.call('config', 'getAppParameter', 'show-gas')
const showErrorSettings = await this.call('config', 'getAppParameter', 'display-errors')
if (showGasSettings || showErrorSettings || completionSettings || this.debuggerIsOn) {
const showGasSettings = await this.call(
'config',
'getAppParameter',
'show-gas'
)
const showErrorSettings = await this.call(
'config',
'getAppParameter',
'display-errors'
)
if (
showGasSettings ||
showErrorSettings ||
completionSettings ||
this.debuggerIsOn
) {
await this.compilerService.compile()
}
}
async onActivation() {
this.gasService = new CodeParserGasService(this)
this.compilerService = new CodeParserCompiler(this)
this.antlrService = new CodeParserAntlrService(this)
this.importService = new CodeParserImports(this)
this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService)
this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService)
this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(
this.antlrService
)
this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService)
this.getANTLRBlockAtPosition = this.antlrService.getANTLRBlockAtPosition.bind(this.antlrService)
this.setCurrentFileAST = this.antlrService.setCurrentFileAST.bind(this.antlrService)
this.getANTLRBlockAtPosition =
this.antlrService.getANTLRBlockAtPosition.bind(this.antlrService)
this.setCurrentFileAST = this.antlrService.setCurrentFileAST.bind(
this.antlrService
)
this.getImports = this.importService.getImports.bind(this.importService)
this.on('editor', 'didChangeFile', async (file) => {
@ -138,7 +205,11 @@ export class CodeParser extends Plugin {
this.on('fileManager', 'currentFileChanged', async () => {
await this.call('editor', 'discardLineTexts')
const completionSettings = await this.call('config', 'getAppParameter', 'auto-completion')
const completionSettings = await this.call(
'config',
'getAppParameter',
'auto-completion'
)
if (completionSettings) {
this.antlrService.setCurrentFileAST()
}
@ -189,7 +260,7 @@ export class CodeParser extends Plugin {
}
getSubNodes<T extends genericASTNode>(node: T): number[] {
return node.nodeType == "ContractDefinition" && node.contractDependencies;
return node.nodeType == 'ContractDefinition' && node.contractDependencies
}
/**
@ -200,7 +271,11 @@ export class CodeParser extends Plugin {
_buildIndex(compilationResult: CompilationResult, source) {
if (compilationResult && compilationResult.sources) {
const callback = (node: genericASTNode) => {
if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) {
if (
node &&
'referencedDeclaration' in node &&
node.referencedDeclaration
) {
if (!this.nodeIndex.declarations[node.referencedDeclaration]) {
this.nodeIndex.declarations[node.referencedDeclaration] = []
}
@ -211,9 +286,7 @@ export class CodeParser extends Plugin {
for (const s in compilationResult.sources) {
this.astWalker.walkFull(compilationResult.sources[s].ast, callback)
}
}
}
// NODE HELPERS
@ -232,16 +305,32 @@ export class CodeParser extends Plugin {
return '(' + params.toString() + ')'
}
_flatNodeList(contractNode: ContractDefinitionAstNode, fileName: string, inScope: boolean, compilatioResult: any) {
_flatNodeList(
contractNode: ContractDefinitionAstNode,
fileName: string,
inScope: boolean,
compilatioResult: any
) {
const index = {}
const contractName: string = contractNode.name
const callback = (node) => {
if (inScope && node.scope !== contractNode.id
&& !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition'))
if (
inScope &&
node.scope !== contractNode.id &&
!(
node.nodeType === 'EnumDefinition' ||
node.nodeType === 'EventDefinition' ||
node.nodeType === 'ModifierDefinition'
)
)
return
if (inScope) node.isClassNode = true;
node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult)
if (inScope) node.isClassNode = true
node.gasEstimate = this._getContractGasEstimate(
node,
contractName,
fileName,
compilatioResult
)
node.functionName = node.name + this._getInputParams(node)
node.contractName = contractName
node.contractId = contractNode.id
@ -251,17 +340,37 @@ export class CodeParser extends Plugin {
return index
}
_extractFileNodes(fileName: string, compilationResult: lastCompilationResult) {
if (compilationResult && compilationResult.data.sources && compilationResult.data.sources[fileName]) {
_extractFileNodes(
fileName: string,
compilationResult: lastCompilationResult
) {
if (
compilationResult &&
compilationResult.data.sources &&
compilationResult.data.sources[fileName]
) {
const source = compilationResult.data.sources[fileName]
const nodesByContract: any = {}
nodesByContract.imports = {}
nodesByContract.contracts = {}
this.astWalker.walkFull(source.ast, async (node) => {
if (node.nodeType === 'ContractDefinition') {
const flatNodes = this._flatNodeList(node, fileName, false, compilationResult)
node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilationResult)
nodesByContract.contracts[node.name] = { contractDefinition: node, contractNodes: flatNodes }
const flatNodes = this._flatNodeList(
node,
fileName,
false,
compilationResult
)
node.gasEstimate = this._getContractGasEstimate(
node,
node.name,
fileName,
compilationResult
)
nodesByContract.contracts[node.name] = {
contractDefinition: node,
contractNodes: flatNodes
}
const baseNodes = {}
const baseNodesWithBaseContractScope = {}
if (node.linearizedBaseContracts) {
@ -271,11 +380,12 @@ export class CodeParser extends Plugin {
const callback = (node) => {
node.contractName = (baseContract as any).name
node.contractId = (baseContract as any).id
node.isBaseNode = true;
node.isBaseNode = true
baseNodes[node.id] = node
if ((node.scope && node.scope === baseContract.id)
|| node.nodeType === 'EnumDefinition'
|| node.nodeType === 'EventDefinition'
if (
(node.scope && node.scope === baseContract.id) ||
node.nodeType === 'EnumDefinition' ||
node.nodeType === 'EventDefinition'
) {
baseNodesWithBaseContractScope[node.id] = node
}
@ -283,7 +393,7 @@ export class CodeParser extends Plugin {
for (const member of node.members) {
member.contractName = (baseContract as any).name
member.contractId = (baseContract as any).id
member.isBaseNode = true;
member.isBaseNode = true
}
}
}
@ -292,14 +402,15 @@ export class CodeParser extends Plugin {
}
}
nodesByContract.contracts[node.name].baseNodes = baseNodes
nodesByContract.contracts[node.name].baseNodesWithBaseContractScope = baseNodesWithBaseContractScope
nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult)
nodesByContract.contracts[node.name].baseNodesWithBaseContractScope =
baseNodesWithBaseContractScope
nodesByContract.contracts[node.name].contractScopeNodes =
this._flatNodeList(node, fileName, true, compilationResult)
}
if (node.nodeType === 'ImportDirective') {
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)
for (const subNode of importedNode.nodes) {
nodesByContract.imports[subNode.id] = subNode
@ -311,9 +422,15 @@ export class CodeParser extends Plugin {
}
}
_getContractGasEstimate(node: ContractDefinitionAstNode | FunctionDefinitionAstNode, contractName: string, fileName: string, compilationResult: lastCompilationResult) {
const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile]
_getContractGasEstimate(
node: ContractDefinitionAstNode | FunctionDefinitionAstNode,
contractName: string,
fileName: string,
compilationResult: lastCompilationResult
) {
const contracts =
compilationResult.data.contracts &&
compilationResult.data.contracts[this.currentFile]
for (const name in contracts) {
if (name === contractName) {
const contract = contracts[name]
@ -327,15 +444,21 @@ export class CodeParser extends Plugin {
const fn = fnName + this._getInputParams(node)
if (visibility === 'public' || visibility === 'external') {
executionCost = estimationObj === null ? '-' : estimationObj.external[fn]
executionCost =
estimationObj === null ? '-' : estimationObj.external[fn]
} else if (visibility === 'private' || visibility === 'internal') {
executionCost = estimationObj === null ? '-' : estimationObj.internal[fn]
executionCost =
estimationObj === null ? '-' : estimationObj.internal[fn]
}
return {executionCost}
} else {
return {
creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost,
codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost,
creationCost:
estimationObj === null ? '-' : estimationObj.creation.totalCost,
codeDepositCost:
estimationObj === null
? '-'
: estimationObj.creation.codeDepositCost
}
}
}
@ -349,16 +472,38 @@ export class CodeParser extends Plugin {
* @param type
* @returns
*/
async nodesAtPosition(position: number, type = ''): Promise<genericASTNode[]> {
async nodesAtPosition(
position: number,
type = ''
): Promise<genericASTNode[]> {
let lastCompilationResult = this.compilerAbstract
if (this.debuggerIsOn) {
lastCompilationResult = await this.call('compilerArtefacts', 'get', '__last')
lastCompilationResult = await this.call(
'compilerArtefacts',
'get',
'__last'
)
this.currentFile = await this.call('fileManager', 'file')
}
if (!lastCompilationResult) return []
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]) {
const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(type, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file])
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]
) {
const nodes: genericASTNode[] = sourceMappingDecoder.nodesAtPosition(
type,
position,
lastCompilationResult.data.sources[this.currentFile] ||
lastCompilationResult.data.sources[urlFromPath.file]
)
return nodes
}
return []
@ -383,7 +528,8 @@ export class CodeParser extends Plugin {
* @returns
*/
async getDeclaration(id: number) {
if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) return this.nodeIndex.declarations[id]
if (this.nodeIndex.declarations && this.nodeIndex.declarations[id])
return this.nodeIndex.declarations[id]
}
/**
@ -417,7 +563,7 @@ export class CodeParser extends Plugin {
* @returns
*/
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 null
@ -436,7 +582,7 @@ export class CodeParser extends Plugin {
node = nodes[nodes.length - 1]
nodeDefinition = node
if (!isNodeDefinition(node)) {
nodeDefinition = await this.declarationOf(node) || node
nodeDefinition = (await this.declarationOf(node)) || node
}
if (node.nodeType === 'ImportDirective') {
for (const key in this.nodeIndex.flatReferences) {
@ -457,29 +603,39 @@ export class CodeParser extends Plugin {
if (!nodeDefinition) nodeDefinition = node
}
}
if (nodeDefinition && nodeDefinition.type && nodeDefinition.type === 'Identifier') {
if (
nodeDefinition &&
nodeDefinition.type &&
nodeDefinition.type === 'Identifier'
) {
const nodeForIdentifier = await this.findIdentifier(nodeDefinition)
if (nodeForIdentifier) nodeDefinition = nodeForIdentifier
}
return nodeDefinition
}
}
}
async getContractNodes(contractName: string) {
if (this.nodeIndex.nodesPerFile
&& this.nodeIndex.nodesPerFile[this.currentFile]
&& this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName]
&& this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName].contractNodes) {
return this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName]
if (
this.nodeIndex.nodesPerFile &&
this.nodeIndex.nodesPerFile[this.currentFile] &&
this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName] &&
this.nodeIndex.nodesPerFile[this.currentFile].contracts[contractName]
.contractNodes
) {
return this.nodeIndex.nodesPerFile[this.currentFile].contracts[
contractName
]
}
return false
}
async getCurrentFileNodes() {
if (this.nodeIndex.nodesPerFile
&& this.nodeIndex.nodesPerFile[this.currentFile]) {
if (
this.nodeIndex.nodesPerFile &&
this.nodeIndex.nodesPerFile[this.currentFile]
) {
return this.nodeIndex.nodesPerFile[this.currentFile]
}
return false
@ -535,8 +691,6 @@ export class CodeParser extends Plugin {
return imported
}
/**
*
* @param node
@ -553,7 +707,7 @@ export class CodeParser extends Plugin {
}
}
}
if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) {
if (node && 'referencedDeclaration' in node && node.referencedDeclaration) {
highlights(node.referencedDeclaration)
const current = this.nodeIndex.flatReferences[node.referencedDeclaration]
results.push(current)
@ -586,8 +740,6 @@ export class CodeParser extends Plugin {
return this.nodeIndex.flatReferences
}
/**
*
* @param node
@ -598,7 +750,9 @@ export class CodeParser extends Plugin {
const position = await this.positionOfDefinition(node)
if (this.compilerAbstract && this.compilerAbstract.source && position) {
const fileName = this.compilerAbstract.getSourceName(position.file)
return lineColumn ? `${fileName} ${lineColumn.start.line}:${lineColumn.start.column}` : null
return lineColumn
? `${fileName} ${lineColumn.start.line}:${lineColumn.start.column}`
: null
}
return ''
}
@ -617,8 +771,13 @@ export class CodeParser extends Plugin {
async getLineColumnOfPosition(position: any) {
if (position) {
const fileName = this.compilerAbstract.getSourceName(position.file)
const lineBreaks = sourceMappingDecoder.getLinebreakPositions(this.compilerAbstract.source.sources[fileName].content)
const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn(position, lineBreaks)
const lineBreaks = sourceMappingDecoder.getLinebreakPositions(
this.compilerAbstract.source.sources[fileName].content
)
const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn(
position,
lineBreaks
)
return lineColumn
}
}
@ -629,9 +788,13 @@ export class CodeParser extends Plugin {
* @returns
*/
async getNodeDocumentation(node: genericASTNode) {
if (("documentation" in node) && node.documentation && (node.documentation as any).text) {
let text = '';
(node.documentation as any).text.split('\n').forEach(line => {
if (
'documentation' in node &&
node.documentation &&
(node.documentation as any).text
) {
let text = ''
;(node.documentation as any).text.split('\n').forEach((line) => {
text += `${line.trim()}\n`
})
return text
@ -644,18 +807,17 @@ export class CodeParser extends Plugin {
* @returns
*/
async getVariableDeclaration(node: any) {
const nodeVisibility = node.visibility && node.visibility.length ? node.visibility + ' ' : ''
const nodeVisibility =
node.visibility && node.visibility.length ? node.visibility + ' ' : ''
const nodeName = node.name && node.name.length ? node.name : ''
if (node.typeDescriptions && node.typeDescriptions.typeString) {
return `${node.typeDescriptions.typeString} ${nodeVisibility}${nodeName}`
} else {
if (node.typeName && node.typeName.name) {
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}`
}
else {
} else {
return `${nodeName}${nodeName}`
}
}
@ -667,7 +829,8 @@ export class CodeParser extends Plugin {
* @returns
*/
async getFunctionParamaters(node: any) {
const localParam = (node.parameters && node.parameters.parameters) || (node.parameters)
const localParam =
(node.parameters && node.parameters.parameters) || node.parameters
if (localParam) {
const params = []
for (const param of localParam) {
@ -683,7 +846,7 @@ export class CodeParser extends Plugin {
* @returns
*/
async getFunctionReturnParameters(node: any) {
const localParam = (node.returnParameters && node.returnParameters.parameters)
const localParam = node.returnParameters && node.returnParameters.parameters
if (localParam) {
const params = []
for (const param of localParam) {
@ -692,7 +855,4 @@ export class CodeParser extends Plugin {
return `(${params.join(', ')})`
}
}
}

@ -2,7 +2,10 @@ import React from 'react' // eslint-disable-line
import {FormattedMessage} from 'react-intl'
import {Plugin} from '@remixproject/engine'
import {AppModal} from '@remix-ui/app'
import { PermissionHandlerDialog, PermissionHandlerValue } from '@remix-ui/permission-handler'
import {
PermissionHandlerDialog,
PermissionHandlerValue
} from '@remix-ui/permission-handler'
import {Profile} from '@remixproject/plugin-utils'
const profile = {
@ -46,14 +49,20 @@ 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) {
set
? this.sessionPermissions[to.name][method][from.name] = {}
? (this.sessionPermissions[to.name][method][from.name] = {})
: delete this.sessionPermissions[to.name][method][from.name]
} else {
set
? this.permissions[to.name][method][from.name] = {}
? (this.permissions[to.name][method][from.name] = {})
: delete this.permissions[to.name][method][from.name]
}
}
@ -65,7 +74,9 @@ export class PermissionHandlerPlugin extends Plugin {
}
notAllowWarning(from: Profile, to: Profile, method: string) {
return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.`
return `${
from.displayName || from.name
} is not allowed to call ${method} method of ${to.displayName || to.name}.`
}
async getTheme() {
@ -80,20 +91,33 @@ export class PermissionHandlerPlugin extends Plugin {
* @param {string} message from the caller plugin to add more details if needed
* @returns {Promise<boolean>}
*/
async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
async askPermission(
from: Profile,
to: Profile,
method: string,
message: string,
sensitiveCall: boolean
) {
try {
if (sensitiveCall) {
if (!this.sessionPermissions[to.name]) this.sessionPermissions[to.name] = {}
if (!this.sessionPermissions[to.name][method]) this.sessionPermissions[to.name][method] = {}
if (!this.sessionPermissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall)
if (!this.sessionPermissions[to.name])
this.sessionPermissions[to.name] = {}
if (!this.sessionPermissions[to.name][method])
this.sessionPermissions[to.name][method] = {}
if (!this.sessionPermissions[to.name][method][from.name])
return this.openPermission(from, to, method, message, sensitiveCall)
} else {
this.permissions = this._getFromLocal()
if (!this.permissions[to.name]) this.permissions[to.name] = {}
if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {}
if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall)
if (!this.permissions[to.name][method])
this.permissions[to.name][method] = {}
if (!this.permissions[to.name][method][from.name])
return this.openPermission(from, to, method, message, sensitiveCall)
}
const { allow, hash } = sensitiveCall ? this.sessionPermissions[to.name][method][from.name] : this.permissions[to.name][method][from.name]
const {allow, hash} = sensitiveCall
? this.sessionPermissions[to.name][method][from.name]
: this.permissions[to.name][method][from.name]
if (!allow) {
const warning = this.notAllowWarning(from, to, method)
this.call('notification', 'toast', warning)
@ -107,7 +131,13 @@ export class PermissionHandlerPlugin extends Plugin {
}
}
async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
async openPermission(
from: Profile,
to: Profile,
method: string,
message: string,
sensitiveCall: boolean
) {
let remember
if (sensitiveCall) {
remember = this.sessionPermissions[to.name][method][from.name]
@ -124,10 +154,21 @@ export class PermissionHandlerPlugin extends Plugin {
}
const modal: AppModal = {
id: 'PermissionHandler',
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />,
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>,
okLabel: <FormattedMessage id='permissionHandler.accept' />,
cancelLabel: <FormattedMessage id='permissionHandler.decline' />
title: (
<FormattedMessage
id="permissionHandler.permissionNeededFor"
values={{to: to.displayName || to.name}}
/>
),
message: (
<PermissionHandlerDialog
plugin={this}
theme={await this.getTheme()}
value={value}
></PermissionHandlerDialog>
),
okLabel: <FormattedMessage id="permissionHandler.accept" />,
cancelLabel: <FormattedMessage id="permissionHandler.decline" />
}
const result = await this.call('notification', 'modal', modal)

@ -14,15 +14,27 @@ const profile = {
name: 'remixd',
displayName: 'RemixD',
url: 'ws://127.0.0.1:65520',
methods: ['folderIsReadOnly', 'resolveDirectory', 'get', 'exists', 'isFile', 'set', 'rename', 'remove', 'isDirectory', 'list', 'createDir'],
methods: [
'folderIsReadOnly',
'resolveDirectory',
'get',
'exists',
'isFile',
'set',
'rename',
'remove',
'isDirectory',
'list',
'createDir'
],
events: [],
description: 'Using Remixd daemon, allow to access file system',
kind: 'other',
version: packageJson.version,
repo: "https://github.com/ethereum/remix-project/tree/master/libs/remixd",
maintainedBy: "Remix",
documentation: "https://remix-ide.readthedocs.io/en/latest/remixd.html",
authorContact: ""
repo: 'https://github.com/ethereum/remix-project/tree/master/libs/remixd',
maintainedBy: 'Remix',
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixd.html',
authorContact: ''
}
export class RemixdHandle extends WebsocketPlugin {
@ -62,7 +74,6 @@ export class RemixdHandle extends WebsocketPlugin {
}
await this.appManager.deactivatePlugin('remixd')
}
async callPluginMethod(key: string, payload?: any[]) {
@ -85,25 +96,33 @@ export class RemixdHandle extends WebsocketPlugin {
console.log(error)
const alert: AlertModal = {
id: 'connectionAlert',
message: 'Cannot connect to the remixd daemon. Please make sure you have the remixd running in the background.'
message:
'Cannot connect to the remixd daemon. Please make sure you have the remixd running in the background.'
}
this.call('notification', 'alert', alert)
this.canceled()
} else {
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)
const alert: AlertModal = {
id: 'connectionAlert',
message: 'Connection to remixd terminated. Please make sure remixd is still running in the background.'
message:
'Connection to remixd terminated. Please make sure remixd is still running in the background.'
}
this.call('notification', 'alert', alert)
this.canceled()
}
}, 3000)
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) {
await this.appManager.activatePlugin(plugin)
}
@ -118,7 +137,7 @@ export class RemixdHandle extends WebsocketPlugin {
title: 'Access file system using remixd',
message: remixdDialog(),
okLabel: 'Connect',
cancelLabel: 'Cancel',
cancelLabel: 'Cancel'
}
const result = await this.call('notification', 'modal', mod)
if (result) {
@ -126,7 +145,8 @@ export class RemixdHandle extends WebsocketPlugin {
this.localhostProvider.preInit()
super.activate()
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.'))
} else {
connection()
@ -135,14 +155,15 @@ export class RemixdHandle extends WebsocketPlugin {
} catch (error) {
connection(error)
}
}
else {
} else {
await this.canceled()
}
} else {
try {
super.activate()
setTimeout(() => { connection() }, 2000)
setTimeout(() => {
connection()
}, 2000)
} catch (error) {
connection(error)
}
@ -152,38 +173,73 @@ export class RemixdHandle extends WebsocketPlugin {
function remixdDialog() {
const commandText = 'remixd'
const fullCommandText = 'remixd -s <path-to-the-shared-folder> -u <remix-ide-instance-URL>'
return (<>
<div className=''>
<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>.
const fullCommandText =
'remixd -s <path-to-the-shared-folder> -u <remix-ide-instance-URL>'
return (
<>
<div className="">
<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 className='mb-2 text-break'>
Remixd <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">documentation</a>.
<div className="mb-2 text-break">
Remixd{' '}
<a
target="_blank"
href="https://remix-ide.readthedocs.io/en/latest/remixd.html"
>
documentation
</a>
.
</div>
<div className='mb-2 text-break'>
<div className="mb-2 text-break">
The remixd command is:
<br /><b>{commandText}</b>
<br />
<b>{commandText}</b>
</div>
<div className='mb-2 text-break'>
The remixd command without options uses the terminal's current directory as the shared directory and the shared Remix domain can only be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org
<div className="mb-2 text-break">
The remixd command without options uses the terminal's current
directory as the shared directory and the shared Remix domain can only
be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or
https://remix-beta.ethereum.org
</div>
<div className='mb-2 text-break'>
<div className="mb-2 text-break">
Example command with flags: <br />
<b>{fullCommandText}</b>
</div>
<div className='mb-2 text-break'>
For info about ports, see <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a>
<div className="mb-2 text-break">
For info about ports, see{' '}
<a
target="_blank"
href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage"
>
Remixd ports usage
</a>
</div>
<div className='mb-2 text-break'>
This feature is still in Alpha. We recommend to keep a backup of the shared folder.
<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'>
<div className="mb-2 text-break">
<h6 className="text-danger">
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>
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>
</h6>
</div>
</div>
</>)
</>
)
}

@ -3,7 +3,7 @@ import { format } from 'util'
import {Plugin} from '@remixproject/engine'
import {compile} from '@remix-project/remix-solidity'
import {TransactionConfig} from 'web3-core'
const _paq = window._paq = window._paq || [] //eslint-disable-line
const _paq = (window._paq = window._paq || []) //eslint-disable-line
const profile = {
name: 'solidity-script',
@ -19,11 +19,14 @@ export class SolidityScript extends Plugin {
async execute(path: string, functionName: string = 'run') {
_paq.push(['trackEvent', 'SolidityScript', 'execute', 'script'])
this.call('terminal', 'log', `Running free function '${functionName}' from ${path}...`)
this.call(
'terminal',
'log',
`Running free function '${functionName}' from ${path}...`
)
let content = await this.call('fileManager', 'readFile', path)
const params = await this.call('solidity', 'getCompilerParameters')
content = `
// SPDX-License-Identifier: GPL-3.0
@ -42,7 +45,9 @@ export class SolidityScript extends Plugin {
// compile
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) {
@ -83,15 +88,21 @@ export class SolidityScript extends Plugin {
const hhlogs = await web3.eth.getHHLogsForTx(receiptCall.transactionHash)
if (hhlogs && hhlogs.length) {
const finalLogs = <div><div><b>console.log:</b></div>
{
hhlogs.map((log) => {
const finalLogs = (
<div>
<div>
<b>console.log:</b>
</div>
{hhlogs.map((log) => {
let formattedLog
// 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
// For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6'
// We check first arg to determine if 'util.format' is needed
if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) {
if (
typeof log[0] === 'string' &&
(log[0].includes('%s') || log[0].includes('%d'))
) {
formattedLog = format(log[0], ...log.slice(1))
} else {
formattedLog = log.join(' ')
@ -99,6 +110,7 @@ export class SolidityScript extends Plugin {
return <div>{formattedLog}</div>
})}
</div>
)
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'logHtml', finalLogs)
}

@ -3,7 +3,11 @@ import { ViewPlugin } from '@remixproject/engine-web'
import React from 'react'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import {RemixUiSolidityUmlGen} from '@remix-ui/solidity-uml-gen'
import { ISolidityUmlGen, ThemeQualityType, ThemeSummary } from 'libs/remix-ui/solidity-uml-gen/src/types'
import {
ISolidityUmlGen,
ThemeQualityType,
ThemeSummary
} from 'libs/remix-ui/solidity-uml-gen/src/types'
import {RemixAppManager} from 'libs/remix-ui/plugin-manager/src/types'
import {normalizeContractPath} from 'libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities'
import {convertAST2UmlClasses} from 'sol2uml/lib/converterAST2Classes'
@ -13,15 +17,16 @@ import { customAction } from '@remixproject/plugin-api'
import {ClassOptions} from 'sol2uml/lib/converterClass2Dot'
const parser = (window as any).SolidityParser
const _paq = window._paq = window._paq || []
const _paq = (window._paq = window._paq || [])
const profile = {
name: 'solidityumlgen',
displayName: 'Solidity UML Generator',
description: 'Generates UML diagram in svg format from last compiled contract',
description:
'Generates UML diagram in svg format from last compiled contract',
location: 'mainPanel',
methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'],
events: [],
events: []
}
/**
@ -54,7 +59,6 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
this.currentlySelectedTheme = ''
this.themeName = ''
this.activeTheme = {} as ThemeSummary
this.appManager = appManager
this.element = document.createElement('div')
@ -63,24 +67,39 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
onActivation(): void {
this.handleThemeChange()
this.on('solidity', 'compilationFinished', async (file: string, source, languageVersion, data, input, version) => {
this.on(
'solidity',
'compilationFinished',
async (file: string, source, languageVersion, data, input, version) => {
if (!this.triggerGenerateUml) return
this.triggerGenerateUml = false
const currentTheme: ThemeQualityType = await this.call('theme', 'currentTheme')
const currentTheme: ThemeQualityType = await this.call(
'theme',
'currentTheme'
)
this.currentlySelectedTheme = currentTheme.quality
this.themeName = currentTheme.name
let result = ''
const normalized = normalizeContractPath(file)
this.currentFile = normalized[normalized.length - 1]
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)
}
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)
let umlDot = ''
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)
this.updatedSvg = payload
_paq.push(['trackEvent', 'solidityumlgen', 'umlgenerated'])
@ -89,11 +108,13 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
} catch (error) {
console.log('error', error)
}
})
}
)
}
getThemeCssVariables(cssVars: string) {
return window.getComputedStyle(document.documentElement)
return window
.getComputedStyle(document.documentElement)
.getPropertyValue(cssVars)
}
@ -102,7 +123,12 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
this.currentlySelectedTheme = theme.quality
this.activeTheme = theme
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.renderComponent()
await this.call('tabs', 'focus', 'solidityumlgen')
@ -134,12 +160,16 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
* @returns {Promise<string>}
*/
async flattenContract(source: any, filePath: string, data: any) {
const result = await this.call('contractflattener', 'flattenContract', source, filePath, data)
const result = await this.call(
'contractflattener',
'flattenContract',
source,
filePath,
data
)
return result
}
async showUmlDiagram(svgPayload: string) {
this.updatedSvg = svgPayload
this.renderComponent()
@ -156,9 +186,11 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
}
render() {
return <div id='sol-uml-gen'>
return (
<div id="sol-uml-gen">
<PluginViewWrapper plugin={this} />
</div>
)
}
renderComponent() {
@ -171,12 +203,13 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
themeDark: this.themeDark,
fileName: this.currentFile,
themeCollection: this.themeCollection,
activeTheme: this.activeTheme,
activeTheme: this.activeTheme
})
}
updateComponent(state: any) {
return <RemixUiSolidityUmlGen
return (
<RemixUiSolidityUmlGen
updatedSvg={state.updatedSvg}
loading={state.loading}
themeSelected={state.currentlySelectedTheme}
@ -185,10 +218,10 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
themeCollection={state.themeCollection}
themeDark={state.themeDark}
/>
)
}
}
interface Sol2umlClassOptions extends ClassOptions {
backColor?: string
shapeColor?: string
@ -202,7 +235,7 @@ import {
Association,
ClassStereotype,
ReferenceType,
UmlClass,
UmlClass
} from 'sol2uml/lib/umlClass'
import {findAssociatedClass} from 'sol2uml/lib/associations'

@ -4,17 +4,17 @@ import { Blockchain } from '../../blockchain/blockchain'
import {ethers} from 'ethers'
export type JsonDataRequest = {
id: number,
id: number
jsonrpc: string // version
method: string,
params: Array<any>,
method: string
params: Array<any>
}
export type JsonDataResult = {
id: number,
id: number
jsonrpc: string // version
result?: any,
error?: any,
result?: any
error?: any
}
export type RejectRequest = (error: Error) => void
@ -61,7 +61,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
okLabel: 'OK',
cancelLabel: 'Cancel',
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://')) {
return {
valid: true,
@ -70,7 +70,8 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
} else {
return {
valid: false,
message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
message:
'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
}
}
},
@ -110,7 +111,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
const modalContent: AlertModal = {
id: this.profile.name,
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)
}
@ -118,13 +119,22 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
return
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
private async sendAsyncInternal(
data: JsonDataRequest,
resolve: SuccessRequest,
reject: RejectRequest
): Promise<void> {
if (this.provider) {
try {
const result = await this.provider.send(data.method, data.params)
resolve({jsonrpc: '2.0', result, id: data.id})
} catch (error) {
if (error && error.message && error.message.includes('net_version') && error.message.includes('SERVER_ERROR')) {
if (
error &&
error.message &&
error.message.includes('net_version') &&
error.message.includes('SERVER_ERROR')
) {
this.switchAway(true)
}
reject(error)

@ -10,14 +10,17 @@ export class CustomForkVMProvider extends BasicVMProvider {
inputs: any
constructor(blockchain) {
super({
super(
{
name: 'vm-custom-fork',
displayName: 'Custom fork - Remix VM',
kind: 'provider',
description: 'Custom fork - Remix VM',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
},
blockchain
)
this.blockchain = blockchain
this.fork = ''
this.nodeUrl = ''
@ -27,25 +30,51 @@ export class CustomForkVMProvider extends BasicVMProvider {
async init() {
const body = () => {
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>
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>
<div>
<label className="mt-3 mb-1">Node URL</label>
<input data-id="CustomForkNodeUrl" name="nodeUrl" type="text" className="border form-control border-right-0" />
<input
data-id="CustomForkNodeUrl"
name="nodeUrl"
type="text"
className="border form-control border-right-0"
/>
</div>
<div>
<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>
<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) => {
return <option value={Hardfork[value]} key={index}>{value}</option>
return (
<option value={Hardfork[value]} key={index}>
{value}
</option>
)
})}
</select>
</div>
</div>
)
}
const result = await ((): Promise<any> => {
return new Promise((resolve, reject) => {
@ -54,7 +83,7 @@ export class CustomForkVMProvider extends BasicVMProvider {
title: this.profile.displayName,
message: body(),
validationFn: (data: any) => {
if(data.nodeUrl !== '' && !data.nodeUrl.startsWith("http")) {
if (data.nodeUrl !== '' && !data.nodeUrl.startsWith('http')) {
return {
valid: false,
message: 'node URL should be a valid URL'
@ -98,9 +127,9 @@ export class CustomForkVMProvider extends BasicVMProvider {
}
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
fork: this.fork,
nodeUrl: this.nodeUrl,
blockNumber: this.blockNumber
}
}
}

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

@ -18,11 +18,20 @@ export class FoundryProvider extends AbstractProvider {
body(): JSX.Element {
return (
<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>
{' '}
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">
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>Anvil JSON-RPC Endpoint:</div>
</div>

@ -18,11 +18,20 @@ export class GanacheProvider extends AbstractProvider {
body(): JSX.Element {
return (
<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>
{' '}
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">
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>Ganache JSON-RPC Endpoint:</div>
</div>

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

@ -18,10 +18,21 @@ export class HardhatProvider extends AbstractProvider {
body(): JSX.Element {
return (
<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>
{' '}
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">
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>Hardhat JSON-RPC Endpoint:</div>
</div>

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

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

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

@ -9,8 +9,17 @@ export class InjectedProviderDefaultBase extends InjectedProvider {
async init() {
const injectedProvider = this.getInjectedProvider()
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 (
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).'
)
}
return super.init()
}

@ -1,7 +1,11 @@
/* global ethereum */
import React from 'react' // eslint-disable-line
import {Plugin} from '@remixproject/engine'
import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider'
import {
JsonDataRequest,
RejectRequest,
SuccessRequest
} from '../providers/abstract-provider'
import {IProvider} from './abstract-provider'
export abstract class InjectedProvider extends Plugin implements IProvider {
@ -25,8 +29,8 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
onActivation(): void {
try {
const web3Provider = this.getInjectedProvider()
web3Provider.on('accountsChanged', this.listenerAccountsChanged);
web3Provider.on('chainChanged', this.listenerChainChanged);
web3Provider.on('accountsChanged', this.listenerAccountsChanged)
web3Provider.on('chainChanged', this.listenerChainChanged)
} catch (error) {
console.log('unable to listen on context changed')
}
@ -35,7 +39,10 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
onDeactivation(): void {
try {
const web3Provider = this.getInjectedProvider()
web3Provider.removeListener('accountsChanged', this.listenerAccountsChanged)
web3Provider.removeListener(
'accountsChanged',
this.listenerAccountsChanged
)
web3Provider.removeListener('chainChanged', this.listenerChainChanged)
} catch (error) {
console.log('unable to remove listener on context changed')
@ -44,17 +51,18 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
askPermission(throwIfNoInjectedProvider) {
const web3Provider = this.getInjectedProvider()
if (typeof web3Provider !== "undefined" && typeof web3Provider.request === "function") {
web3Provider.request({ method: "eth_requestAccounts" })
if (
typeof web3Provider !== 'undefined' &&
typeof web3Provider.request === 'function'
) {
web3Provider.request({method: 'eth_requestAccounts'})
} else if (throwIfNoInjectedProvider) {
throw new Error(this.notFound())
}
}
body(): JSX.Element {
return (
<div></div>
)
return <div></div>
}
async init() {
@ -74,18 +82,35 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
})
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
private async sendAsyncInternal(
data: JsonDataRequest,
resolve: SuccessRequest,
reject: RejectRequest
): Promise<void> {
// Check the case where current environment is VM on UI and it still sends RPC requests
// This will be displayed on UI tooltip as 'cannot get account list: Environment Updated !!'
const web3Provider = this.getInjectedProvider()
if (!web3Provider) {
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 })
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
})
}
try {
let resultData
if (web3Provider.send) resultData = await web3Provider.send(data.method, data.params)
else if (web3Provider.request) resultData = await web3Provider.request({ method: data.method, params: data.params})
if (web3Provider.send)
resultData = await web3Provider.send(data.method, data.params)
else if (web3Provider.request)
resultData = await web3Provider.request({
method: data.method,
params: data.params
})
else {
resolve({jsonrpc: '2.0', error: 'provider not valid', id: data.id})
return
@ -99,7 +124,12 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
resolve({jsonrpc: '2.0', error: 'no return data provided', id: data.id})
}
} 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,25 +5,29 @@ export class MainnetForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
constructor(blockchain) {
super({
super(
{
name: 'vm-mainnet-fork',
displayName: 'Mainet fork -Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
},
blockchain
)
this.blockchain = blockchain
this.fork = 'shanghai'
this.nodeUrl = 'https://mainnet.infura.io/v3/08b2a484451e4635a28b3d8234f24332'
this.nodeUrl =
'https://mainnet.infura.io/v3/08b2a484451e4635a28b3d8234f24332'
this.blockNumber = 'latest'
}
async init() {
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
fork: this.fork,
nodeUrl: this.nodeUrl,
blockNumber: this.blockNumber
}
}
}

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

@ -1,6 +1,10 @@
import React from 'react' // eslint-disable-line
import * as packageJson from '../../../../../package.json'
import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider'
import {
JsonDataRequest,
RejectRequest,
SuccessRequest
} from '../providers/abstract-provider'
import {Plugin} from '@remixproject/engine'
import {IProvider} from './abstract-provider'
@ -14,12 +18,12 @@ export class BasicVMProvider extends Plugin implements IProvider {
this.fork = ''
}
async init (): Promise<{ [id: string] : any }> { return {} }
async init(): Promise<{[id: string]: any}> {
return {}
}
body(): JSX.Element {
return (
<div></div>
)
return <div></div>
}
sendAsync(data: JsonDataRequest): Promise<any> {
@ -28,14 +32,21 @@ export class BasicVMProvider extends Plugin implements IProvider {
})
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
private async sendAsyncInternal(
data: JsonDataRequest,
resolve: SuccessRequest,
reject: RejectRequest
): Promise<void> {
try {
await this.blockchain.providers.vm.provider.sendAsync(data, (error, result) => {
await this.blockchain.providers.vm.provider.sendAsync(
data,
(error, result) => {
if (error) return reject(error)
else {
resolve({jsonrpc: '2.0', result, id: data.id})
}
})
}
)
} catch (error) {
reject(error)
}
@ -44,14 +55,17 @@ export class BasicVMProvider extends Plugin implements IProvider {
export class MergeVMProvider extends BasicVMProvider {
constructor(blockchain) {
super({
super(
{
name: 'vm-merge',
displayName: 'Remix VM (Merge)',
kind: 'provider',
description: 'Remix VM (Merge)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
},
blockchain
)
this.blockchain = blockchain
this.fork = 'merge'
}
@ -59,14 +73,17 @@ export class MergeVMProvider extends BasicVMProvider {
export class LondonVMProvider extends BasicVMProvider {
constructor(blockchain) {
super({
super(
{
name: 'vm-london',
displayName: 'Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
},
blockchain
)
this.blockchain = blockchain
this.fork = 'london'
}
@ -74,14 +91,17 @@ export class LondonVMProvider extends BasicVMProvider {
export class BerlinVMProvider extends BasicVMProvider {
constructor(blockchain) {
super({
super(
{
name: 'vm-berlin',
displayName: 'Remix VM (Berlin)',
kind: 'provider',
description: 'Remix VM (Berlin)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
},
blockchain
)
this.blockchain = blockchain
this.fork = 'berlin'
}
@ -89,14 +109,17 @@ export class BerlinVMProvider extends BasicVMProvider {
export class ShanghaiVMProvider extends BasicVMProvider {
constructor(blockchain) {
super({
super(
{
name: 'vm-shanghai',
displayName: 'Remix VM (Shanghai)',
kind: 'provider',
description: 'Remix VM (Shanghai)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
},
blockchain
)
this.blockchain = blockchain
this.fork = 'shanghai'
}

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

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

@ -1,4 +1,3 @@
import React from 'react' // eslint-disable-line
import Web3 from 'web3'
import {Plugin} from '@remixproject/engine'
@ -12,36 +11,58 @@ import { InjectedProvider } from './providers/injected'
import {NodeProvider} from './providers/node'
import {execution, EventManager, helpers} from '@remix-project/remix-lib'
import {etherScanLink} from './helper'
import { logBuilder, cancelUpgradeMsg, cancelProxyMsg, addressToString } from "@remix-ui/helper"
const { txFormat, txExecution, typeConversion, txListener: Txlistener, TxRunner, TxRunnerWeb3, txHelper } = execution
import {
logBuilder,
cancelUpgradeMsg,
cancelProxyMsg,
addressToString
} from '@remix-ui/helper'
const {
txFormat,
txExecution,
typeConversion,
txListener: Txlistener,
TxRunner,
TxRunnerWeb3,
txHelper
} = execution
const {txResultHelper} = helpers
const {resultToRemixTx} = txResultHelper
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 = {
name: 'blockchain',
displayName: 'Blockchain',
description: 'Blockchain - Logic',
methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'getProvider'],
methods: [
'getCode',
'getTransactionReceipt',
'addProvider',
'removeProvider',
'getCurrentFork',
'getAccounts',
'web3VM',
'getProvider'
],
version: packageJson.version
}
export type TransactionContextAPI = {
getAddress: (cb: (error: Error, result: string) => void) => void,
getValue: (cb: (error: Error, result: string) => void) => void,
getAddress: (cb: (error: Error, result: string) => void) => void
getValue: (cb: (error: Error, result: string) => void) => void
getGasLimit: (cb: (error: Error, result: string) => void) => void
}
// see TxRunner.ts in remix-lib
export type Transaction = {
from: string,
to: string,
value: string,
data: string,
gasLimit: number,
useCall: boolean,
from: string
to: string
value: string
data: string
gasLimit: number
useCall: boolean
timestamp?: number
}
@ -55,7 +76,7 @@ export class Blockchain extends Plugin {
networkcallid: number
networkStatus: {
network: {
name: string,
name: string
id: string
}
error?: string
@ -72,16 +93,24 @@ export class Blockchain extends Plugin {
this.events = new EventEmitter()
this.config = config
const web3Runner = new TxRunnerWeb3({
const web3Runner = new TxRunnerWeb3(
{
config: this.config,
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
},
isVM: () => { return this.executionContext.isVM() },
isVM: () => {
return this.executionContext.isVM()
},
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.networkcallid = 0
@ -189,33 +218,86 @@ 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 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) {
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...`)
this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb)
}, statusCb, (data, runTxCallback) => {
this.createContract(
selectedContract,
data,
continueCb,
promptCb,
confirmationCb,
finalCb
)
},
statusCb,
(data, runTxCallback) => {
// called for libraries deployment
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 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) {
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...`)
this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb)
})
this.createContract(
selectedContract,
data,
continueCb,
promptCb,
confirmationCb,
finalCb
)
}
)
}
async deployProxy(proxyData, implementationContractObject) {
@ -229,11 +311,21 @@ export class Blockchain extends Plugin {
cancelLabel: 'Cancel',
okFn: () => {
this.runProxyTx(proxyData, implementationContractObject)
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'modal ok confirmation'])
_paq.push([
'trackEvent',
'blockchain',
'Deploy With Proxy',
'modal ok confirmation'
])
},
cancelFn: () => {
this.call('notification', 'toast', cancelProxyMsg())
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'cancel proxy deployment'])
_paq.push([
'trackEvent',
'blockchain',
'Deploy With Proxy',
'cancel proxy deployment'
])
},
hideFn: () => null
}
@ -243,30 +335,70 @@ export class Blockchain extends Plugin {
async runProxyTx(proxyData, implementationContractObject) {
const args = {useCall: false, data: proxyData}
let networkInfo
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
const confirmationCb = (
network,
tx,
gasEstimation,
continueTxExecution,
cancelCb
) => {
networkInfo = network
// continue using original authorization given by user
continueTxExecution(null)
}
const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() }
const promptCb = (okCb, cancelCb) => { okCb() }
const continueCb = (error, continueTxExecution, cancelCb) => {
continueTxExecution()
}
const promptCb = (okCb, cancelCb) => {
okCb()
}
const finalCb = async (error, txResult, address, returnValue) => {
if (error) {
const log = logBuilder(error)
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment failed: ' + error])
_paq.push([
'trackEvent',
'blockchain',
'Deploy With Proxy',
'Proxy deployment failed: ' + error
])
return this.call('terminal', 'logHtml', log)
}
await this.saveDeployedContractStorageLayout(implementationContractObject, address, networkInfo)
this.events.emit('newProxyDeployment', address, new Date().toISOString(), implementationContractObject.contractName)
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful'])
this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name)
await this.saveDeployedContractStorageLayout(
implementationContractObject,
address,
networkInfo
)
this.events.emit(
'newProxyDeployment',
address,
new Date().toISOString(),
implementationContractObject.contractName
)
_paq.push([
'trackEvent',
'blockchain',
'Deploy With Proxy',
'Proxy deployment successful'
])
this.call(
'udapp',
'addInstance',
addressToString(address),
implementationContractObject.abi,
implementationContractObject.name
)
}
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
}
async upgradeProxy(proxyAddress, newImplAddress, data, newImplementationContractObject) {
async upgradeProxy(
proxyAddress,
newImplAddress,
data,
newImplementationContractObject
) {
const upgradeModal = {
id: 'confirmProxyDeployment',
title: 'Confirm Update Proxy (ERC1967)',
@ -276,11 +408,21 @@ export class Blockchain extends Plugin {
cancelLabel: 'Cancel',
okFn: () => {
this.runUpgradeTx(proxyAddress, data, newImplementationContractObject)
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'proxy upgrade confirmation click'])
_paq.push([
'trackEvent',
'blockchain',
'Upgrade With Proxy',
'proxy upgrade confirmation click'
])
},
cancelFn: () => {
this.call('notification', 'toast', cancelUpgradeMsg())
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'proxy upgrade cancel click'])
_paq.push([
'trackEvent',
'blockchain',
'Upgrade With Proxy',
'proxy upgrade cancel click'
])
},
hideFn: () => null
}
@ -290,42 +432,96 @@ export class Blockchain extends Plugin {
async runUpgradeTx(proxyAddress, data, newImplementationContractObject) {
const args = {useCall: false, data, to: proxyAddress}
let networkInfo
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
const confirmationCb = (
network,
tx,
gasEstimation,
continueTxExecution,
cancelCb
) => {
// continue using original authorization given by user
networkInfo = network
continueTxExecution(null)
}
const continueCb = (error, continueTxExecution, cancelCb) => { continueTxExecution() }
const promptCb = (okCb, cancelCb) => { okCb() }
const continueCb = (error, continueTxExecution, cancelCb) => {
continueTxExecution()
}
const promptCb = (okCb, cancelCb) => {
okCb()
}
const finalCb = async (error, txResult, address, returnValue) => {
if (error) {
const log = logBuilder(error)
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade failed'])
_paq.push([
'trackEvent',
'blockchain',
'Upgrade With Proxy',
'Upgrade failed'
])
return this.call('terminal', 'logHtml', log)
}
await this.saveDeployedContractStorageLayout(newImplementationContractObject, proxyAddress, networkInfo)
_paq.push(['trackEvent', 'blockchain', 'Upgrade With Proxy', 'Upgrade Successful'])
this.call('udapp', 'addInstance', addressToString(proxyAddress), newImplementationContractObject.abi, newImplementationContractObject.name)
await this.saveDeployedContractStorageLayout(
newImplementationContractObject,
proxyAddress,
networkInfo
)
_paq.push([
'trackEvent',
'blockchain',
'Upgrade With Proxy',
'Upgrade Successful'
])
this.call(
'udapp',
'addInstance',
addressToString(proxyAddress),
newImplementationContractObject.abi,
newImplementationContractObject.name
)
}
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
}
async saveDeployedContractStorageLayout (contractObject, proxyAddress, networkInfo) {
async saveDeployedContractStorageLayout(
contractObject,
proxyAddress,
networkInfo
) {
const {contractName, implementationAddress} = contractObject
const networkName = networkInfo.name === 'custom' ? networkInfo.name + '-' + networkInfo.id : networkInfo.name
const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`)
const networkName =
networkInfo.name === 'custom'
? networkInfo.name + '-' + networkInfo.id
: networkInfo.name
const hasPreviousDeploys = await this.call(
'fileManager',
'exists',
`.deploys/upgradeable-contracts/${networkName}/UUPS.json`
)
// TODO: make deploys folder read only.
if (hasPreviousDeploys) {
const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`)
const deployments = await this.call(
'fileManager',
'readFile',
`.deploys/upgradeable-contracts/${networkName}/UUPS.json`
)
const parsedDeployments = JSON.parse(deployments)
const proxyDeployment = parsedDeployments.deployments[proxyAddress]
if (proxyDeployment) {
const oldImplementationAddress = proxyDeployment.implementationAddress
const hasPreviousBuild = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`)
const hasPreviousBuild = await this.call(
'fileManager',
'exists',
`.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`
)
if (hasPreviousBuild) await this.call('fileManager', 'remove', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`)
if (hasPreviousBuild)
await this.call(
'fileManager',
'remove',
`.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`
)
}
parsedDeployments.deployments[proxyAddress] = {
date: new Date().toISOString(),
@ -335,17 +531,45 @@ export class Blockchain extends Plugin {
solcOutput: contractObject.compiler.data,
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,
solcOutput: contractObject.compiler.data
}, null, 2))
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2))
},
null,
2
)
)
await this.call(
'fileManager',
'writeFile',
`.deploys/upgradeable-contracts/${networkName}/UUPS.json`,
JSON.stringify(parsedDeployments, null, 2)
)
} 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,
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,
network: networkInfo.name,
deployments: {
@ -356,7 +580,11 @@ export class Blockchain extends Plugin {
implementationAddress: implementationAddress
}
}
}, null, 2))
},
null,
2
)
)
}
}
@ -378,20 +606,41 @@ export class Blockchain extends Plugin {
})
}
createContract (selectedContract, data, continueCb, promptCb, confirmationCb, finalCb) {
createContract(
selectedContract,
data,
continueCb,
promptCb,
confirmationCb,
finalCb
) {
if (data) {
data.contractName = selectedContract.name
data.linkReferences = selectedContract.bytecodeLinkReferences
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) => {
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
}`
)
}
if (txResult.receipt.status === false || txResult.receipt.status === '0x0' || txResult.receipt.status === 0) {
return finalCb(`creation of ${selectedContract.name} errored: transaction execution failed`)
if (
txResult.receipt.status === false ||
txResult.receipt.status === '0x0' ||
txResult.receipt.status === 0
) {
return finalCb(
`creation of ${selectedContract.name} errored: transaction execution failed`
)
}
finalCb(null, selectedContract, address)
}
@ -400,9 +649,14 @@ export class Blockchain extends Plugin {
determineGasPrice(cb) {
this.getCurrentProvider().getGasPrice((error, gasPrice) => {
const warnMessage = ' Please fix this issue before sending any transaction. '
const warnMessage =
' Please fix this issue before sending any transaction. '
if (error) {
return cb('Unable to retrieve the current network gas price.' + warnMessage + error)
return cb(
'Unable to retrieve the current network gas price.' +
warnMessage +
error
)
}
try {
const gasPriceValue = this.fromWei(gasPrice, false, 'gwei')
@ -432,7 +686,13 @@ export class Blockchain extends Plugin {
}
calculateFee(gas, gasPrice, unit?) {
return Web3.utils.toBN(gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10) as string, unit || 'gwei')))
return Web3.utils
.toBN(gas)
.mul(
Web3.utils.toBN(
Web3.utils.toWei(gasPrice.toString(10) as string, unit || 'gwei')
)
)
}
determineGasFees(tx) {
@ -445,7 +705,8 @@ export class Blockchain extends Plugin {
txFeeText = ' ' + this.fromWei(fee, false, 'ether') + ' Ether'
priceStatus = true
} catch (e) {
txFeeText = ' Please fix this issue before sending any transaction. ' + e.message
txFeeText =
' Please fix this issue before sending any transaction. ' + e.message
priceStatus = false
}
cb(txFeeText, priceStatus)
@ -455,7 +716,13 @@ export class Blockchain extends Plugin {
}
changeExecutionContext(context, confirmCb, infoCb, cb) {
return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb)
return this.executionContext.executionContextChange(
context,
null,
confirmCb,
infoCb,
cb
)
}
detectNetwork(cb) {
@ -481,7 +748,7 @@ export class Blockchain extends Plugin {
isWeb3Provider() {
const isVM = this.executionContext.isVM()
const isInjected = this.getProvider() === 'injected'
return (!isVM && !isInjected)
return !isVM && !isInjected
}
isInjectedWeb3() {
@ -514,11 +781,35 @@ export class Blockchain extends Plugin {
return txlistener
}
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
txFormat.buildData(contractName, contractAbi, {}, false, funABI, callType, (error, data) => {
txFormat.buildData(
contractName,
contractAbi,
{},
false,
funABI,
callType,
(error, data) => {
if (error) {
return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`)
return logCallback(
`${logMsg} errored: ${error.message ? error.message : error}`
)
}
if (!lookupOnly) {
logCallback(`${logMsg} pending ... `)
@ -532,31 +823,46 @@ export class Blockchain extends Plugin {
data.contractABI = contractAbi
data.contract = contract
}
const useCall = funABI.stateMutability === 'view' || funABI.stateMutability === 'pure'
this.runTx({ to: address, data, useCall }, confirmationCb, continueCb, promptCb, (error, txResult, _address, returnValue) => {
const useCall =
funABI.stateMutability === 'view' || funABI.stateMutability === 'pure'
this.runTx(
{to: address, data, useCall},
confirmationCb,
continueCb,
promptCb,
(error, txResult, _address, returnValue) => {
if (error) {
return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`)
return logCallback(
`${logMsg} errored: ${error.message ? error.message : error}`
)
}
if (lookupOnly) {
outputCb(returnValue)
}
})
}
)
},
(msg) => {
logCallback(msg)
},
(data, runTxCallback) => {
// called for libraries deployment
this.runTx(data, confirmationCb, runTxCallback, promptCb, () => { /* Do nothing. */ })
this.runTx(data, confirmationCb, runTxCallback, promptCb, () => {
/* Do nothing. */
})
}
)
}
context() {
return (this.executionContext.isVM() ? 'memory' : 'blockchain')
return this.executionContext.isVM() ? 'memory' : 'blockchain'
}
// NOTE: the config is only needed because exectuionContext.init does
async resetAndInit (config: Config, transactionContextAPI: TransactionContextAPI) {
async resetAndInit(
config: Config,
transactionContextAPI: TransactionContextAPI
) {
this.transactionContextAPI = transactionContextAPI
this.executionContext.init(config)
this.executionContext.stopListenOnLastBlock()
@ -582,16 +888,24 @@ export class Blockchain extends Plugin {
async resetEnvironment() {
await this.getCurrentProvider().resetEnvironment()
// TODO: most params here can be refactored away in txRunner
const web3Runner = new TxRunnerWeb3({
const web3Runner = new TxRunnerWeb3(
{
config: this.config,
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
},
isVM: () => { return this.executionContext.isVM() },
isVM: () => {
return this.executionContext.isVM()
},
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()
)
web3Runner.event.register('transactionBroadcasted', (txhash) => {
this.executionContext.detectNetwork((error, network) => {
@ -600,10 +914,13 @@ export class Blockchain extends Plugin {
const viewEtherScanLink = etherScanLink(network.name, txhash)
if (viewEtherScanLink) {
this.call('terminal', 'logHtml',
(<a href={etherScanLink(network.name, txhash)} target="_blank">
this.call(
'terminal',
'logHtml',
<a href={etherScanLink(network.name, txhash)} target="_blank">
view on etherscan
</a>))
</a>
)
}
})
})
@ -616,7 +933,9 @@ export class Blockchain extends Plugin {
*/
createVMAccount(newAccount) {
if (!this.executionContext.isVM()) {
throw new Error('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed')
throw new Error(
'plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed'
)
}
return (this.providers.vm as VMProvider).createVMAccount(newAccount)
}
@ -653,22 +972,36 @@ export class Blockchain extends Plugin {
this.executionContext.detectNetwork((error, network) => {
if (error) return reject(error)
if (network.name === 'Main' && network.id === '1') {
return reject(new Error('It is not allowed to make this action against mainnet'))
return reject(
new Error('It is not allowed to make this action against mainnet')
)
}
this.txRunner.rawRun(
tx,
(network, tx, gasEstimation, continueTxExecution, cancelCb) => { continueTxExecution() },
(error, continueTxExecution, cancelCb) => { if (error) { reject(error) } else { continueTxExecution() } },
(okCb, cancelCb) => { okCb() },
(network, tx, gasEstimation, continueTxExecution, cancelCb) => {
continueTxExecution()
},
(error, continueTxExecution, cancelCb) => {
if (error) {
reject(error)
} else {
continueTxExecution()
}
},
(okCb, cancelCb) => {
okCb()
},
async (error, result) => {
if (error) return reject(error)
try {
if (this.executionContext.isVM()) {
const execResult = await this.web3().eth.getExecutionResultFromSimulator(result.transactionHash)
const execResult =
await this.web3().eth.getExecutionResultFromSimulator(
result.transactionHash
)
resolve(resultToRemixTx(result, execResult))
} else
resolve(resultToRemixTx(result))
} else resolve(resultToRemixTx(result))
} catch (e) {
reject(e)
}
@ -712,7 +1045,10 @@ export class Blockchain extends Plugin {
if (this.transactionContextAPI.getAddress) {
return this.transactionContextAPI.getAddress(function (err, address) {
if (err) return reject(err)
if (!address) return reject('"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.')
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)
})
}
@ -721,7 +1057,10 @@ export class Blockchain extends Plugin {
const address = accounts[0]
if (!address) return reject('No accounts available')
if (this.executionContext.isVM() && !this.providers.vm.RemixSimulatorProvider.Accounts.accounts[address]) {
if (
this.executionContext.isVM() &&
!this.providers.vm.RemixSimulatorProvider.Accounts.accounts[address]
) {
return reject('Invalid account selected')
}
return resolve(address)
@ -743,21 +1082,44 @@ export class Blockchain extends Plugin {
return
}
const tx = { to: args.to, data: args.data.dataHex, useCall: args.useCall, from: fromAddress, value: value, gasLimit: gasLimit, timestamp: args.data.timestamp }
const payLoad = { funAbi: args.data.funAbi, funArgs: args.data.funArgs, contractBytecode: args.data.contractBytecode, contractName: args.data.contractName, contractABI: args.data.contractABI, linkReferences: args.data.linkReferences }
const tx = {
to: args.to,
data: args.data.dataHex,
useCall: args.useCall,
from: fromAddress,
value: value,
gasLimit: gasLimit,
timestamp: args.data.timestamp
}
const payLoad = {
funAbi: args.data.funAbi,
funArgs: args.data.funArgs,
contractBytecode: args.data.contractBytecode,
contractName: args.data.contractName,
contractABI: args.data.contractABI,
linkReferences: args.data.linkReferences
}
if (!tx.timestamp) tx.timestamp = Date.now()
const timestamp = tx.timestamp
this._triggerEvent('initiatingTransaction', [timestamp, tx, payLoad])
try {
this.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb,
this.txRunner.rawRun(
tx,
confirmationCb,
continueCb,
promptCb,
async (error, result) => {
if (error) {
if (typeof (error) !== 'string') {
if (typeof error !== 'string') {
if (error.message) error = error.message
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)
@ -766,23 +1128,39 @@ export class Blockchain extends Plugin {
const isVM = this.executionContext.isVM()
if (isVM && tx.useCall) {
try {
result.transactionHash = await this.web3().eth.getHashFromTagBySimulator(timestamp)
result.transactionHash =
await this.web3().eth.getHashFromTagBySimulator(timestamp)
} catch (e) {
console.log('unable to retrieve back the "call" hash', e)
}
}
const eventName = (tx.useCall ? 'callExecuted' : 'transactionExecuted')
this._triggerEvent(eventName, [error, tx.from, tx.to, tx.data, tx.useCall, result, timestamp, payLoad])
const eventName = tx.useCall
? 'callExecuted'
: 'transactionExecuted'
this._triggerEvent(eventName, [
error,
tx.from,
tx.to,
tx.data,
tx.useCall,
result,
timestamp,
payLoad
])
return resolve({result, tx})
}
)
} catch (err) {
let error = err
if (error && (typeof (error) !== 'string')) {
if (error && typeof error !== 'string') {
if (error.message) error = error.message
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)
@ -804,18 +1182,26 @@ export class Blockchain extends Plugin {
let execResult
let returnValue = null
if (isVM) {
const hhlogs = await this.web3().eth.getHHLogsForTx(txResult.transactionHash)
const hhlogs = await this.web3().eth.getHHLogsForTx(
txResult.transactionHash
)
if (hhlogs && hhlogs.length) {
const finalLogs = <div><div><b>console.log:</b></div>
{
hhlogs.map((log) => {
const finalLogs = (
<div>
<div>
<b>console.log:</b>
</div>
{hhlogs.map((log) => {
let formattedLog
// 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
// For example: console.log("Name: %s, Age: %d", remix, 6) will log 'Name: remix, Age: 6'
// We check first arg to determine if 'util.format' is needed
if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) {
if (
typeof log[0] === 'string' &&
(log[0].includes('%s') || log[0].includes('%d'))
) {
formattedLog = format(log[0], ...log.slice(1))
} else {
formattedLog = log.join(' ')
@ -823,15 +1209,29 @@ export class Blockchain extends Plugin {
return <div>{formattedLog}</div>
})}
</div>
)
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'logHtml', finalLogs)
}
execResult = await this.web3().eth.getExecutionResultFromSimulator(txResult.transactionHash)
execResult = await this.web3().eth.getExecutionResultFromSimulator(
txResult.transactionHash
)
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.
returnValue = execResult ? toBuffer(execResult.returnValue) : toBuffer(addHexPrefix(txResult.result) || '0x0000000000000000000000000000000000000000000000000000000000000000')
const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas')
const vmError = txExecution.checkVMError(execResult, compiledContracts)
returnValue = execResult
? toBuffer(execResult.returnValue)
: toBuffer(
addHexPrefix(txResult.result) ||
'0x0000000000000000000000000000000000000000000000000000000000000000'
)
const compiledContracts = await this.call(
'compilerArtefacts',
'getAllContractDatas'
)
const vmError = txExecution.checkVMError(
execResult,
compiledContracts
)
if (vmError.error) {
return cb(vmError.message)
}

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

@ -1,11 +1,11 @@
const {composePlugins, withNx} = require('@nrwl/webpack')
const {withReact} = require('@nrwl/react')
const webpack = require('webpack')
const CopyPlugin = require("copy-webpack-plugin")
const CopyPlugin = require('copy-webpack-plugin')
const version = require('../../package.json').version
const fs = require('fs')
const TerserPlugin = require("terser-webpack-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const axios = require('axios')
const versionData = {
@ -16,12 +16,18 @@ const versionData = {
const loadLocalSolJson = async () => {
// execute apps/remix-ide/ci/downloadsoljson.sh
const child = require('child_process').execSync('bash ./apps/remix-ide/ci/downloadsoljson.sh', { encoding: 'utf8', cwd: process.cwd(), shell: true })
const child = require('child_process').execSync(
'bash ./apps/remix-ide/ci/downloadsoljson.sh',
{encoding: 'utf8', cwd: process.cwd(), shell: true}
)
// show output
console.log(child)
}
fs.writeFileSync('./apps/remix-ide/src/assets/version.json', JSON.stringify(versionData))
fs.writeFileSync(
'./apps/remix-ide/src/assets/version.json',
JSON.stringify(versionData)
)
loadLocalSolJson()
@ -33,8 +39,7 @@ const copyPatterns = implicitDependencies.map((dep) => {
try {
fs.statSync(__dirname + `/../../dist/apps/${dep}`).isDirectory()
return {from: `../../dist/apps/${dep}`, to: `plugins/${dep}`}
}
catch (e) {
} catch (e) {
console.log('error', e)
return false
}
@ -50,30 +55,29 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// add fallback for node modules
config.resolve.fallback = {
...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"path": require.resolve("path-browserify"),
"http": require.resolve("stream-http"),
"https": require.resolve("https-browserify"),
"constants": require.resolve("constants-browserify"),
"os": false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"),
"fs": false,
"module": false,
"tls": false,
"net": false,
"readline": false,
"child_process": false,
"buffer": require.resolve("buffer/"),
"vm": require.resolve('vm-browserify'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
path: require.resolve('path-browserify'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
constants: require.resolve('constants-browserify'),
os: false, //require.resolve("os-browserify/browser"),
timers: false, // require.resolve("timers-browserify"),
zlib: require.resolve('browserify-zlib'),
fs: false,
module: false,
tls: false,
net: false,
readline: false,
child_process: false,
buffer: require.resolve('buffer/'),
vm: require.resolve('vm-browserify')
}
// add externals
config.externals = {
...config.externals,
solc: 'solc',
solc: 'solc'
}
// add public path
@ -83,12 +87,14 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
config.output.filename = `[name].${versionData.version}.${versionData.timestamp}.js`
config.output.chunkFilename = `[name].${versionData.version}.${versionData.timestamp}.js`
// add copy & provide plugin
config.plugins.push(
new CopyPlugin({
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
].filter(Boolean)
}),
@ -96,15 +102,15 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'],
process: 'process/browser',
process: 'process/browser'
})
)
// souce-map loader
config.module.rules.push({
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
use: ['source-map-loader'],
enforce: 'pre'
})
config.ignoreWarnings = [/Failed to parse source map/, /require function/] // ignore source-map-loader warnings & AST warnings
@ -118,20 +124,20 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false,
mangle: false,
format: {
comments: false,
},
comments: false
}
},
extractComments: false,
extractComments: false
}),
new CssMinimizerPlugin(),
];
new CssMinimizerPlugin()
]
config.watchOptions = {
ignored: /node_modules/
}
return config;
});
return config
})
class CopyFileAfterBuild {
apply(compiler) {
@ -142,17 +148,20 @@ class CopyFileAfterBuild {
// 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.
const files = fs.readdirSync('./dist/apps/etherscan')
files.forEach(file => {
files.forEach((file) => {
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
)
}
})
} catch (e) {
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 { SolHint } from "./SolhintPluginClient";
import React, {useEffect, useState} from 'react'
import {SolHint} from './SolhintPluginClient'
const client = new SolHint();
const client = new SolHint()
export default function App() {
return <></>;
return <></>
}

@ -1,18 +1,18 @@
const {composePlugins, withNx} = require('@nrwl/webpack')
const {withReact} = require('@nrwl/react')
const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => {
// Update the webpack config as needed here.
config.resolve.fallback = {
...config.resolve.fallback,
"path": false,
"os": false,
"fs": false,
"module": false,
path: false,
os: false,
fs: false,
module: false
}
// add public path
@ -23,18 +23,18 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'],
process: 'process/browser',
process: 'process/browser'
}),
new webpack.DefinePlugin({
"BROWSER": true,
}),
BROWSER: true
})
)
// souce-map loader
config.module.rules.push({
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
use: ['source-map-loader'],
enforce: 'pre'
})
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
@ -48,13 +48,13 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false,
mangle: false,
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 version = require('../../package.json').version
const fs = require('fs')
const TerserPlugin = require("terser-webpack-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const versionData = {
version: version,
@ -12,7 +12,10 @@ const versionData = {
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development'
}
fs.writeFileSync('./apps/remix-ide/src/assets/version.json', JSON.stringify(versionData))
fs.writeFileSync(
'./apps/remix-ide/src/assets/version.json',
JSON.stringify(versionData)
)
// Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => {
@ -22,56 +25,52 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
// add fallback for node modules
config.resolve.fallback = {
...config.resolve.fallback,
"crypto": require.resolve("crypto-browserify"),
"stream": require.resolve("stream-browserify"),
"path": require.resolve("path-browserify"),
"http": require.resolve("stream-http"),
"https": require.resolve("https-browserify"),
"constants": require.resolve("constants-browserify"),
"os": false, //require.resolve("os-browserify/browser"),
"timers": false, // require.resolve("timers-browserify"),
"zlib": require.resolve("browserify-zlib"),
"fs": false,
"module": false,
"tls": false,
"net": false,
"readline": false,
"child_process": false,
"buffer": require.resolve("buffer/"),
"vm": require.resolve('vm-browserify'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
path: require.resolve('path-browserify'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
constants: require.resolve('constants-browserify'),
os: false, //require.resolve("os-browserify/browser"),
timers: false, // require.resolve("timers-browserify"),
zlib: require.resolve('browserify-zlib'),
fs: false,
module: false,
tls: false,
net: false,
readline: false,
child_process: false,
buffer: require.resolve('buffer/'),
vm: require.resolve('vm-browserify')
}
// add externals
config.externals = {
...config.externals,
solc: 'solc',
solc: 'solc'
}
// add public path
config.output.publicPath = '/'
// add copy & provide plugin
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'],
process: 'process/browser',
process: 'process/browser'
})
)
// souce-map loader
config.module.rules.push({
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
use: ['source-map-loader'],
enforce: 'pre'
})
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
// set minimizer
config.optimization.minimizer = [
new TerserPlugin({
@ -81,17 +80,17 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
compress: false,
mangle: false,
format: {
comments: false,
},
comments: false
}
},
extractComments: false,
extractComments: false
}),
new CssMinimizerPlugin(),
];
new CssMinimizerPlugin()
]
config.watchOptions = {
ignored: /node_modules/
}
return config;
});
return config
})

@ -38,7 +38,7 @@ const App: React.FC = () => {
async function start() {
try {
await remixClient.loaded()
remixClient.onFileChange(name => setContract(name))
remixClient.onFileChange((name) => setContract(name))
remixClient.onNoFileSelected(() => setContract(''))
} catch (err) {
console.log(err)
@ -83,7 +83,11 @@ const App: React.FC = () => {
</header>
<section>
<div className="px-4 w-100">
<Button data-id="add-repository" className="w-100 text-dark w-100 bg-light btn-outline-primary " onClick={() => remixClient.cloneVyperRepo()}>
<Button
data-id="add-repository"
className="w-100 text-dark w-100 bg-light btn-outline-primary "
onClick={() => remixClient.cloneVyperRepo()}
>
Clone Vyper examples repository
</Button>
</div>
@ -93,10 +97,20 @@ const App: React.FC = () => {
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
</ToggleButton>
<ToggleButton data-id="local-compiler" variant="secondary" name="local" value="local">
<ToggleButton
data-id="local-compiler"
variant="secondary"
name="local"
value="local"
>
Local Compiler
</ToggleButton>
</ToggleButtonGroup>
@ -110,9 +124,7 @@ const App: React.FC = () => {
<CompilerButton
compilerUrl={compilerUrl()}
contract={contract}
setOutput={(name, update) =>
setOutput({ ...output, [name]: update })
}
setOutput={(name, update) => setOutput({...output, [name]: update})}
/>
</div>
<article id="result" className="px-2">

@ -11,12 +11,11 @@ import Button from 'react-bootstrap/Button'
interface Props {
compilerUrl: string
contract?: string,
contract?: string
setOutput: (name: string, output: VyperCompilationOutput) => void
}
function CompilerButton({contract, setOutput, compilerUrl}: Props) {
if (!contract || !contract) {
return <Button disabled>No contract selected</Button>
}
@ -69,7 +68,8 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
errorIndex = errorIndex + 4
if (message && message.split('\n\n').length > 0) {
try {
message = message.split('\n\n')[message.split('\n\n').length - 1]
message =
message.split('\n\n')[message.split('\n\n').length - 1]
} catch (e) {}
}
if (location.length > 0) {
@ -77,7 +77,11 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
start: {line: parseInt(location[0]) - 1, column: 10},
end: {line: parseInt(location[0]) - 1, column: 10}
}
remixClient.highlight(lineColumnPos as any, _contract.name, message)
remixClient.highlight(
lineColumnPos as any,
_contract.name,
message
)
}
})
}
@ -103,9 +107,17 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
}
return (
<Button data-id="compile" onClick={compileContract} variant="primary" title={contract} className="d-flex flex-column">
<Button
data-id="compile"
onClick={compileContract}
variant="primary"
title={contract}
className="d-flex flex-column"
>
<span>Compile</span>
<span className="overflow-hidden text-truncate text-nowrap" >{contract}</span>
<span className="overflow-hidden text-truncate text-nowrap">
{contract}
</span>
</Button>
)
}

@ -3,12 +3,11 @@ import Form from 'react-bootstrap/Form'
interface Props {
url: string
setUrl: (url: string) => void,
setUrl: (url: string) => void
environment: 'remote' | 'local'
}
function LocalUrlInput({url, setUrl, environment}: Props) {
if (environment === 'remote') {
return <></>
}
@ -20,17 +19,20 @@ function LocalUrlInput({ url, setUrl, environment }: Props) {
return (
<Form id="local-url">
<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.Control onBlur={updateUrl}
<Form.Control
onBlur={updateUrl}
defaultValue={url}
type="email"
placeholder="eg http://localhost:8000/compile" />
<Form.Text className="text-muted">
</Form.Text>
placeholder="eg http://localhost:8000/compile"
/>
<Form.Text className="text-muted"></Form.Text>
</Form.Group>
</Form>
)
}
export default LocalUrlInput;
export default LocalUrlInput

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

@ -5,14 +5,16 @@ interface Props {
}
function WarnRemoteLabel({environment}: Props) {
if (environment === 'local') {
return <></>
}
return (
<small className="mx-4 text-warning pb-4">The remote compiler should only be used for testing NOT for production environments. For production, use a local compiler.</small>
<small className="mx-4 text-warning pb-4">
The remote compiler should only be used for testing NOT for production
environments. For production, use a local compiler.
</small>
)
}
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 {
name: string;
content: string;
name: string
content: string
}
export interface VyperCompilationResult {
status: 'success',
bytecode: string,
bytecode_runtime: string,
abi: ABIDescription[],
ir: string,
status: 'success'
bytecode: string
bytecode_runtime: string
abi: ABIDescription[]
ir: string
method_identifiers: {
[method: string]: string
}
@ -23,10 +23,14 @@ export interface VyperCompilationError {
message: string
}
export type VyperCompilationOutput = VyperCompilationResult | VyperCompilationError
export type VyperCompilationOutput =
| VyperCompilationResult
| VyperCompilationError
/** Check if the output is an error */
export function isCompilationError(output: VyperCompilationOutput): output is VyperCompilationError {
export function isCompilationError(
output: VyperCompilationOutput
): output is VyperCompilationError {
return output.status === 'failed'
}
@ -35,7 +39,10 @@ export function isCompilationError(output: VyperCompilationOutput): output is Vy
* @param url The url of the compiler
* @param contract The name and content of the contract
*/
export async function compile(url: string, contract: Contract): Promise<VyperCompilationOutput> {
export async function compile(
url: string,
contract: Contract
): Promise<VyperCompilationOutput> {
if (!contract.name) {
throw new Error('Set your Vyper contract file.')
}
@ -63,9 +70,14 @@ export async function compile(url: string, contract: Contract): Promise<VyperCom
* @param name Name of the contract file
* @param compilationResult Result returned by the compiler
*/
export function toStandardOutput(fileName: string, compilationResult: VyperCompilationResult): CompilationResult {
const contractName = fileName.split('/').slice(-1)[0].split('.')[0];
const methodIdentifiers = JSON.parse(JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g,''));
export function toStandardOutput(
fileName: string,
compilationResult: VyperCompilationResult
): CompilationResult {
const contractName = fileName.split('/').slice(-1)[0].split('.')[0]
const methodIdentifiers = JSON.parse(
JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g, '')
)
return {
sources: {
[fileName]: {
@ -85,21 +97,20 @@ export function toStandardOutput(fileName: string, compilationResult: VyperCompi
bytecode: {
linkReferences: {},
object: compilationResult['bytecode'].replace('0x', ''),
opcodes: ""
opcodes: ''
},
deployedBytecode: {
linkReferences: {},
object: compilationResult['bytecode_runtime'].replace('0x', ''),
opcodes: ""
opcodes: ''
},
methodIdentifiers: methodIdentifiers
}
}
} as any
}
};
}
}
/*
export function createCompilationResultMessage(name: string, result: any) {

@ -1,12 +1,16 @@
import { HighlightPosition, CompilationResult, RemixApi } from '@remixproject/plugin-api';
import { Api, Status } from '@remixproject/plugin-utils';
import {
HighlightPosition,
CompilationResult,
RemixApi
} from '@remixproject/plugin-api'
import {Api, Status} from '@remixproject/plugin-utils'
import {createClient} from '@remixproject/plugin-webview'
import { PluginClient } from '@remixproject/plugin';
import { Contract } from './compiler';
import { ExampleContract } from '../components/VyperResult';
import {PluginClient} from '@remixproject/plugin'
import {Contract} from './compiler'
import {ExampleContract} from '../components/VyperResult'
export class RemixClient extends PluginClient {
private client = createClient<Api, Readonly<RemixApi>>(this);
private client = createClient<Api, Readonly<RemixApi>>(this)
loaded() {
return this.client.onload()
@ -14,9 +18,13 @@ export class RemixClient extends PluginClient {
/** Emit an event when file changed */
async onFileChange(cb: (contract: string) => any) {
this.client.on('fileManager', 'currentFileChanged', async (name: string) => {
this.client.on(
'fileManager',
'currentFileChanged',
async (name: string) => {
cb(name)
})
}
)
}
/** Emit an event when file changed */
@ -29,8 +37,17 @@ export class RemixClient extends PluginClient {
/** Load Ballot contract example into the file manager */
async loadContract({name, address}: ExampleContract) {
try {
const content = await this.client.call('contentImport', 'resolve', address)
await this.client.call('fileManager', 'setFile', content.cleanUrl, content.content)
const content = await this.client.call(
'contentImport',
'resolve',
address
)
await this.client.call(
'fileManager',
'setFile',
content.cleanUrl,
content.content
)
await this.client.call('fileManager', 'switchFile', content.cleanUrl)
} catch (err) {
console.log(err)
@ -43,9 +60,18 @@ export class RemixClient extends PluginClient {
this.call('notification', 'toast', 'cloning Vyper repository...')
await this.call('manager', 'activatePlugin', 'dGitProvider')
// @ts-ignore
await this.call('dGitProvider', 'clone', { url: 'https://github.com/vyperlang/vyper', token: null }, 'vyper-lang')
await this.call(
'dGitProvider',
'clone',
{url: 'https://github.com/vyperlang/vyper', token: null},
'vyper-lang'
)
// @ts-ignore
this.call('notification', 'toast', 'Vyper repository cloned, the workspace Vyper has been created.')
this.call(
'notification',
'toast',
'Vyper repository cloned, the workspace Vyper has been created.'
)
} catch (e) {
// @ts-ignore
this.call('notification', 'toast', e.message)
@ -54,11 +80,15 @@ export class RemixClient extends PluginClient {
/** Update the status of the plugin in remix */
changeStatus(status: Status) {
this.client.emit('statusChanged', status);
this.client.emit('statusChanged', status)
}
/** Highlight a part of the editor */
async highlight(lineColumnPos: HighlightPosition, name: string, message: string) {
async highlight(
lineColumnPos: HighlightPosition,
name: string,
message: string
) {
await this.client.call('editor', 'highlight', lineColumnPos, name)
/*
column: -1
@ -94,13 +124,13 @@ export class RemixClient extends PluginClient {
const content = await this.client.call('fileManager', 'getFile', name)
return {
name,
content,
content
}
}
/** Emit an event to Remix with compilation result */
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 * as ReactDOM from 'react-dom';
import {StrictMode} from 'react'
import * as ReactDOM from 'react-dom'
import App from './app/app'
import App from './app/app';
ReactDOM.render(<StrictMode><App /></StrictMode>, document.getElementById('root'));
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
document.getElementById('root')
)

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

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

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

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

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

@ -10,8 +10,7 @@ const shortFilename = 'simple_storage.sol'
const inputJson = {
language: 'Solidity',
sources: {
},
sources: {},
settings: {
optimizer: {
enabled: true,
@ -20,19 +19,33 @@ const inputJson = {
outputSelection: {
'*': {
'': ['ast'],
'*': [ 'abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates' ]
'*': [
'abi',
'metadata',
'devdoc',
'userdoc',
'evm.legacyAssembly',
'evm.bytecode',
'evm.deployedBytecode',
'evm.methodIdentifiers',
'evm.gasEstimates'
]
}
}
}
}
inputJson.sources[shortFilename] = {content: fs.readFileSync(filename).toString()}
inputJson.sources[shortFilename] = {
content: fs.readFileSync(filename).toString()
}
console.dir(inputJson)
console.log('compiling...')
const compilationData = JSON.parse(solc.compileStandardWrapper(JSON.stringify(inputJson)))
const compilationData = JSON.parse(
solc.compileStandardWrapper(JSON.stringify(inputJson))
)
console.dir(Object.keys(compilationData))
const compilation = {}
compilation['data'] = compilationData
@ -106,4 +119,3 @@ repl.start({
})
module.exports = cmdLine

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

@ -10,7 +10,12 @@ const AppDialogs = () => {
return (
<>
<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

@ -6,7 +6,7 @@ declare global {
_paq: any
}
}
const _paq = window._paq = window._paq || []
const _paq = (window._paq = window._paq || [])
const MatomoDialog = (props) => {
const {settings, showMatamo, appManager} = useContext(AppContext)
@ -14,17 +14,63 @@ const MatomoDialog = (props) => {
const [visible, setVisible] = useState<boolean>(props.hide)
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>
<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>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>You can change your choice in the Settings panel anytime.</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>
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>
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(() => {
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])
@ -38,13 +84,14 @@ const MatomoDialog = (props) => {
const handleModalOkClick = async () => {
_paq.push(['forgetUserOptOut'])
// @TODO remove next line when https://github.com/matomo-org/matomo/commit/9e10a150585522ca30ecdd275007a882a70c6df5 is used
document.cookie = 'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'
document.cookie =
'mtm_consent_removed=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'
settings.updateMatomoAnalyticsChoice(true)
appManager.call('walkthrough', 'start')
setVisible(false)
}
return (<></>)
return <></>
}
export default MatomoDialog

@ -1,5 +1,9 @@
import React, {useEffect, useRef, useState} from 'react'
import { ModalDialog, ModalDialogProps, ValidationResult } from '@remix-ui/modal-dialog'
import {
ModalDialog,
ModalDialogProps,
ValidationResult
} from '@remix-ui/modal-dialog'
import {ModalTypes} from '../../types'
interface ModalWrapperProps extends ModalDialogProps {
@ -28,36 +32,54 @@ const ModalWrapper = (props: ModalWrapperProps) => {
if (ref.current === undefined && formRef.current === undefined) {
onOkFn()
} else if (formRef.current) {
(props.okFn) ? props.okFn(getFormData()) : props.resolve(getFormData())
props.okFn ? props.okFn(getFormData()) : props.resolve(getFormData())
} else if (ref.current) {
// @ts-ignore: Object is possibly 'null'.
(props.okFn) ? props.okFn(ref.current.value) : props.resolve(ref.current.value)
props.okFn
? props.okFn(ref.current.value)
: props.resolve(ref.current.value)
}
}
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 () => {
(props.cancelFn) ? props.cancelFn() : props.resolve(false)
props.cancelFn ? props.cancelFn() : props.resolve(false)
}
const onInputChanged = (event) => {
if (props.validationFn) {
const validation = props.validationFn(event.target.value)
setState(prevState => {
return { ...prevState, message: createModalMessage(props.defaultValue, validation), validation }
setState((prevState) => {
return {
...prevState,
message: createModalMessage(props.defaultValue, validation),
validation
}
})
}
}
const createModalMessage = (defaultValue: string, validation: ValidationResult) => {
const createModalMessage = (
defaultValue: string,
validation: ValidationResult
) => {
return (
<>
{props.message}
<input 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>}
<input
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 +87,7 @@ const ModalWrapper = (props: ModalWrapperProps) => {
const onFormChanged = () => {
if (props.validationFn) {
const validation = props.validationFn(getFormData())
setState(prevState => {
setState((prevState) => {
return {...prevState, message: createForm(validation), validation}
})
}
@ -77,7 +99,9 @@ const ModalWrapper = (props: ModalWrapperProps) => {
<form onChange={onFormChanged} ref={formRef}>
{props.message}
</form>
{validation && !validation.valid && <span className='text-warning'>{validation.message}</span>}
{validation && !validation.valid && (
<span className="text-warning">{validation.message}</span>
)}
</>
)
}
@ -122,14 +146,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.
const handleHide = () => {
setState(prevState => {
setState((prevState) => {
return {...prevState, message: ''}
})
props.handleHide()
}
return (
<ModalDialog id={props.id} {...state} handleHide={handleHide} />
)
return <ModalDialog id={props.id} {...state} handleHide={handleHide} />
}
export default ModalWrapper

@ -9,14 +9,23 @@ const OriginWarning = () => {
useEffect(() => {
// check the origin and warn message
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.')
} else if (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.')
} else if (window.location.protocol.indexOf('http') === 0 &&
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' ||
(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.'
)
} else if (
window.location.protocol.indexOf('http') === 0 &&
window.location.hostname !== 'remix.ethereum.org' &&
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
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`)
@ -29,7 +38,7 @@ const OriginWarning = () => {
}
}, [content])
return (<></>)
return <></>
}
export default OriginWarning

@ -1,16 +1,27 @@
import React from 'react'
const RemixSplashScreen = (props) => {
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">
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"
>
<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="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>
<div className="info-secondary splash">
REMIX IDE
<div className="info-secondary splash">REMIX IDE</div>
</div>
</div></>)
</>
)
}
export default RemixSplashScreen

@ -8,16 +8,18 @@ export interface dispatchModalInterface {
modal: (data: AppModal) => void
toast: (message: string | JSX.Element) => void
alert: (data: AlertModal) => void
handleHideModal: () => void,
handleHideModal: () => void
handleToaster: () => void
}
export const dispatchModalContext = React.createContext<dispatchModalInterface>({
export const dispatchModalContext = React.createContext<dispatchModalInterface>(
{
modal: (data: AppModal) => {},
toast: (message: string | JSX.Element) => {},
alert: (data: AlertModal) => {},
handleHideModal: () => {},
handleToaster: () => {}
})
}
)
export const modalContext = React.createContext(ModalInitialState)

@ -6,8 +6,15 @@ import { ModalInitialState } from '../state/modals'
import {ModalTypes} from '../types'
import {AppContext, dispatchModalContext, modalContext} from './context'
export const ModalProvider = ({ children = [], reducer = modalReducer, initialState = ModalInitialState } = {}) => {
const [{ modals, toasters, focusModal, focusToaster }, dispatch] = useReducer(reducer, initialState)
export const ModalProvider = ({
children = [],
reducer = modalReducer,
initialState = ModalInitialState
} = {}) => {
const [{modals, toasters, focusModal, focusToaster}, dispatch] = useReducer(
reducer,
initialState
)
const onNextFn = async () => {
dispatch({
@ -16,17 +23,53 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
}
const modal = (modalData: AppModal) => {
const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData
const {
id,
title,
message,
validationFn,
okLabel,
okFn,
cancelLabel,
cancelFn,
modalType,
defaultValue,
hideFn,
data
} = modalData
return new Promise((resolve, reject) => {
dispatch({
type: modalActionTypes.setModal,
payload: { id, title, message, okLabel, 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) => {
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 = () => {
@ -50,17 +93,25 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
})
}
return (<dispatchModalContext.Provider value={{ modal, toast, alert, handleHideModal, handleToaster }}>
<modalContext.Provider value={{ modals, toasters, focusModal, focusToaster }}>
return (
<dispatchModalContext.Provider
value={{modal, toast, alert, handleHideModal, handleToaster}}
>
<modalContext.Provider
value={{modals, toasters, focusModal, focusToaster}}
>
{children}
</modalContext.Provider>
</dispatchModalContext.Provider>)
</dispatchModalContext.Provider>
)
}
export const AppProvider = ({children = [], value = {}} = {}) => {
return <AppContext.Provider value={value}>
return (
<AppContext.Provider value={value}>
<ModalProvider>{children}</ModalProvider>
</AppContext.Provider>
)
}
export const useDialogs = () => {

@ -9,7 +9,7 @@ import AppDialogs from './components/modals/dialogs'
import DialogViewPlugin from './components/modals/dialogViewPlugin'
import {AppContext} from './context/context'
import {IntlProvider} from 'react-intl'
import { CustomTooltip } from '@remix-ui/helper';
import {CustomTooltip} from '@remix-ui/helper'
interface IRemixAppUi {
app: any
@ -20,7 +20,10 @@ const RemixApp = (props: IRemixAppUi) => {
const [hideSidePanel, setHideSidePanel] = useState<boolean>(false)
const [maximiseTrigger, setMaximiseTrigger] = 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)
useEffect(() => {
@ -39,7 +42,7 @@ const RemixApp = (props: IRemixAppUi) => {
function setListeners() {
props.app.sidePanel.events.on('toggle', () => {
setHideSidePanel(prev => {
setHideSidePanel((prev) => {
return !prev
})
})
@ -55,13 +58,13 @@ const RemixApp = (props: IRemixAppUi) => {
})
props.app.layout.event.on('maximisesidepanel', () => {
setMaximiseTrigger(prev => {
setMaximiseTrigger((prev) => {
return prev + 1
})
})
props.app.layout.event.on('resetsidepanel', () => {
setResetTrigger(prev => {
setResetTrigger((prev) => {
return prev + 1
})
})
@ -84,18 +87,47 @@ const RemixApp = (props: IRemixAppUi) => {
<AppProvider value={value}>
<OriginWarning></OriginWarning>
<MatomoDialog hide={!appReady}></MatomoDialog>
<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 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'>
<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
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>
<CustomTooltip
placement="bottom"
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>
<div className="remix-ui-tabs_end remix-bg-opacity position-absolute position-fixed"></div>
</CustomTooltip>
</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 './remix-ui-checkbox.css'
type Placement = import('react-overlays/usePopper').Placement;
type Placement = import('react-overlays/usePopper').Placement
/* eslint-disable-next-line */
export interface RemixUiCheckboxProps {
@ -38,16 +37,25 @@ export const RemixUiCheckbox = ({
optionalClassName = '',
display = 'flex',
disabled,
tooltipPlacement = 'right',
tooltipPlacement = 'right'
}: RemixUiCheckboxProps) => {
const childJSXWithTooltip = (
<CustomTooltip
tooltipText={title}
tooltipId={`${name}Tooltip`}
placement={tooltipPlacement}
>
<div className={`listenOnNetwork_2A0YE0 custom-control custom-checkbox ${optionalClassName}`} 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}
@ -58,7 +66,12 @@ export const RemixUiCheckbox = ({
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> : ''}
{label}
</label>
@ -66,7 +79,17 @@ export const RemixUiCheckbox = ({
</CustomTooltip>
)
const childJSX = (
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
<div
className="listenOnNetwork_2A0YE0 custom-control custom-checkbox"
style={
{
display: display,
alignItems: 'center',
visibility: visibility
} as CSSProperties
}
onClick={onClick}
>
<input
id={id}
type={inputType}
@ -76,15 +99,17 @@ export const RemixUiCheckbox = ({
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> : ''}
{label}
</label>
</div>
)
return (
title ? (childJSXWithTooltip) : (childJSX)
)
return title ? childJSXWithTooltip : childJSX
}
export default RemixUiCheckbox

@ -6,17 +6,24 @@ import './copy-to-clipboard.css'
import {CustomTooltip} from '@remix-ui/helper'
interface ICopyToClipboard {
content?: any,
tip?: string,
icon?: string,
direction?: Placement,
className?: string,
title?: string,
children?: JSX.Element,
content?: any
tip?: string
icon?: string
direction?: Placement
className?: string
title?: string
children?: JSX.Element
getContent?: () => any
}
export const CopyToClipboard = (props: ICopyToClipboard) => {
const { tip = 'Copy', icon = 'fa-copy', direction = 'right', getContent, children, ...otherProps } = props
const {
tip = 'Copy',
icon = 'fa-copy',
direction = 'right',
getContent,
children,
...otherProps
} = props
let {content} = props
const [message, setMessage] = useState(tip)
@ -37,7 +44,8 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
}
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()
} else {
content = getContent && getContent()
@ -50,14 +58,16 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
setTimeout(() => setMessage(tip), 500)
}
const childJSX = (
children || (<i className={`far ${icon} ml-1 p-2`} aria-hidden="true"
const childJSX = children || (
<i
className={`far ${icon} ml-1 p-2`}
aria-hidden="true"
{...otherProps}
></i>)
></i>
)
return (
<a href='#' onClick={handleClick} onMouseLeave={reset}>
<a href="#" onClick={handleClick} onMouseLeave={reset}>
<CustomTooltip
tooltipText={message}
tooltipId="overlay-tooltip"

@ -1,11 +1,15 @@
import React, {useState, useEffect} from 'react' // 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([])
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) {
return {
key: innerKey,
@ -17,7 +21,8 @@ export const useExtractData = (json, extractFunc?: ExtractFunc): Array<{ key: st
data: extractDataDefault(json[innerKey], json)
}
}
})
}
)
setData(data)

@ -2,7 +2,19 @@ import { CustomTooltip } from '@remix-ui/helper'
import React, {useState, useEffect} from 'react' // eslint-disable-line
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({
intoBackDisabled: true,
overBackDisabled: true,
@ -46,29 +58,72 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
intoForwardDisabled: stepState === 'end',
overForwardDisabled: stepState === 'end',
jumpNextBreakpointDisabled: stepState === 'end',
jumpOutDisabled: (jumpOutDisabled !== null) && (jumpOutDisabled !== undefined) ? jumpOutDisabled : true
jumpOutDisabled:
jumpOutDisabled !== null && jumpOutDisabled !== undefined
? jumpOutDisabled
: true
}
})
}
const stepBtnStyle = 'd-flex align-items-center justify-content-center btn btn-primary btn-sm stepButton h-75 m-0 p-1'
const stepBtnStyle =
'd-flex align-items-center justify-content-center btn btn-primary btn-sm stepButton h-75 m-0 p-1'
const disableStepBtnStyle = 'stepButtonDisabled'
const disableJumpBtnStyle = 'jumpButtonDisabled'
const stepMarkupStructure = {
stepOverBackJSX: {
markup: (<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' }}>
markup: (
<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>
</button>
</div>),
</div>
),
placement: 'top-start',
tagId: 'overbackTooltip',
tooltipMsg: 'Step over back'
},
stepBackJSX: {
markup: (
<div 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' }}>
<div
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>
</button>
</div>
@ -80,8 +135,26 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
stepIntoJSX: {
markup: (
<div 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}
<div
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'}}
>
<span className="fas fa-level-down-alt"></span>
@ -94,22 +167,65 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
},
stepOverForwardJSX: {
markup: (
<div 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' }}>
<div
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>
</button>
</div>
),
placement: 'top-end',
tagId: 'overbackTooltip',
tooltipMsg: 'Step over forward',
tooltipMsg: 'Step over forward'
}
}
const jumpMarkupStructure = {
jumpPreviousBreakpointJSX: {
markup: (
<div 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' }}>
<div
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>
</button>
</div>
@ -120,8 +236,32 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
},
jumpOutJSX: {
markup: (
<div 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">
<div
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>
</button>
</div>
@ -132,8 +272,28 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
},
jumpNextBreakpointJSX: {
markup: (
<div 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' }}>
<div
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>
</button>
</div>
@ -147,8 +307,7 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
return (
<div className="buttons">
<div className="stepButtons btn-group py-1">
{
Object.keys(stepMarkupStructure).map(x => (
{Object.keys(stepMarkupStructure).map((x) => (
<CustomTooltip
placement={stepMarkupStructure[x].placement}
tooltipId={stepMarkupStructure[x].tagId}
@ -157,13 +316,11 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
>
{stepMarkupStructure[x].markup}
</CustomTooltip>
))
}
))}
</div>
<div className="jumpButtons btn-group py-1">
{
Object.keys(jumpMarkupStructure).map(x => (
{Object.keys(jumpMarkupStructure).map((x) => (
<CustomTooltip
placement={jumpMarkupStructure[x].placement}
tooltipText={jumpMarkupStructure[x].tooltipMsg}
@ -172,14 +329,46 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
>
{jumpMarkupStructure[x].markup}
</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 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>
)

@ -10,7 +10,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import {CustomTooltip, isValidHash} from '@remix-ui/helper'
/* eslint-disable-next-line */
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) => {
const debuggerModule = props.debuggerAPI
@ -54,7 +54,12 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const handleResize = () => {
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 +70,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
useEffect(() => {
window.addEventListener('resize', handleResize)
// TODO: not a good way to wait on the ref doms element to be rendered of course
setTimeout(() =>
handleResize(), 2000)
setTimeout(() => handleResize(), 2000)
return () => window.removeEventListener('resize', handleResize)
}, [state.debugging, state.isActive])
@ -85,11 +89,16 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
useEffect(() => {
const setEditor = () => {
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) => {
if (state.debugger) state.debugger.breakPointManager.add({ fileName: fileName, row: row })
if (state.debugger)
state.debugger.breakPointManager.add({fileName: fileName, row: row})
})
debuggerModule.onEditorContentChanged(() => {
@ -101,8 +110,9 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const providerChanged = () => {
debuggerModule.onEnvChanged((provider) => {
setState(prevState => {
const isLocalNodeUsed = !provider.startsWith('vm') && provider !== 'injected'
setState((prevState) => {
const isLocalNodeUsed =
!provider.startsWith('vm') && provider !== 'injected'
return {...prevState, isLocalNodeUsed: isLocalNodeUsed}
})
})
@ -116,34 +126,52 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
debuggerInstance.event.register('debuggerStatus', async (isActive) => {
await debuggerModule.discardHighlight()
setState(prevState => {
setState((prevState) => {
return {...prevState, isActive}
})
})
debuggerInstance.event.register('locatingBreakpoint', async (isActive) => {
setState(prevState => {
return { ...prevState, sourceLocationStatus: 'Locating breakpoint, this might take a while...' }
setState((prevState) => {
return {
...prevState,
sourceLocationStatus:
'Locating breakpoint, this might take a while...'
}
})
})
debuggerInstance.event.register('noBreakpointHit', async (isActive) => {
setState(prevState => {
setState((prevState) => {
return {...prevState, sourceLocationStatus: ''}
})
})
debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address, stepDetail, lineGasCost) => {
debuggerInstance.event.register(
'newSourceLocation',
async (
lineColumnPos,
rawLocation,
generatedSources,
address,
stepDetail,
lineGasCost
) => {
if (!lineColumnPos) {
await debuggerModule.discardHighlight()
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.' }
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
}
const contracts = await debuggerModule.fetchContractAndCompile(
address || currentReceipt.contractAddress || currentReceipt.to,
currentReceipt)
currentReceipt
)
if (contracts) {
let path = contracts.getSourceName(rawLocation.file)
if (!path) {
@ -155,7 +183,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
try {
content = await debuggerModule.getFile(path)
} 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)
}
if (content !== source.contents) {
@ -166,14 +195,21 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
}
if (path) {
setState(prevState => {
setState((prevState) => {
return {...prevState, sourceLocationStatus: ''}
})
await debuggerModule.discardHighlight()
await debuggerModule.highlight(lineColumnPos, path, rawLocation, stepDetail, lineGasCost)
await debuggerModule.highlight(
lineColumnPos,
path,
rawLocation,
stepDetail,
lineGasCost
)
}
}
})
}
)
debuggerInstance.event.register('debuggerUnloaded', () => unLoad())
}
@ -183,7 +219,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
const updateTxNumberFlag = (empty: boolean) => {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
txNumberIsEmpty: empty,
@ -194,7 +230,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const unloadRequested = (blockNumber, txIndex, tx) => {
unLoad()
setState(prevState => {
setState((prevState) => {
return {
...prevState,
sourceLocationStatus: ''
@ -205,7 +241,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const unLoad = () => {
debuggerModule.onStopDebugging()
if (state.debugger) state.debugger.unload()
setState(prevState => {
setState((prevState) => {
return {
...prevState,
isActive: false,
@ -228,10 +264,10 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const startDebugging = async (blockNumber, txNumber, tx, optWeb3?) => {
if (state.debugger) {
unLoad()
await (new Promise((resolve) => setTimeout(() => resolve({}), 1000)))
await new Promise((resolve) => setTimeout(() => resolve({}), 1000))
}
if (!txNumber) return
setState(prevState => {
setState((prevState) => {
return {
...prevState,
txNumber: txNumber,
@ -239,7 +275,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
})
if (!isValidHash(txNumber)) {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
validationError: 'Invalid transaction hash.'
@ -248,15 +284,20 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return
}
const web3 = optWeb3 || (state.opt.debugWithLocalNode ? await debuggerModule.web3() : await debuggerModule.getDebugWeb3())
const web3 =
optWeb3 ||
(state.opt.debugWithLocalNode
? await debuggerModule.web3()
: await debuggerModule.getDebugWeb3())
try {
const networkId = await web3.eth.net.getId()
_paq.push(['trackEvent', 'debugger', 'startDebugging', networkId])
if (networkId === 42) {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
validationError: 'Unfortunately, the Kovan network is not supported.'
validationError:
'Unfortunately, the Kovan network is not supported.'
}
})
return
@ -272,7 +313,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
currentBlock = await web3.eth.getBlock(currentReceipt.blockHash)
currentTransaction = await web3.eth.getTransaction(txNumber)
} catch (e) {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
validationError: e.message
@ -287,7 +328,11 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter,
compilationResult: async (address) => {
try {
if (!localCache[address]) localCache[address] = await debuggerModule.fetchContractAndCompile(address, currentReceipt)
if (!localCache[address])
localCache[address] = await debuggerModule.fetchContractAndCompile(
address,
currentReceipt
)
return localCache[address]
} catch (e) {
// debuggerModule.showMessage('Debugging error', 'Unable to fetch a transaction.')
@ -303,7 +348,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
try {
await debuggerInstance.debug(blockNumber, txNumber, tx, () => {
listenToEvents(debuggerInstance, currentReceipt)
setState(prevState => {
setState((prevState) => {
return {
...prevState,
blockNumber,
@ -320,10 +365,12 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
})
} catch (error) {
unLoad()
setState(prevState => {
setState((prevState) => {
let errorMsg = error.message || error
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.'
}
return {
...prevState,
@ -338,7 +385,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
const debug = (txHash, web3?) => {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
validationError: '',
@ -350,36 +397,105 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
const stepManager = {
jumpTo: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpTo.bind(state.debugger.step_manager) : null,
stepOverBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverBack.bind(state.debugger.step_manager) : null,
stepIntoBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepIntoBack.bind(state.debugger.step_manager) : null,
stepIntoForward: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepIntoForward.bind(state.debugger.step_manager) : null,
stepOverForward: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverForward.bind(state.debugger.step_manager) : null,
jumpOut: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpOut.bind(state.debugger.step_manager) : null,
jumpPreviousBreakpoint: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpPreviousBreakpoint.bind(state.debugger.step_manager) : null,
jumpNextBreakpoint: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpNextBreakpoint.bind(state.debugger.step_manager) : null,
jumpToException: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpToException.bind(state.debugger.step_manager) : null,
traceLength: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.traceLength : null,
registerEvent: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.event.register.bind(state.debugger.step_manager.event) : null
jumpTo:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.jumpTo.bind(state.debugger.step_manager)
: null,
stepOverBack:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.stepOverBack.bind(
state.debugger.step_manager
)
: null,
stepIntoBack:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.stepIntoBack.bind(
state.debugger.step_manager
)
: null,
stepIntoForward:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.stepIntoForward.bind(
state.debugger.step_manager
)
: null,
stepOverForward:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.stepOverForward.bind(
state.debugger.step_manager
)
: null,
jumpOut:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.jumpOut.bind(state.debugger.step_manager)
: null,
jumpPreviousBreakpoint:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.jumpPreviousBreakpoint.bind(
state.debugger.step_manager
)
: null,
jumpNextBreakpoint:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.jumpNextBreakpoint.bind(
state.debugger.step_manager
)
: null,
jumpToException:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.jumpToException.bind(
state.debugger.step_manager
)
: null,
traceLength:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.traceLength
: null,
registerEvent:
state.debugger && state.debugger.step_manager
? state.debugger.step_manager.event.register.bind(
state.debugger.step_manager.event
)
: null
}
const vmDebugger = {
registerEvent: state.debugger && state.debugger.vmDebuggerLogic ? state.debugger.vmDebuggerLogic.event.register.bind(state.debugger.vmDebuggerLogic.event) : null,
triggerEvent: state.debugger && state.debugger.vmDebuggerLogic ? state.debugger.vmDebuggerLogic.event.trigger.bind(state.debugger.vmDebuggerLogic.event) : null
registerEvent:
state.debugger && state.debugger.vmDebuggerLogic
? state.debugger.vmDebuggerLogic.event.register.bind(
state.debugger.vmDebuggerLogic.event
)
: null,
triggerEvent:
state.debugger && state.debugger.vmDebuggerLogic
? state.debugger.vmDebuggerLogic.event.trigger.bind(
state.debugger.vmDebuggerLogic.event
)
: null
}
const customJSX = (
<span className="p-0 m-0">
<input className="custom-control-input" id="debugGeneratedSourcesInput" onChange={({ target: { checked } }) => {
setState(prevState => {
return { ...prevState, opt: { ...prevState.opt, debugWithGeneratedSources: checked } }
<input
className="custom-control-input"
id="debugGeneratedSourcesInput"
onChange={({target: {checked}}) => {
setState((prevState) => {
return {
...prevState,
opt: {...prevState.opt, debugWithGeneratedSources: checked}
}
})
}} type="checkbox" />
}}
type="checkbox"
/>
<label
data-id="debugGeneratedSourcesLabel"
className="form-check-label custom-control-label"
htmlFor="debugGeneratedSourcesInput">
<FormattedMessage id='debugger.useGeneratedSources' />(Solidity {'>='} v0.7.2)
htmlFor="debugGeneratedSourcesInput"
>
<FormattedMessage id="debugger.useGeneratedSources" />
(Solidity {'>='} v0.7.2)
</label>
</span>
)
@ -391,48 +507,93 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
<div className="mt-2 mb-2 debuggerConfig custom-control custom-checkbox">
<CustomTooltip
tooltipId="debuggerGenSourceCheckbox"
tooltipText={<FormattedMessage id='debugger.debugWithGeneratedSources' />}
tooltipText={
<FormattedMessage id="debugger.debugWithGeneratedSources" />
}
placement="bottom-start"
>
{customJSX}
</CustomTooltip>
</div>
{ state.isLocalNodeUsed && <div className="mb-2 debuggerConfig custom-control custom-checkbox">
{state.isLocalNodeUsed && (
<div className="mb-2 debuggerConfig custom-control custom-checkbox">
<CustomTooltip
tooltipId="debuggerGenSourceInput"
tooltipText="Force the debugger to use the current local node"
placement='right'
placement="right"
>
<input
className="custom-control-input"
id="debugWithLocalNodeInput"
onChange={({target: {checked}}) => {
setState(prevState => {
return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } }
setState((prevState) => {
return {
...prevState,
opt: {...prevState.opt, debugWithLocalNode: checked}
}
})
}}
type="checkbox"
/>
</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>
}
{ 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>
<TxBrowser
requestDebug={requestDebug}
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>
<TxBrowser requestDebug={ requestDebug } 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 &&
)}
{!state.debugging && (
<div>
<i className="fas fa-info-triangle" aria-hidden="true"></i>
<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>
</div> }
</div>
)}
{state.debugging && <StepManager stepManager={stepManager} />}
</div>
<div className="debuggerPanels" ref={panelsRef}>
{ 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 && (
<VmDebuggerHead debugging={state.debugging} vmDebugger={vmDebugger} />
)}
{state.debugging && (
<VmDebugger
debugging={state.debugging}
vmDebugger={vmDebugger}
currentBlock={state.currentBlock}
currentReceipt={state.currentReceipt}
currentTransaction={state.currentTransaction}
/>
)}
</div>
</div>
)

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

@ -2,7 +2,21 @@ import React, { useState, useEffect } from 'react' // eslint-disable-line
import Slider from '../slider/slider' // 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({
sliderValue: -1,
revertWarning: '',
@ -16,13 +30,13 @@ export const StepManager = ({ stepManager: { jumpTo, traceLength, stepIntoBack,
}, [registerEvent])
const setRevertWarning = (warning) => {
setState(prevState => {
setState((prevState) => {
return {...prevState, revertWarning: warning}
})
}
const updateStep = (step, stepState, jumpOutDisabled) => {
setState(prevState => {
setState((prevState) => {
return {...prevState, sliderValue: step, stepState, jumpOutDisabled}
})
}
@ -30,7 +44,11 @@ export const StepManager = ({ stepManager: { jumpTo, traceLength, stepIntoBack,
return (
<div className="py-1">
<Slider jumpTo={jumpTo} sliderValue={sliderValue} traceLength={traceLength} />
<Slider
jumpTo={jumpTo}
sliderValue={sliderValue}
traceLength={traceLength}
/>
<ButtonNavigator
stepIntoBack={stepIntoBack}
stepIntoForward={stepIntoForward}

@ -3,7 +3,13 @@ import React, { useState, useEffect, useRef } from 'react' //eslint-disable-lin
import {useIntl, FormattedMessage} from 'react-intl'
import './tx-browser.css'
export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, transactionNumber, debugging }) => {
export const TxBrowser = ({
requestDebug,
updateTxNumberFlag,
unloadRequested,
transactionNumber,
debugging
}) => {
const [state, setState] = useState({
txNumber: ''
})
@ -13,7 +19,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
const intl = useIntl()
useEffect(() => {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
txNumber: transactionNumber
@ -39,7 +45,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')"
// pattern="^0[x,X]+[0-9a-fA-F]{22}"
// this.state.txNumberInput.setCustomValidity('')
setState(prevState => {
setState((prevState) => {
return {
...prevState,
txNumber: value
@ -51,40 +57,55 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
updateTxNumberFlag(!inputValue.current.value)
}
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
className='btn btn-link btn-sm btn-block h-75 p-0 m-0 text-decoration-none'
id='load'
className="btn btn-link btn-sm btn-block h-75 p-0 m-0 text-decoration-none"
id="load"
onClick={handleSubmit}
data-id='debuggerTransactionStartButton'
data-id="debuggerTransactionStartButton"
disabled={!state.txNumber}
style={{pointerEvents: 'none', color: 'white'}}
>
<span><FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} /></span>
<span>
<FormattedMessage
id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`}
/>
</span>
</button>
</div>
)
return (
<div className='pb-2 container px-0'>
<div className='txContainer'>
<div className='py-1 d-flex justify-content-center w-100 input-group'>
<div className="pb-2 container px-0">
<div className="txContainer">
<div className="py-1 d-flex justify-content-center w-100 input-group">
<input
ref={inputValue}
value={state.txNumber}
className='form-control m-0 txinput'
id='txinput'
type='text'
className="form-control m-0 txinput"
id="txinput"
type="text"
onChange={({target: {value}}) => txInputChanged(value)}
onInput={txInputOnInput}
placeholder={intl.formatMessage({id: 'debugger.placeholder'})}
data-id='debuggerTransactionInput'
data-id="debuggerTransactionInput"
disabled={debugging}
/>
</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
placement="bottom"
tooltipText={<FormattedMessage id={`debugger.${debugging ? 'stopDebugging' : 'startDebugging'}`} />}
tooltipText={
<FormattedMessage
id={`debugger.${
debugging ? 'stopDebugging' : 'startDebugging'
}`}
/>
}
tooltipId={'debuggingButtontooltip'}
tooltipClasses="text-nowrap"
>
@ -92,7 +113,7 @@ export const TxBrowser = ({ requestDebug, updateTxNumberFlag, unloadRequested, t
</CustomTooltip>
</div>
</div>
<span id='error' />
<span id="error" />
</div>
)
}

@ -8,19 +8,49 @@ export const AssemblyItems = ({ registerEvent }) => {
const [selectedItem, setSelectedItem] = useState(0)
const [nextSelectedItems, setNextSelectedItems] = useState([1])
const [returnInstructionIndexes, setReturnInstructionIndexes] = useState([])
const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState([])
const [outOfGasInstructionIndexes, setOutOfGasInstructionIndexes] = useState(
[]
)
const [opcodeTooltipText, setOpcodeTooltipText] = useState('')
const refs = useRef({})
const asmItemsRef = useRef(null)
useEffect(() => {
registerEvent && registerEvent('codeManagerChanged', (code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes) => {
dispatch({ type: 'FETCH_OPCODES_SUCCESS', payload: { code, address, index, nextIndexes, returnInstructionIndexes, outOfGasInstructionIndexes } })
registerEvent &&
registerEvent(
'codeManagerChanged',
(
code,
address,
index,
nextIndexes,
returnInstructionIndexes,
outOfGasInstructionIndexes
) => {
dispatch({
type: 'FETCH_OPCODES_SUCCESS',
payload: {
code,
address,
index,
nextIndexes,
returnInstructionIndexes,
outOfGasInstructionIndexes
}
})
}
)
registerEvent && registerEvent('lineGasCostChanged', (instructionsIndexes: number[], line: []) => {
dispatch({ type: 'FETCH_INDEXES_FOR_NEW_LINE', payload: { currentLineIndexes: instructionsIndexes || [], line } })
registerEvent &&
registerEvent(
'lineGasCostChanged',
(instructionsIndexes: number[], line: []) => {
dispatch({
type: 'FETCH_INDEXES_FOR_NEW_LINE',
payload: {currentLineIndexes: instructionsIndexes || [], line}
})
}
)
}, [])
useEffect(() => {
@ -128,17 +158,37 @@ export const AssemblyItems = ({ registerEvent }) => {
return (
<div className="h-100 border rounded px-1 mt-1 bg-light">
<div className='dropdownpanel'>
<div className='dropdowncontent pb-2'>
<div className="dropdownpanel">
<div className="dropdowncontent pb-2">
{assemblyItems.display.length == 0 && <div>No data available</div>}
<div className="pl-2 my-1 small instructions" data-id="asmitems" id='asmitems' ref={asmItemsRef}>
{
assemblyItems.display.map((item, i) => {
return <div className="px-1" key={i} ref={ref => { refs.current[i] = ref }}>
<span>{item}</span>{assemblyItems.currentLineIndexes.includes(i) ? <span><i><b> - LINE {assemblyItems.line + 1}</b></i></span> : ' - '}
<div
className="pl-2 my-1 small instructions"
data-id="asmitems"
id="asmitems"
ref={asmItemsRef}
>
{assemblyItems.display.map((item, i) => {
return (
<div
className="px-1"
key={i}
ref={(ref) => {
refs.current[i] = ref
}}
>
<span>{item}</span>
{assemblyItems.currentLineIndexes.includes(i) ? (
<span>
<i>
<b> - LINE {assemblyItems.line + 1}</b>
</i>
</span>
) : (
' - '
)}
</div>
})
}
)
})}
</div>
</div>
</div>

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

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

Loading…
Cancel
Save