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

pull/5370/head
Oleksii Kosynskyi 1 year ago
commit 5942374311
  1. 7
      .eslintrc.json
  2. 4
      .prettierrc.json
  3. 14
      apps/debugger/src/app/app.tsx
  4. 11
      apps/debugger/src/main.tsx
  5. 67
      apps/debugger/webpack.config.js
  6. 30
      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. 9
      apps/doc-viewer/src/app/App.tsx
  12. 4
      apps/doc-viewer/src/main.tsx
  13. 32
      apps/doc-viewer/webpack.config.js
  14. 20
      apps/etherscan/src/app/AppContext.tsx
  15. 87
      apps/etherscan/src/app/RemixPlugin.tsx
  16. 100
      apps/etherscan/src/app/app.tsx
  17. 63
      apps/etherscan/src/app/components/HeaderWithSettings.tsx
  18. 30
      apps/etherscan/src/app/components/SubmitButton.tsx
  19. 5
      apps/etherscan/src/app/hooks/useLocalStorage.tsx
  20. 10
      apps/etherscan/src/app/layouts/Default.tsx
  21. 18
      apps/etherscan/src/app/routes.tsx
  22. 107
      apps/etherscan/src/app/views/CaptureKeyView.tsx
  23. 16
      apps/etherscan/src/app/views/ErrorView.tsx
  24. 17
      apps/etherscan/src/app/views/HomeView.tsx
  25. 135
      apps/etherscan/src/app/views/ReceiptsView.tsx
  26. 316
      apps/etherscan/src/app/views/VerifyView.tsx
  27. 14
      apps/etherscan/src/main.tsx
  28. 64
      apps/etherscan/webpack.config.js
  29. 64
      apps/remix-ide-e2e/nightwatch.ts
  30. 104
      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. 4
      apps/remix-ide/background.js
  33. 28
      apps/remix-ide/src/app/components/hidden-panel.tsx
  34. 34
      apps/remix-ide/src/app/components/main-panel.tsx
  35. 200
      apps/remix-ide/src/app/components/preload.tsx
  36. 19
      apps/remix-ide/src/app/components/side-panel.tsx
  37. 72
      apps/remix-ide/src/app/components/vertical-icons.tsx
  38. 32
      apps/remix-ide/src/app/plugins/contractFlattener.tsx
  39. 24
      apps/remix-ide/src/app/plugins/notification.tsx
  40. 384
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  41. 32
      apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx
  42. 128
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  43. 39
      apps/remix-ide/src/app/plugins/solidity-script.tsx
  44. 155
      apps/remix-ide/src/app/plugins/solidity-umlgen.tsx
  45. 56
      apps/remix-ide/src/app/providers/abstract-provider.tsx
  46. 90
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  47. 31
      apps/remix-ide/src/app/providers/external-http-provider.tsx
  48. 25
      apps/remix-ide/src/app/providers/foundry-provider.tsx
  49. 25
      apps/remix-ide/src/app/providers/ganache-provider.tsx
  50. 31
      apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx
  51. 21
      apps/remix-ide/src/app/providers/hardhat-provider.tsx
  52. 31
      apps/remix-ide/src/app/providers/injected-L2-provider.tsx
  53. 7
      apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx
  54. 7
      apps/remix-ide/src/app/providers/injected-optimism-provider.tsx
  55. 16
      apps/remix-ide/src/app/providers/injected-provider-default.tsx
  56. 10
      apps/remix-ide/src/app/providers/injected-provider-trustwallet.tsx
  57. 52
      apps/remix-ide/src/app/providers/injected-provider.tsx
  58. 31
      apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx
  59. 31
      apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx
  60. 114
      apps/remix-ide/src/app/providers/vm-provider.tsx
  61. 12
      apps/remix-ide/src/app/tabs/search.tsx
  62. 46
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  63. 573
      apps/remix-ide/src/blockchain/blockchain.tsx
  64. 19
      apps/remix-ide/src/index.tsx
  65. 92
      apps/remix-ide/webpack.config.js
  66. 8
      apps/solhint/src/app/App.tsx
  67. 38
      apps/solhint/webpack.config.js
  68. 4
      apps/solidity-compiler/src/app/app.tsx
  69. 68
      apps/solidity-compiler/webpack.config.js
  70. 45
      apps/vyper/src/app/app.tsx
  71. 34
      apps/vyper/src/app/components/CompilerButton.tsx
  72. 17
      apps/vyper/src/app/components/LocalUrl.tsx
  73. 68
      apps/vyper/src/app/components/VyperResult.tsx
  74. 5
      apps/vyper/src/app/components/WarnRemote.tsx
  75. 39
      apps/vyper/src/app/utils/compiler.tsx
  76. 39
      apps/vyper/src/app/utils/remix-client.tsx
  77. 14
      apps/vyper/src/main.tsx
  78. 67
      apps/vyper/webpack.config.js
  79. 18
      apps/walletconnect/src/app/app.tsx
  80. 29
      apps/walletconnect/src/app/walletConnectUI.tsx
  81. 5
      apps/walletconnect/src/main.tsx
  82. 66
      apps/walletconnect/webpack.config.js
  83. 14
      libs/remix-debug/test.ts
  84. 25
      libs/remix-ui/app/src/lib/remix-app/components/dragbar/dragbar.tsx
  85. 10
      libs/remix-ui/app/src/lib/remix-app/components/modals/dialogViewPlugin.tsx
  86. 11
      libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx
  87. 58
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  88. 58
      libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx
  89. 28
      libs/remix-ui/app/src/lib/remix-app/components/modals/origin-warning.tsx
  90. 23
      libs/remix-ui/app/src/lib/remix-app/components/splashscreen.tsx
  91. 8
      libs/remix-ui/app/src/lib/remix-app/context/context.tsx
  92. 69
      libs/remix-ui/app/src/lib/remix-app/context/provider.tsx
  93. 54
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  94. 69
      libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx
  95. 41
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  96. 12
      libs/remix-ui/debugger-ui/src/hooks/extract-data.tsx
  97. 267
      libs/remix-ui/debugger-ui/src/lib/button-navigator/button-navigator.tsx
  98. 213
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  99. 15
      libs/remix-ui/debugger-ui/src/lib/slider/slider.tsx
  100. 28
      libs/remix-ui/debugger-ui/src/lib/step-manager/step-manager.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]
}
},
@ -67,4 +72,4 @@
"globals": {
"JSX": true
}
}
}

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

@ -1,17 +1,17 @@
import React, { useState, useEffect } from 'react';
import React, {useState, useEffect} from 'react'
import { DebuggerUI } from '@remix-ui/debugger-ui' // eslint-disable-line
import {DebuggerUI} from '@remix-ui/debugger-ui' // eslint-disable-line
import { DebuggerClientApi } from './debugger'
import {DebuggerClientApi} from './debugger'
const remix = new DebuggerClientApi()
export const App = () => {
export const App = () => {
return (
<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 {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
})

@ -4,13 +4,13 @@ import './App.css'
import { DocGenClient } from './docgen-client'
import { Build } from './docgen/site'
export const client = new DocGenClient()
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">
<h6>File: {fileName}</h6>
</div>}
{hasBuild && <button className="btn btn-primary btn-block mt-4" onClick={() => client.generateDocs()}>Generate Docs</button>}
<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>
)
}

@ -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,13 +11,12 @@ export default function App() {
client.eventEmitter.on('contentsReady', (fileContents: string) => {
setContents(fileContents)
})
}, [])
return (
<>
<div className="m-5 p-2">
<ReactMarkdown children={contents} remarkPlugins={[remarkGfm]}/>
<ReactMarkdown children={contents} remarkPlugins={[remarkGfm]} />
</div>
</>
)
}
}

@ -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 {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,40 +1,59 @@
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()
}
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) => {})
return result
}
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) {
try {
const {network, networkId} = await getNetworkName(this)
if (network === 'vm') {
throw new Error('Cannot check the receipt status in the selected network')
}
const etherscanApi = getEtherScanApi(networkId)
let receiptStatus
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")
}
const etherscanApi = getEtherScanApi(networkId)
let receiptStatus
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
}
} catch (e: any){
return {
status: 'error',
message: e.message,
succeed: false
}
}
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
}
} catch (e: any) {
return {
status: 'error',
message: e.message,
succeed: false
}
}
}
}

@ -1,22 +1,19 @@
import React, { useState, useEffect, useRef } from "react"
import React, {useState, useEffect, useRef} from 'react'
import {
CompilationFileSources,
CompilationResult,
} from "@remixproject/plugin-api"
import {CompilationFileSources, 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 +28,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,26 +46,15 @@ const App = () => {
const loadClient = async () => {
await client.onload()
setClientInstance(client)
client.on("solidity",
"compilationFinished",
(
fileName: string,
source: CompilationFileSources,
languageVersion: string,
data: CompilationResult
) => {
const newContractsNames = getNewContractNames(data)
const newContractsToSave: string[] = [
...contractsRef.current,
...newContractsNames,
]
const uniqueContracts: string[] = [...new Set(newContractsToSave)]
setContracts(uniqueContracts)
}
)
client.on('solidity', 'compilationFinished', (fileName: string, source: CompilationFileSources, languageVersion: string, data: CompilationResult) => {
const newContractsNames = getNewContractNames(data)
const newContractsToSave: string[] = [...contractsRef.current, ...newContractsNames]
const uniqueContracts: string[] = [...new Set(newContractsToSave)]
setContracts(uniqueContracts)
})
//const currentTheme = await client.call("theme", "currentTheme")
//setThemeType(currentTheme.quality)
@ -82,7 +68,7 @@ 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,51 +77,41 @@ 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.
for (const item of receiptsNotVerified) {
await new Promise((r) => setTimeout(r, 500)) // avoid api rate limit exceed.
let status
if (item.isProxyContract) {
status = await getProxyContractReceiptStatus(
item.guid,
apiKey,
getEtherScanApi(networkId)
)
status = await getProxyContractReceiptStatus(item.guid, apiKey, getEtherScanApi(networkId))
if (status.status === '1') {
status.message = status.result
status.result = 'Successfully Updated'
}
} else
status = await getReceiptStatus(
item.guid,
apiKey,
getEtherScanApi(networkId)
)
if (status.result === "Pass - Verified" || status.result === "Already Verified" ||
status.result === "Successfully Updated") {
} else status = await getReceiptStatus(item.guid, apiKey, getEtherScanApi(networkId))
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
}
return currentReceipt
})
}
})
}
}
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 +133,7 @@ const App = () => {
contracts,
setContracts,
themeType,
setThemeType,
setThemeType
}}
>
<DisplayRoutes />
@ -165,4 +141,4 @@ const App = () => {
)
}
export default App
export default App

@ -1,8 +1,8 @@
import React from "react"
import React from 'react'
import { NavLink } from "react-router-dom"
import { CustomTooltip } from '@remix-ui/helper'
import { AppContext } from "../AppContext"
import {NavLink} from 'react-router-dom'
import {CustomTooltip} from '@remix-ui/helper'
import {AppContext} from '../AppContext'
interface Props {
title?: string
@ -13,76 +13,61 @@ interface IconProps {
from: string
}
const HomeIcon: React.FC<IconProps> = ({ from }: IconProps) => {
const HomeIcon: React.FC<IconProps> = ({from}: IconProps) => {
return (
<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"}}
state={ from }
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'
>
<CustomTooltip tooltipText="Home" tooltipId="etherscan-nav-home" placement="bottom">
<i className="fas fa-home"></i>
</CustomTooltip>
</NavLink>
)
}
const ReceiptsIcon: React.FC<IconProps> = ({ from }: IconProps) => {
const ReceiptsIcon: React.FC<IconProps> = ({from}: IconProps) => {
return (
<NavLink
<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"}}
state={ from }
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'
>
<CustomTooltip tooltipText="Receipts" tooltipId="etherscan-nav-receipts" placement="bottom">
<i className="fas fa-receipt"></i>
</CustomTooltip>
</NavLink>
)
}
const SettingsIcon: React.FC<IconProps> = ({ from }: IconProps) => {
const SettingsIcon: React.FC<IconProps> = ({from}: IconProps) => {
return (
<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"}}
state= {from}
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'
>
<CustomTooltip tooltipText="Settings" tooltipId="etherscan-nav-settings" placement="bottom">
<i className="fas fa-cog"></i>
</CustomTooltip>
</NavLink>
)
}
export const HeaderWithSettings: React.FC<Props> = ({
title = "",
from,
}) => {
export const HeaderWithSettings: React.FC<Props> = ({title = '', from}) => {
return (
<AppContext.Consumer>
{() => (

@ -1,5 +1,5 @@
import React from "react"
import { CustomTooltip } from '@remix-ui/helper'
import React from 'react'
import {CustomTooltip} from '@remix-ui/helper'
interface Props {
text: string
@ -8,35 +8,21 @@ interface Props {
disable?: boolean
}
export const SubmitButton: React.FC<Props> = ({
text,
dataId,
isSubmitting = false,
disable = true
}) => {
export const SubmitButton: React.FC<Props> = ({text, dataId, isSubmitting = false, disable = true}) => {
return (
<div>
<button
data-id={dataId}
type="submit"
className="btn btn-primary btn-block p-1 text-decoration-none"
disabled={disable}
>
<button data-id={dataId} type="submit" className="btn btn-primary btn-block p-1 text-decoration-none" disabled={disable}>
<CustomTooltip
tooltipText={disable ? "Fill in the valid value(s) and select a supported network" : "Click to proceed"}
tooltipId={'etherscan-submit-button-'+ dataId}
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}
{isSubmitting && (
<div>
<span
className="spinner-border spinner-border-sm mr-1"
role="status"
aria-hidden="true"
/>
<span className="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true" />
Verifying... Please wait
</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
@ -21,8 +21,7 @@ export function useLocalStorage(key: string, initialValue: any) {
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)
// Save to local storage

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

@ -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
@ -16,7 +11,7 @@ interface Props extends RouteProps {
export const DisplayRoutes = () => (
<Router>
<Routes>
<Routes>
<Route
path="/"
element={
@ -25,8 +20,7 @@ export const DisplayRoutes = () => (
</DefaultLayout>
}
/>
<Route path="/error"
element={<ErrorView />} />
<Route path="/error" element={<ErrorView />} />
<Route
path="/receipts"
element={

@ -1,75 +1,62 @@
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 }) => {
{({apiKey, clientInstance, setAPIKey}) => {
if (!apiKey) setMsg('Please provide a 34-character API key to continue')
return (
<div>
<Formik
initialValues={{ apiKey }}
validate={(values) => {
const errors = {} as any
if (!values.apiKey) {
errors.apiKey = "Required"
} else if (values.apiKey.length !== 34) {
errors.apiKey = "API key should be 34 characters long"
}
return errors
}}
onSubmit={(values) => {
const apiKey = values.apiKey
if (apiKey.length === 34) {
setAPIKey(values.apiKey)
navigate((location && location.state ? location.state : '/'))
}
}}
>
{({ errors, touched, handleSubmit }) => (
<form onSubmit={handleSubmit}>
<div className="form-group mb-2">
<label htmlFor="apikey">API Key</label>
<Field
className={
errors.apiKey && touched.apiKey
? "form-control form-control-sm is-invalid"
: "form-control form-control-sm"
}
type="password"
name="apiKey"
placeholder="e.g. GM1T20XY6JGSAPWKDCYZ7B2FJXKTJRFVGZ"
/>
<ErrorMessage
className="invalid-feedback"
name="apiKey"
component="div"
/>
</div>
<div>
<Formik
initialValues={{apiKey}}
validate={(values) => {
const errors = {} as any
if (!values.apiKey) {
errors.apiKey = 'Required'
} else if (values.apiKey.length !== 34) {
errors.apiKey = 'API key should be 34 characters long'
}
return errors
}}
onSubmit={(values) => {
const apiKey = values.apiKey
if (apiKey.length === 34) {
setAPIKey(values.apiKey)
navigate(location && location.state ? location.state : '/')
}
}}
>
{({errors, touched, handleSubmit}) => (
<form onSubmit={handleSubmit}>
<div className="form-group mb-2">
<label htmlFor="apikey">API Key</label>
<Field
className={errors.apiKey && touched.apiKey ? 'form-control form-control-sm is-invalid' : 'form-control form-control-sm'}
type="password"
name="apiKey"
placeholder="e.g. GM1T20XY6JGSAPWKDCYZ7B2FJXKTJRFVGZ"
/>
<ErrorMessage className="invalid-feedback" name="apiKey" component="div" />
</div>
<div>
<SubmitButton text="Save" dataId="save-api-key" disable={errors && errors.apiKey ? true : false } />
</div>
</form>
)}
</Formik>
<div>
<SubmitButton text="Save" dataId="save-api-key" disable={errors && errors.apiKey ? true : false} />
</div>
</form>
)}
</Formik>
<div
data-id="api-key-result"
className="text-primary mt-4 text-center"
style={{fontSize: "0.8em"}}
dangerouslySetInnerHTML={{ __html: msg }}
/>
</div>
<div data-id="api-key-result" className="text-primary mt-4 text-center" style={{fontSize: '0.8em'}} dangerouslySetInnerHTML={{__html: msg}} />
</div>
)
}}
</AppContext.Consumer>

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

@ -1,20 +1,20 @@
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 (
<AppContext.Consumer>
{({ apiKey, clientInstance, setReceipts, receipts, contracts }) => {
{({apiKey, clientInstance, setReceipts, receipts, contracts}) => {
return !apiKey ? (
<Navigate
to={{
pathname: "/settings"
pathname: '/settings'
}}
/>
) : (
@ -28,8 +28,7 @@ export const HomeView: React.FC = () => {
}}
/>
)
}
}
}}
</AppContext.Consumer>
)
}

@ -1,13 +1,13 @@
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 { CustomTooltip } from '@remix-ui/helper'
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 {
receiptGuid: string
@ -17,38 +17,25 @@ export const ReceiptsView: React.FC = () => {
const [results, setResults] = useState({succeed: false, message: ''})
const [isProxyContractReceipt, setIsProxyContractReceipt] = useState(false)
const onGetReceiptStatus = async (
values: FormValues,
clientInstance: any,
apiKey: string
) => {
const onGetReceiptStatus = async (values: FormValues, clientInstance: any, apiKey: string) => {
try {
const { network, networkId } = await getNetworkName(clientInstance)
if (network === "vm") {
const {network, networkId} = await getNetworkName(clientInstance)
if (network === 'vm') {
setResults({
succeed: false,
message: "Cannot verify in the selected network"
message: 'Cannot verify in the selected network'
})
return
}
const etherscanApi = getEtherScanApi(networkId)
let result
if (isProxyContractReceipt) {
result = await getProxyContractReceiptStatus(
values.receiptGuid,
apiKey,
etherscanApi
)
result = await getProxyContractReceiptStatus(values.receiptGuid, apiKey, etherscanApi)
if (result.status === '1') {
result.message = result.result
result.result = 'Successfully Updated'
}
} else
result = await getReceiptStatus(
values.receiptGuid,
apiKey,
etherscanApi
)
} else result = await getReceiptStatus(values.receiptGuid, apiKey, etherscanApi)
setResults({
succeed: result.status === '1' ? true : false,
message: result.result || (result.status === '0' ? 'Verification failed' : result.message)
@ -63,46 +50,36 @@ export const ReceiptsView: React.FC = () => {
return (
<AppContext.Consumer>
{({ apiKey, clientInstance, receipts, setReceipts }) => {
{({apiKey, clientInstance, receipts, setReceipts}) => {
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
}}
onSubmit={(values) =>
onGetReceiptStatus(values, clientInstance, apiKey)
}
onSubmit={(values) => onGetReceiptStatus(values, clientInstance, apiKey)}
>
{({ errors, touched, handleSubmit, handleChange }) => (
{({errors, touched, handleSubmit, handleChange}) => (
<form onSubmit={handleSubmit}>
<div className="form-group mb-2">
<label htmlFor="receiptGuid">Receipt GUID</label>
<Field
className={
errors.receiptGuid && touched.receiptGuid
? "form-control form-control-sm is-invalid"
: "form-control form-control-sm"
}
className={errors.receiptGuid && touched.receiptGuid ? 'form-control form-control-sm is-invalid' : 'form-control form-control-sm'}
type="text"
name="receiptGuid"
/>
<ErrorMessage
className="invalid-feedback"
name="receiptGuid"
component="div"
/>
<ErrorMessage className="invalid-feedback" name="receiptGuid" component="div" />
</div>
<div className="d-flex mb-2 custom-control custom-checkbox">
@ -115,37 +92,44 @@ export const ReceiptsView: React.FC = () => {
handleChange(e)
if (e.target.checked) setIsProxyContractReceipt(true)
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/>
<CustomTooltip
tooltipText="Clear the list of receipts"
tooltipId='etherscan-clear-receipts'
placement='bottom'
>
<Button className="btn-sm" onClick={() => { setReceipts([]) }} >Clear</Button>
<ReceiptsTable receipts={receipts} />
<br />
<CustomTooltip tooltipText="Clear the list of receipts" tooltipId="etherscan-clear-receipts" placement="bottom">
<Button
className="btn-sm"
onClick={() => {
setReceipts([])
}}
>
Clear
</Button>
</CustomTooltip>
</div>
)
}
}
}}
</AppContext.Consumer>
)
}
const ReceiptsTable: React.FC<{ receipts: Receipt[] }> = ({ receipts }) => {
const ReceiptsTable: React.FC<{receipts: Receipt[]}> = ({receipts}) => {
return (
<div className="table-responsive">
<h6>Receipts</h6>
@ -162,20 +146,23 @@ 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'))}>
{item.status}
{item.status === 'Successfully Updated' && <CustomTooltip
placement={'bottom'}
tooltipClasses="text-wrap"
tooltipId="etherscan-receipt-proxy-status"
tooltipText={item.message}
>
<i style={{ fontSize: 'small' }} className={'ml-1 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
</CustomTooltip>
<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 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>
)}
</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 { CustomTooltip } from '@remix-ui/helper'
import { Formik, ErrorMessage, Field } from "formik"
import {PluginClient} from '@remixproject/plugin'
import {CustomTooltip} from '@remix-ui/helper'
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
@ -25,15 +23,10 @@ interface FormValues {
expectedImplAddress?: string
}
export const VerifyView: React.FC<Props> = ({
apiKey,
client,
contracts,
onVerifiedContract,
}) => {
const [results, setResults] = useState("")
const [networkName, setNetworkName] = useState("Loading...")
const [selectedContract, setSelectedContract] = useState("")
export const VerifyView: React.FC<Props> = ({apiKey, client, contracts, onVerifiedContract}) => {
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 +34,13 @@ export const VerifyView: React.FC<Props> = ({
useEffect(() => {
if (client && client.on) {
client.on("blockchain" as any, 'networkStatus', (result) => {
client.on('blockchain' as any, 'networkStatus', (result) => {
setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`)
})
}
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,8 +49,8 @@ export const VerifyView: React.FC<Props> = ({
}, [contracts])
const updateConsFields = (contractName) => {
client.call("compilerArtefacts" as any, "getArtefactsByContractName", contractName).then((result) => {
const { artefact } = 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) {
setConstructorInputs(artefact.abi[0].inputs)
setShowConstructorArgs(true)
@ -69,13 +62,10 @@ export const VerifyView: React.FC<Props> = ({
}
const onVerifyContract = async (values: FormValues) => {
const compilationResult = (await client.call(
"solidity",
"getCompilationResult"
)) as any
const compilationResult = (await client.call('solidity', 'getCompilationResult')) as any
if (!compilationResult) {
throw new Error("no compilation result available")
throw new Error('no compilation result available')
}
const constructorValues = []
@ -83,9 +73,9 @@ export const VerifyView: React.FC<Props> = ({
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 +88,7 @@ export const VerifyView: React.FC<Props> = ({
values.expectedImplAddress,
client,
onVerifiedContract,
setResults,
setResults
)
setResults(verificationResult.current['message'])
}
@ -107,189 +97,147 @@ 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}>
<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'
>
{({errors, touched, handleSubmit, handleChange, isSubmitting}) => {
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"
>
<Field className="form-control" type="text" name="network" value={networkName} disabled={true} />
</CustomTooltip>
</div>
<div className="form-group">
<label htmlFor="contractName">Contract Name</label>
<Field
className="form-control"
type="text"
name="network"
value={networkName}
disabled={true}
/>
</CustomTooltip>
</div>
<div className="form-group">
<label htmlFor="contractName">Contract Name</label>
<Field
as="select"
className={
errors.contractName && touched.contractName && contracts.length
? "form-control is-invalid"
: "form-control"
}
name="contractName"
onChange={async (e) => {
handleChange(e)
setSelectedContract(e.target.value)
updateConsFields(e.target.value)
}}
>
<option disabled={true} value="">
{ contracts.length ? 'Select a contract' : `--- No compiled contracts ---` }
</option>
{contracts.map((item) => (
<option key={item} value={item}>
{item}
as="select"
className={errors.contractName && touched.contractName && contracts.length ? 'form-control is-invalid' : 'form-control'}
name="contractName"
onChange={async (e) => {
handleChange(e)
setSelectedContract(e.target.value)
updateConsFields(e.target.value)
}}
>
<option disabled={true} value="">
{contracts.length ? 'Select a contract' : `--- No compiled contracts ---`}
</option>
))}
</Field>
<ErrorMessage
className="invalid-feedback"
name="contractName"
component="div"
/>
</div>
<div className={ showConstructorArgs ? 'form-group d-block': 'form-group d-none' } >
<label>Constructor Arguments</label>
{contracts.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</Field>
<ErrorMessage className="invalid-feedback" name="contractName" component="div" />
</div>
<div className={showConstructorArgs ? 'form-group d-block' : 'form-group d-none'}>
<label>Constructor Arguments</label>
{constructorInputs.map((item, index) => {
return (
<div className="d-flex">
<Field
className="form-control m-1"
type="text"
key={`contractArgName${index}`}
name={`contractArgName${index}`}
value={item.name}
disabled={true}
/>
<CustomTooltip
tooltipText={`value of ${item.name}`}
tooltipId={`etherscan-constructor-value${index}`}
placement='top'
>
<Field
className="form-control m-1"
type="text"
key={`contractArgValue${index}`}
name={`contractArgValue${index}`}
placeholder={item.type}
/>
<Field className="form-control m-1" type="text" key={`contractArgName${index}`} name={`contractArgName${index}`} value={item.name} disabled={true} />
<CustomTooltip tooltipText={`value of ${item.name}`} tooltipId={`etherscan-constructor-value${index}`} placement="top">
<Field className="form-control m-1" type="text" key={`contractArgValue${index}`} name={`contractArgValue${index}`} placeholder={item.type} />
</CustomTooltip>
</div>
)}
)}
</div>
<div className="form-group">
<label htmlFor="contractAddress">Contract Address</label>
<Field
className={
errors.contractAddress && touched.contractAddress
? "form-control is-invalid"
: "form-control"
}
type="text"
name="contractAddress"
placeholder="e.g. 0x11b79afc03baf25c631dd70169bb6a3160b2706e"
/>
<ErrorMessage
className="invalid-feedback"
name="contractAddress"
component="div"
/>
<div className="d-flex mb-2 custom-control custom-checkbox">
<Field
className="custom-control-input"
type="checkbox"
name="isProxy"
id="isProxy"
onChange={async (e) => {
handleChange(e)
if (e.target.checked) setIsProxyContract(true)
else setIsProxyContract(false)
}}
/>
<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>
<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'
>
<div className="form-group">
<label htmlFor="contractAddress">Contract Address</label>
<Field
className="form-control"
className={errors.contractAddress && touched.contractAddress ? 'form-control is-invalid' : 'form-control'}
type="text"
name="expectedImplAddress"
placeholder="verified implementation contract address"
name="contractAddress"
placeholder="e.g. 0x11b79afc03baf25c631dd70169bb6a3160b2706e"
/>
</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>
</div>
<SubmitButton dataId="verify-contract" text="Verify"
isSubmitting={isSubmitting}
disable={ !contracts.length ||
!touched.contractName ||
!touched.contractAddress ||
(touched.contractName && errors.contractName) ||
(touched.contractAddress && errors.contractAddress) ||
(networkName === 'VM (Not supported)')
? true
: false}
/>
<br/>
<CustomTooltip
tooltipText='Generate the required TS scripts to verify a contract on Etherscan'
tooltipId='etherscan-generate-scripts'
placement='bottom'
>
<button
type="button"
className="mr-2 mb-2 py-1 px-2 btn btn-secondary btn-block"
onClick={async () => {
etherscanScripts(client)
}}
<ErrorMessage className="invalid-feedback" name="contractAddress" component="div" />
<div className="d-flex mb-2 custom-control custom-checkbox">
<Field
className="custom-control-input"
type="checkbox"
name="isProxy"
id="isProxy"
onChange={async (e) => {
handleChange(e)
if (e.target.checked) setIsProxyContract(true)
else setIsProxyContract(false)
}}
/>
<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>
<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"
>
<Field className="form-control" type="text" name="expectedImplAddress" 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>
</div>
<SubmitButton
dataId="verify-contract"
text="Verify"
isSubmitting={isSubmitting}
disable={
!contracts.length ||
!touched.contractName ||
!touched.contractAddress ||
(touched.contractName && errors.contractName) ||
(touched.contractAddress && errors.contractAddress) ||
networkName === 'VM (Not supported)'
? true
: false
}
/>
<br />
<CustomTooltip tooltipText="Generate the required TS scripts to verify a contract on Etherscan" tooltipId="etherscan-generate-scripts" placement="bottom">
<button
type="button"
className="mr-2 mb-2 py-1 px-2 btn btn-secondary btn-block"
onClick={async () => {
etherscanScripts(client)
}}
>
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"}}
dangerouslySetInnerHTML={{ __html: results }}
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" }}>
<Link to="/receipts">View Receipts</Link>

@ -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 {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: {
@ -23,28 +23,29 @@ module.exports = {
exclude: ['dist/apps/remix-ide-e2e/src/tests/runAndDeploy.test.js', 'dist/apps/remix-ide-e2e/src/tests/pluginManager.test.ts']
},
chrome: {
'chrome': {
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',
"--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",
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'
]
}
}
},
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 +54,33 @@ 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']
}
}
},
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,32 +1,43 @@
import React, { useEffect, useState } from 'react'
import { RemixPlugin } from './Client'
import { Logger } from './logger'
import { filePanelProfile } from '@remixproject/plugin-api'
import { filSystemProfile } from '@remixproject/plugin-api'
import { dGitProfile } from '@remixproject/plugin-api'
import { editorProfile } from '@remixproject/plugin-api'
import { settingsProfile } from '@remixproject/plugin-api'
import { networkProfile } from '@remixproject/plugin-api'
import { udappProfile } from '@remixproject/plugin-api'
import { compilerProfile } from '@remixproject/plugin-api'
import { contentImportProfile } from '@remixproject/plugin-api'
import { windowProfile } from '@remixproject/plugin-api'
import { pluginManagerProfile } from '@remixproject/plugin-api'
import { Profile } from '@remixproject/plugin-utils'
import React, {useEffect, useState} from 'react'
import {RemixPlugin} from './Client'
import {Logger} from './logger'
import {filePanelProfile} from '@remixproject/plugin-api'
import {filSystemProfile} from '@remixproject/plugin-api'
import {dGitProfile} from '@remixproject/plugin-api'
import {editorProfile} from '@remixproject/plugin-api'
import {settingsProfile} from '@remixproject/plugin-api'
import {networkProfile} from '@remixproject/plugin-api'
import {udappProfile} from '@remixproject/plugin-api'
import {compilerProfile} from '@remixproject/plugin-api'
import {contentImportProfile} from '@remixproject/plugin-api'
import {windowProfile} from '@remixproject/plugin-api'
import {pluginManagerProfile} from '@remixproject/plugin-api'
import {Profile} from '@remixproject/plugin-utils'
import './app.css'
const client = new RemixPlugin()
function App () {
function App() {
const [payload, setPayload] = useState<string>('')
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) => {
const handleChange = ({target}: any) => {
setPayload(target.value)
}
@ -44,7 +55,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,8 +88,10 @@ function App () {
let ob: any = null
try {
ob = JSON.parse(payload)
if (ob && !Array.isArray(ob)) { ob = [ob] }
} catch (e) { }
if (ob && !Array.isArray(ob)) {
ob = [ob]
}
} catch (e) {}
const args = ob || [payload]
setStarted(true)
setLog('')
@ -97,30 +110,41 @@ 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>
<input
className='form-control w-100'
type="text"
id="payload"
placeholder="Enter payload here..."
value={payload}
onChange={handleChange}
data-id="payload-input"
/>
<Logger id="events" log={events}></Logger>
<input className="form-control w-100" type="text" id="payload" placeholder="Enter payload here..." value={payload} onChange={handleChange} data-id="payload-input" />
{profiles.map((profile: Profile) => {
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>
)
}

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

@ -1,9 +1,9 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { AbstractPanel } from './panel'
import {AbstractPanel} from './panel'
import * as packageJson from '../../../../../package.json'
import { RemixPluginPanel } from '@remix-ui/panel'
import { PluginViewWrapper } from '@remix-ui/helper'
import {RemixPluginPanel} from '@remix-ui/panel'
import {PluginViewWrapper} from '@remix-ui/helper'
const profile = {
name: 'hiddenPanel',
@ -16,35 +16,37 @@ const profile = {
export class HiddenPanel extends AbstractPanel {
el: HTMLElement
dispatch: React.Dispatch<any> = () => {}
constructor () {
constructor() {
super(profile)
this.el = document.createElement('div')
this.el.setAttribute('class', 'pluginsContainer')
}
addView (profile: any, view: any): void {
addView(profile: any, view: any): void {
super.removeView(profile)
super.addView(profile, view)
this.renderComponent()
}
updateComponent (state: any) {
return <RemixPluginPanel header={<></>} plugins={state.plugins}/>
updateComponent(state: any) {
return <RemixPluginPanel header={<></>} plugins={state.plugins} />
}
setDispatch (dispatch: React.Dispatch<any>) {
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
render() {
render() {
return (
<div className='pluginsContainer'><PluginViewWrapper plugin={this} /></div>
);
<div className="pluginsContainer">
<PluginViewWrapper plugin={this} />
</div>
)
}
renderComponent () {
renderComponent() {
this.dispatch({
plugins: this.plugins,
plugins: this.plugins
})
}
}

@ -1,8 +1,8 @@
import React from 'react' // eslint-disable-line
import { AbstractPanel } from './panel'
import { RemixPluginPanel } from '@remix-ui/panel'
import {AbstractPanel} from './panel'
import {RemixPluginPanel} from '@remix-ui/panel'
import packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
import {PluginViewWrapper} from '@remix-ui/helper'
const profile = {
name: 'mainPanel',
@ -15,7 +15,7 @@ const profile = {
export class MainPanel extends AbstractPanel {
element: HTMLDivElement
dispatch: React.Dispatch<any> = () => {}
constructor (config) {
constructor(config) {
super(profile)
this.element = document.createElement('div')
this.element.setAttribute('data-id', 'mainPanelPluginsContainer')
@ -23,46 +23,50 @@ export class MainPanel extends AbstractPanel {
// this.config = config
}
setDispatch (dispatch: React.Dispatch<any>) {
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
onActivation () {
onActivation() {
this.renderComponent()
}
focus (name) {
focus(name) {
this.emit('focusChanged', name)
super.focus(name)
this.renderComponent()
}
addView (profile, view) {
addView(profile, view) {
super.addView(profile, view)
this.renderComponent()
}
removeView (profile) {
removeView(profile) {
super.removeView(profile)
this.renderComponent()
}
async showContent (name) {
async showContent(name) {
super.showContent(name)
this.renderComponent()
}
renderComponent () {
renderComponent() {
this.dispatch({
plugins: this.plugins
})
}
render() {
return <div style={{height: '100%', width: '100%'}} data-id='mainPanelPluginsContainer'><PluginViewWrapper plugin={this} /></div>
render() {
return (
<div style={{height: '100%', width: '100%'}} data-id="mainPanelPluginsContainer">
<PluginViewWrapper plugin={this} />
</div>
)
}
updateComponent (state: any) {
return <RemixPluginPanel header={<></>} plugins={state.plugins}/>
updateComponent(state: any) {
return <RemixPluginPanel header={<></>} plugins={state.plugins} />
}
}

@ -1,16 +1,15 @@
import { RemixApp } from '@remix-ui/app'
import React, { useEffect, useRef, useState } from 'react'
import { render } from 'react-dom'
import {RemixApp} from '@remix-ui/app'
import React, {useEffect, useRef, useState} from 'react'
import {render} from 'react-dom'
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 {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 './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,26 +17,34 @@ 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) => {
const appComponent = new AppComponent.default()
appComponent.run().then(() => {
render(
<>
<RemixApp app={appComponent} />
</>,
document.getElementById('root')
)
import('../../app')
.then((AppComponent) => {
const appComponent = new AppComponent.default()
appComponent.run().then(() => {
render(
<>
<RemixApp app={appComponent} />
</>,
document.getElementById('root')
)
})
})
.catch((err) => {
_paq.push(['trackEvent', 'Preload', 'error', err && err.message])
console.error('Error loading Remix:', err)
setError(true)
})
}).catch(err => {
_paq.push(['trackEvent', 'Preload', 'error', err && err.message])
console.error('Error loading Remix:', err)
setError(true)
})
}
const downloadBackup = async () => {
@ -51,12 +58,15 @@ export const Preload = () => {
setShowDownloader(false)
const fsUtility = new fileSystemUtility()
const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current)
_paq.push(['trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail'])
_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 setFileSystems = async () => {
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])
@ -67,7 +77,7 @@ export const Preload = () => {
}
}
const testmigration = async() => {
const testmigration = async () => {
if (testmigrationResult.current) {
const fsUtility = new fileSystemUtility()
fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs)
@ -76,69 +86,89 @@ export const Preload = () => {
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'>
{logo}
<div className="info-secondary splash">
REMIX IDE
<br />
<span className='version'> v{packageJson.version}</span>
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>
</div>
</div>
</div>
{!supported ?
<div className='preload-info-container alert alert-warning'>
Your browser does not support any of the filesystems required by Remix.
Either change the settings in your browser or use a supported browser.
</div> : 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>
{!supported ? (
<div className="preload-info-container alert alert-warning">
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser.
</div>
<div className='pt-2'>
MacOS:<br></br>
- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<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>
<div className="pt-2">
MacOS:<br></br>- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
</div>
<div className="pt-2">
Linux:<br></br>- Chrome & FireFox: CTRL + SHIFT + R<br></br>
</div>
</div>
<div className='pt-2'>
Linux:<br></br>
- Chrome & FireFox: CTRL + SHIFT + R<br></br>
) : 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>
</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.
<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) ?
<div>
<i className="fas fa-spinner fa-spin fa-2x"></i>
</div> : null}
</div>
</>
) : null}
{supported && !error && !showDownloader ? (
<div>
<i className="fas fa-spinner fa-spin fa-2x"></i>
</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">
<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>
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>
)

@ -1,10 +1,10 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { AbstractPanel } from './panel'
import { RemixPluginPanel } from '@remix-ui/panel'
import {AbstractPanel} from './panel'
import {RemixPluginPanel} from '@remix-ui/panel'
import packageJson from '../../../../../package.json'
import { RemixUIPanelHeader } from '@remix-ui/panel'
import { PluginViewWrapper } from '@remix-ui/helper'
import {RemixUIPanelHeader} from '@remix-ui/panel'
import {PluginViewWrapper} from '@remix-ui/helper'
// const csjs = require('csjs-inject')
const sidePanel = {
@ -79,14 +79,17 @@ export class SidePanel extends AbstractPanel {
this.renderComponent()
}
setDispatch (dispatch: React.Dispatch<any>) {
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
render() {
render() {
return (
<section className='panel plugin-manager'> <PluginViewWrapper plugin={this} /></section>
);
<section className="panel plugin-manager">
{' '}
<PluginViewWrapper plugin={this} />
</section>
)
}
updateComponent(state: any) {

@ -1,11 +1,11 @@
// eslint-disable-next-line no-use-before-define
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 { Profile } from '@remixproject/plugin-utils'
import { PluginViewWrapper } from '@remix-ui/helper'
import {Plugin} from '@remixproject/engine'
import {EventEmitter} from 'events'
import {IconRecord, RemixUiVerticalIconsPanel} from '@remix-ui/vertical-icons-panel'
import {Profile} from '@remixproject/plugin-utils'
import {PluginViewWrapper} from '@remix-ui/helper'
const profile = {
name: 'menuicons',
@ -21,44 +21,51 @@ export class VerticalIcons extends Plugin {
htmlElement: HTMLDivElement
icons: Record<string, IconRecord> = {}
dispatch: React.Dispatch<any> = () => {}
constructor () {
constructor() {
super(profile)
this.events = new EventEmitter()
this.htmlElement = document.createElement('div')
this.htmlElement.setAttribute('id', 'icon-panel')
}
renderComponent () {
const fixedOrder = ['filePanel', 'search', 'solidity','udapp', 'debugger', 'solidityStaticAnalysis', 'solidityUnitTesting', 'pluginManager']
renderComponent() {
const fixedOrder = ['filePanel', 'search', 'solidity', 'udapp', 'debugger', 'solidityStaticAnalysis', 'solidityUnitTesting', 'pluginManager']
const divived = Object.values(this.icons).map((value) => { return {
...value,
isRequired: fixedOrder.indexOf(value.profile.name) > -1
}}).sort((a,b) => {
return a.timestamp - b.timestamp
})
const divived = Object.values(this.icons)
.map((value) => {
return {
...value,
isRequired: fixedOrder.indexOf(value.profile.name) > -1
}
})
.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>) {
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
onActivation () {
onActivation() {
this.renderComponent()
this.on('sidePanel', 'focusChanged', (name: string) => {
Object.keys(this.icons).map((o) => {
@ -69,7 +76,7 @@ export class VerticalIcons extends Plugin {
})
}
async linkContent (profile: Profile) {
async linkContent(profile: Profile) {
if (!profile.icon) return
if (!profile.kind) profile.kind = 'none'
this.icons[profile.name] = {
@ -81,7 +88,7 @@ export class VerticalIcons extends Plugin {
this.renderComponent()
}
unlinkContent (profile: Profile) {
unlinkContent(profile: Profile) {
delete this.icons[profile.name]
this.renderComponent()
}
@ -95,7 +102,7 @@ export class VerticalIcons extends Plugin {
* Set an icon as active
* @param {string} name Name of profile of the module to activate
*/
select (name: string) {
select(name: string) {
// TODO: Only keep `this.emit` (issue#2210)
this.emit('showContent', name)
this.events.emit('showContent', name)
@ -105,22 +112,21 @@ export class VerticalIcons extends Plugin {
* Toggles the side panel for plugin
* @param {string} name Name of profile of the module to activate
*/
toggle (name: string) {
toggle(name: string) {
// TODO: Only keep `this.emit` (issue#2210)
this.emit('toggleContent', name)
this.events.emit('toggleContent', name)
}
updateComponent(state: any){
return <RemixUiVerticalIconsPanel
verticalIconsPlugin={state.verticalIconsPlugin}
icons={state.icons}
/>
updateComponent(state: any) {
return <RemixUiVerticalIconsPanel verticalIconsPlugin={state.verticalIconsPlugin} icons={state.icons} />
}
render() {
render() {
return (
<div id='icon-panel'><PluginViewWrapper plugin={this} /></div>
);
<div id="icon-panel">
<PluginViewWrapper plugin={this} />
</div>
)
}
}

@ -1,9 +1,9 @@
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 {Plugin} from '@remixproject/engine'
import {customAction} from '@remixproject/plugin-api'
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,11 +11,10 @@ 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)
@ -23,13 +22,13 @@ export class ContractFlattener extends Plugin {
onActivation(): void {
this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => {
if(data.sources && Object.keys(data.sources).length > 1) {
if(this.triggerFlattenContract) {
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 +47,7 @@ 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}`
@ -58,17 +56,15 @@ export class ContractFlattener extends Plugin {
let sorted
let result
let sources
try{
try {
dependencyGraph = getDependencyGraph(ast, filePath)
sorted = dependencyGraph.isEmpty()
? [filePath]
: dependencyGraph.sort().reverse()
sorted = dependencyGraph.isEmpty() ? [filePath] : dependencyGraph.sort().reverse()
sources = source.sources
result = concatSourceFiles(sorted, sources)
}catch(err){
} catch (err) {
console.warn(err)
}
await this.call('fileManager', 'writeFile', path , result)
await this.call('fileManager', 'writeFile', path, result)
_paq.push(['trackEvent', 'plugin', 'contractFlattener', 'flattenAContract'])
// clean up memory references & return result
sorted = null
@ -76,4 +72,4 @@ export class ContractFlattener extends Plugin {
dependencyGraph = null
return result
}
}
}

@ -1,11 +1,11 @@
import { Plugin } from '@remixproject/engine'
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'
import {Plugin} from '@remixproject/engine'
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
@ -13,7 +13,7 @@ interface INotificationApi {
}
}
const profile:LibraryProfile<INotificationApi> = {
const profile: LibraryProfile<INotificationApi> = {
name: 'notification',
displayName: 'Notification',
description: 'Displays notifications',
@ -22,23 +22,23 @@ const profile:LibraryProfile<INotificationApi> = {
export class NotificationPlugin extends Plugin implements MethodApi<INotificationApi> {
dispatcher: dispatchModalInterface
constructor () {
constructor() {
super(profile)
}
setDispatcher (dispatcher: dispatchModalInterface) {
setDispatcher(dispatcher: dispatchModalInterface) {
this.dispatcher = dispatcher
}
async modal (args: AppModal) {
async modal(args: AppModal) {
return this.dispatcher.modal(args)
}
async alert (args: AlertModal) {
async alert(args: AlertModal) {
return this.dispatcher.alert(args)
}
async toast (message: string | JSX.Element) {
async toast(message: string | JSX.Element) {
this.dispatcher.toast(message)
}
}

@ -1,63 +1,108 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
import { CompilerAbstract } from '@remix-project/remix-solidity'
import { CompilationResult } from '@remix-project/remix-solidity'
import {Plugin} from '@remixproject/engine'
import {sourceMappingDecoder} from '@remix-project/remix-debug'
import {CompilerAbstract} from '@remix-project/remix-solidity'
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 { lastCompilationResult, RemixApi } from '@remixproject/plugin-api'
import { antlr } from './types'
import { ParseResult } from './types/antlr-types'
import {Profile} from '@remixproject/plugin-utils'
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' ||
node.nodeType === 'FunctionDefinition' ||
node.nodeType === 'ModifierDefinition' ||
node.nodeType === 'VariableDeclaration' ||
node.nodeType === 'StructDefinition' ||
node.nodeType === 'EventDefinition'
return (
node.nodeType === 'ContractDefinition' ||
node.nodeType === 'FunctionDefinition' ||
node.nodeType === 'ModifierDefinition' ||
node.nodeType === 'VariableDeclaration' ||
node.nodeType === 'StructDefinition' ||
node.nodeType === 'EventDefinition'
)
}
export type genericASTNode =
ContractDefinitionAstNode
| FunctionDefinitionAstNode
| ModifierDefinitionAstNode
| VariableDeclarationAstNode
| StructDefinitionAstNode
| EventDefinitionAstNode
| IdentifierAstNode
| FunctionCallAstNode
| ImportDirectiveAstNode
| SourceUnitAstNode
| ContractDefinitionAstNode
| FunctionDefinitionAstNode
| ModifierDefinitionAstNode
| VariableDeclarationAstNode
| StructDefinitionAstNode
| EventDefinitionAstNode
| IdentifierAstNode
| FunctionCallAstNode
| ImportDirectiveAstNode
| SourceUnitAstNode
interface flatReferenceIndexNode {
[id: number]: genericASTNode
[id: number]: genericASTNode
}
interface declarationIndexNode {
[id: number]: genericASTNode[]
[id: number]: genericASTNode[]
}
interface codeParserIndex {
declarations: declarationIndexNode,
flatReferences: flatReferenceIndexNode,
nodesPerFile: any
declarations: declarationIndexNode
flatReferences: flatReferenceIndexNode
nodesPerFile: any
}
export class CodeParser extends Plugin {
compilerAbstract: CompilerAbstract
currentFile: string
nodeIndex: codeParserIndex
@ -80,7 +125,6 @@ export class CodeParser extends Plugin {
debuggerIsOn: boolean = false
constructor(astWalker: any) {
super(profile)
this.astWalker = astWalker
@ -106,7 +150,6 @@ export class CodeParser extends Plugin {
}
async onActivation() {
this.gasService = new CodeParserGasService(this)
this.compilerService = new CodeParserCompiler(this)
this.antlrService = new CodeParserAntlrService(this)
@ -158,49 +201,49 @@ export class CodeParser extends Plugin {
})
await this.compilerService.init()
this.on('solidity', 'compilerLoaded', async () => {
this.on('solidity', 'compilerLoaded', async () => {
await this.reload()
})
this.on('debugger', 'startDebugging', async () => {
this.on('debugger', 'startDebugging', async () => {
this.debuggerIsOn = true
await this.reload()
})
this.on('debugger', 'stopDebugging', async () => {
this.on('debugger', 'stopDebugging', async () => {
this.debuggerIsOn = false
await this.reload()
})
}
async reload(){
async reload() {
await this.call('editor', 'discardLineTexts')
await this.call('fileDecorator', 'clearFileDecorators')
await this.call('editor', 'clearErrorMarkers', [this.currentFile])
await this.handleChangeEvents()
await this.handleChangeEvents()
}
/**
*
* @returns
*/
*
* @returns
*/
async getLastCompilationResult() {
return this.compilerAbstract
}
getSubNodes<T extends genericASTNode>(node: T): number[] {
return node.nodeType == "ContractDefinition" && node.contractDependencies;
return node.nodeType == 'ContractDefinition' && node.contractDependencies
}
/**
* Builds a flat index and declarations of all the nodes in the compilation result
* @param compilationResult
* @param source
*/
* Builds a flat index and declarations of all the nodes in the compilation result
* @param compilationResult
* @param source
*/
_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 +254,7 @@ export class CodeParser extends Plugin {
for (const s in compilationResult.sources) {
this.astWalker.walkFull(compilationResult.sources[s].ast, callback)
}
}
}
// NODE HELPERS
@ -232,15 +273,13 @@ export class CodeParser extends Plugin {
return '(' + params.toString() + ')'
}
_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;
if (inScope) node.isClassNode = true
node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult)
node.functionName = node.name + this._getInputParams(node)
node.contractName = contractName
@ -261,7 +300,10 @@ export class CodeParser extends Plugin {
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 }
nodesByContract.contracts[node.name] = {
contractDefinition: node,
contractNodes: flatNodes
}
const baseNodes = {}
const baseNodesWithBaseContractScope = {}
if (node.linearizedBaseContracts) {
@ -271,19 +313,16 @@ 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
}
if (node.members) {
for (const member of node.members) {
member.contractName = (baseContract as any).name
member.contractId = (baseContract as any).id
member.isBaseNode = true;
member.isBaseNode = true
}
}
}
@ -296,10 +335,9 @@ export class CodeParser extends Plugin {
nodesByContract.contracts[node.name].contractScopeNodes = this._flatNodeList(node, fileName, true, compilationResult)
}
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
@ -312,7 +350,6 @@ 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]
for (const name in contracts) {
if (name === contractName) {
@ -331,11 +368,11 @@ export class CodeParser extends Plugin {
} else if (visibility === 'private' || visibility === 'internal') {
executionCost = estimationObj === null ? '-' : estimationObj.internal[fn]
}
return { executionCost }
return {executionCost}
} else {
return {
creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost,
codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost,
codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost
}
}
}
@ -344,31 +381,41 @@ export class CodeParser extends Plugin {
}
/**
* Nodes at position where position is a number, offset
* @param position
* @param type
* @returns
*/
* Nodes at position where position is a number, offset
* @param position
* @param type
* @returns
*/
async nodesAtPosition(position: number, type = ''): Promise<genericASTNode[]> {
let lastCompilationResult = this.compilerAbstract
if(this.debuggerIsOn) {
if (this.debuggerIsOn) {
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])
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 []
}
/**
*
* @param id
* @returns
*/
*
* @param id
* @returns
*/
async getNodeById(id: number) {
for (const key in this.nodeIndex.flatReferences) {
if (this.nodeIndex.flatReferences[key].id === id) {
@ -378,19 +425,19 @@ export class CodeParser extends Plugin {
}
/**
*
* @param id
* @returns
*/
*
* @param id
* @returns
*/
async getDeclaration(id: number) {
if (this.nodeIndex.declarations && this.nodeIndex.declarations[id]) return this.nodeIndex.declarations[id]
}
/**
*
* @param scope
* @returns
*/
*
* @param scope
* @returns
*/
async getNodesWithScope(scope: number) {
const nodes = []
for (const node of Object.values(this.nodeIndex.flatReferences)) {
@ -400,10 +447,10 @@ export class CodeParser extends Plugin {
}
/**
*
* @param name
* @returns
*/
*
* @param name
* @returns
*/
async getNodesWithName(name: string) {
const nodes: genericASTNode[] = []
for (const node of Object.values(this.nodeIndex.flatReferences)) {
@ -412,22 +459,22 @@ export class CodeParser extends Plugin {
return nodes
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @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
}
/**
*
* @param position
* @returns
*/
*
* @param position
* @returns
*/
async definitionAtPosition(position: number) {
const nodes = await this.nodesAtPosition(position)
let nodeDefinition: any
@ -436,7 +483,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) {
@ -464,32 +511,32 @@ export class CodeParser extends Plugin {
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) {
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
}
/**
*
* @param identifierNode
* @returns
*/
*
* @param identifierNode
* @returns
*/
async findIdentifier(identifierNode: any) {
const astNodes = await this.antlrService.listAstNodes()
for (const node of astNodes) {
@ -500,10 +547,10 @@ export class CodeParser extends Plugin {
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @returns
*/
async positionOfDefinition(node: genericASTNode): Promise<any | null> {
if (node) {
if (node.src) {
@ -517,11 +564,11 @@ export class CodeParser extends Plugin {
}
/**
*
* @param node
* @param imported
* @returns
*/
*
* @param node
* @param imported
* @returns
*/
async resolveImports(node: any, imported = {}) {
if (node.nodeType === 'ImportDirective' && !imported[node.sourceUnit]) {
const importNode: any = await this.getNodeById(node.sourceUnit)
@ -535,13 +582,11 @@ export class CodeParser extends Plugin {
return imported
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @returns
*/
referencesOf(node: genericASTNode) {
const results: genericASTNode[] = []
const highlights = (id: number) => {
@ -553,7 +598,7 @@ export class CodeParser extends Plugin {
}
}
}
if (node && ("referencedDeclaration" in node) && node.referencedDeclaration) {
if (node && 'referencedDeclaration' in node && node.referencedDeclaration) {
highlights(node.referencedDeclaration)
const current = this.nodeIndex.flatReferences[node.referencedDeclaration]
results.push(current)
@ -564,10 +609,10 @@ export class CodeParser extends Plugin {
}
/**
*
* @param position
* @returns
*/
*
* @param position
* @returns
*/
async referrencesAtPosition(position: any): Promise<genericASTNode[]> {
const nodes = await this.nodesAtPosition(position)
if (nodes && nodes.length) {
@ -579,20 +624,18 @@ export class CodeParser extends Plugin {
}
/**
*
* @returns
*/
*
* @returns
*/
async getNodes(): Promise<flatReferenceIndexNode> {
return this.nodeIndex.flatReferences
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @returns
*/
async getNodeLink(node: genericASTNode) {
const lineColumn = await this.getLineColumnOfNode(node)
const position = await this.positionOfDefinition(node)
@ -604,16 +647,16 @@ export class CodeParser extends Plugin {
}
/*
* @param node
*/
* @param node
*/
async getLineColumnOfNode(node: any) {
const position = await this.positionOfDefinition(node)
return this.getLineColumnOfPosition(position)
}
/*
* @param position
*/
* @param position
*/
async getLineColumnOfPosition(position: any) {
if (position) {
const fileName = this.compilerAbstract.getSourceName(position.file)
@ -624,14 +667,14 @@ export class CodeParser extends Plugin {
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @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
@ -639,10 +682,10 @@ export class CodeParser extends Plugin {
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @returns
*/
async getVariableDeclaration(node: any) {
const nodeVisibility = node.visibility && node.visibility.length ? node.visibility + ' ' : ''
const nodeName = node.name && node.name.length ? node.name : ''
@ -651,23 +694,21 @@ export class CodeParser extends Plugin {
} 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}`
}
}
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @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) {
@ -678,12 +719,12 @@ export class CodeParser extends Plugin {
}
/**
*
* @param node
* @returns
*/
*
* @param node
* @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 +733,4 @@ export class CodeParser extends Plugin {
return `(${params.join(', ')})`
}
}
}

@ -1,9 +1,9 @@
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 { Profile } from '@remixproject/plugin-utils'
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 {Profile} from '@remixproject/plugin-utils'
const profile = {
name: 'permissionhandler',
@ -46,16 +46,12 @@ 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] = {}
: delete this.sessionPermissions[to.name][method][from.name]
set ? (this.sessionPermissions[to.name][method][from.name] = {}) : delete this.sessionPermissions[to.name][method][from.name]
} else {
set
? this.permissions[to.name][method][from.name] = {}
: delete this.permissions[to.name][method][from.name]
}
set ? (this.permissions[to.name][method][from.name] = {}) : delete this.permissions[to.name][method][from.name]
}
}
clear() {
@ -93,7 +89,7 @@ export class PermissionHandlerPlugin extends Plugin {
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)
@ -124,10 +120,10 @@ export class PermissionHandlerPlugin extends Plugin {
}
const modal: AppModal = {
id: 'PermissionHandler',
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />,
title: <FormattedMessage id="permissionHandler.permissionNeededFor" values={{to: to.displayName || to.name}} />,
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>,
okLabel: <FormattedMessage id='permissionHandler.accept' />,
cancelLabel: <FormattedMessage id='permissionHandler.decline' />
okLabel: <FormattedMessage id="permissionHandler.accept" />,
cancelLabel: <FormattedMessage id="permissionHandler.decline" />
}
const result = await this.call('notification', 'modal', modal)
@ -166,7 +162,7 @@ export class PermissionHandlerPlugin extends Plugin {
}
this.persistPermissions()
}
}
}
reject(this.notAllowWarning(from, to, method))
}
})

@ -1,12 +1,12 @@
/* eslint-disable no-unused-vars */
import React, { useRef, useState, useEffect } from 'react' // eslint-disable-line
import React, {useRef, useState, useEffect} from 'react' // eslint-disable-line
import isElectron from 'is-electron'
import { WebsocketPlugin } from '@remixproject/engine-web'
import {WebsocketPlugin} from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { version as remixdVersion } from '../../../../../libs/remixd/package.json'
import { PluginManager } from '@remixproject/engine'
import { AppModal, AlertModal } from '@remix-ui/app'
import {version as remixdVersion} from '../../../../../libs/remixd/package.json'
import {PluginManager} from '@remixproject/engine'
import {AppModal, AlertModal} from '@remix-ui/app'
const LOCALHOST = ' - connect to localhost - '
@ -19,10 +19,10 @@ const profile = {
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 +62,6 @@ export class RemixdHandle extends WebsocketPlugin {
}
await this.appManager.deactivatePlugin('remixd')
}
async callPluginMethod(key: string, payload?: any[]) {
@ -74,11 +73,11 @@ export class RemixdHandle extends WebsocketPlugin {
}
/**
* connect to localhost if no connection and render the explorer
* disconnect from localhost if connected and remove the explorer
*
* @param {String} txHash - hash of the transaction
*/
* connect to localhost if no connection and render the explorer
* disconnect from localhost if connected and remove the explorer
*
* @param {String} txHash - hash of the transaction
*/
async connectToLocalhost() {
const connection = async (error?: any) => {
if (error) {
@ -91,7 +90,8 @@ export class RemixdHandle extends WebsocketPlugin {
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',
@ -102,8 +102,8 @@ export class RemixdHandle extends WebsocketPlugin {
}
}, 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 +118,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 +126,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 +136,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)
}
@ -153,37 +155,53 @@ 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>.
</div>
<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'>
The remixd command is:
<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>
<div className='mb-2 text-break'>
Example command with flags: <br />
<b>{fullCommandText}</b>
</div>
<div className='mb-2 text-break'>
For info about ports, see <a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">Remixd ports usage</a>
</div>
<div className='mb-2 text-break'>
This feature is still in Alpha. We recommend to keep a backup of the shared folder.
</div>
<div className='mb-2 text-break'>
<h6 className="text-danger">
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>
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>
<div className="mb-2 text-break">
The remixd command is:
<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>
<div className="mb-2 text-break">
Example command with flags: <br />
<b>{fullCommandText}</b>
</div>
<div className="mb-2 text-break">
For info about ports, see{' '}
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">
Remixd ports usage
</a>
</div>
<div className="mb-2 text-break">This feature is still in Alpha. We recommend to keep a backup of the shared folder.</div>
<div className="mb-2 text-break">
<h6 className="text-danger">
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>
</div>
</>)
</>
)
}

@ -1,9 +1,9 @@
import React from 'react' // eslint-disable-line
import { format } from 'util'
import { Plugin } from '@remixproject/engine'
import { compile } from '@remix-project/remix-solidity'
import { Transaction } from 'web3-types'
const _paq = window._paq = window._paq || [] //eslint-disable-line
import {format} from 'util'
import {Plugin} from '@remixproject/engine'
import {compile} from '@remix-project/remix-solidity'
import {Transaction} from 'web3-types'
const _paq = (window._paq = window._paq || []) //eslint-disable-line
const profile = {
name: 'solidity-script',
@ -13,17 +13,16 @@ const profile = {
}
export class SolidityScript extends Plugin {
constructor () {
constructor() {
super(profile)
}
async execute (path: string, functionName: string = 'run') {
async execute(path: string, functionName: string = 'run') {
_paq.push(['trackEvent', 'SolidityScript', 'execute', 'script'])
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
@ -38,13 +37,15 @@ export class SolidityScript extends Plugin {
${functionName}();
}
}`
const targets = { 'script.sol': { content } }
const targets = {'script.sol': {content}}
// 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) {
this.call('terminal', 'log', compilation.data.error.formattedMessage)
}
@ -53,7 +54,7 @@ export class SolidityScript extends Plugin {
this.call('terminal', 'log', error.formattedMessage)
})
}
// get the contract
const contract = compilation.getContract('SolidityScript')
if (!contract) {
@ -83,9 +84,12 @@ export class SolidityScript extends Plugin {
const hhlogs = await web3.testPlugin.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
@ -98,9 +102,10 @@ export class SolidityScript extends Plugin {
}
return <div>{formattedLog}</div>
})}
</div>
</div>
)
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'logHtml', finalLogs)
}
}
}
}

@ -1,19 +1,19 @@
/* eslint-disable @nrwl/nx/enforce-module-boundaries */
import { ViewPlugin } from '@remixproject/engine-web'
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 { 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'
import {RemixUiSolidityUmlGen} from '@remix-ui/solidity-uml-gen'
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'
import vizRenderStringSync from '@aduh95/viz.js/sync'
import { PluginViewWrapper } from '@remix-ui/helper'
import { customAction } from '@remixproject/plugin-api'
import { ClassOptions } from 'sol2uml/lib/converterClass2Dot'
import {PluginViewWrapper} from '@remix-ui/helper'
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',
@ -21,7 +21,7 @@ const profile = {
description: 'Generates UML diagram in svg format from last compiled contract',
location: 'mainPanel',
methods: ['showUmlDiagram', 'generateUml', 'generateCustomAction'],
events: [],
events: []
}
/**
@ -54,7 +54,6 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
this.currentlySelectedTheme = ''
this.themeName = ''
this.activeTheme = {} as ThemeSummary
this.appManager = appManager
this.element = document.createElement('div')
@ -64,7 +63,7 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
onActivation(): void {
this.handleThemeChange()
this.on('solidity', 'compilationFinished', async (file: string, source, languageVersion, data, input, version) => {
if(!this.triggerGenerateUml) return
if (!this.triggerGenerateUml) return
this.triggerGenerateUml = false
const currentTheme: ThemeQualityType = await this.call('theme', 'currentTheme')
this.currentlySelectedTheme = currentTheme.quality
@ -73,14 +72,20 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
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)
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'])
@ -93,8 +98,7 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
}
getThemeCssVariables(cssVars: string) {
return window.getComputedStyle(document.documentElement)
.getPropertyValue(cssVars)
return window.getComputedStyle(document.documentElement).getPropertyValue(cssVars)
}
private handleThemeChange() {
@ -102,7 +106,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')
@ -133,13 +142,11 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
* and assigns to a local property
* @returns {Promise<string>}
*/
async flattenContract (source: any, filePath: string, data: any) {
async flattenContract(source: any, filePath: string, data: any) {
const result = await this.call('contractflattener', 'flattenContract', source, filePath, data)
return result
}
async showUmlDiagram(svgPayload: string) {
this.updatedSvg = svgPayload
this.renderComponent()
@ -150,18 +157,20 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
this.renderComponent()
}
setDispatch (dispatch: React.Dispatch<any>) {
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
this.renderComponent()
}
render() {
return <div id='sol-uml-gen'>
<PluginViewWrapper plugin={this} />
</div>
return (
<div id="sol-uml-gen">
<PluginViewWrapper plugin={this} />
</div>
)
}
renderComponent () {
renderComponent() {
this.dispatch({
...this,
updatedSvg: this.updatedSvg,
@ -171,24 +180,25 @@ 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
updatedSvg={state.updatedSvg}
loading={state.loading}
themeSelected={state.currentlySelectedTheme}
themeName={state.themeName}
fileName={state.fileName}
themeCollection={state.themeCollection}
themeDark={state.themeDark}
/>
return (
<RemixUiSolidityUmlGen
updatedSvg={state.updatedSvg}
loading={state.loading}
themeSelected={state.currentlySelectedTheme}
themeName={state.themeName}
fileName={state.fileName}
themeCollection={state.themeCollection}
themeDark={state.themeDark}
/>
)
}
}
interface Sol2umlClassOptions extends ClassOptions {
backColor?: string
shapeColor?: string
@ -196,15 +206,10 @@ interface Sol2umlClassOptions extends ClassOptions {
textColor?: string
}
import { dirname } from 'path'
import { convertClass2Dot } from 'sol2uml/lib/converterClass2Dot'
import {
Association,
ClassStereotype,
ReferenceType,
UmlClass,
} from 'sol2uml/lib/umlClass'
import { findAssociatedClass } from 'sol2uml/lib/associations'
import {dirname} from 'path'
import {convertClass2Dot} from 'sol2uml/lib/converterClass2Dot'
import {Association, ClassStereotype, ReferenceType, UmlClass} from 'sol2uml/lib/umlClass'
import {findAssociatedClass} from 'sol2uml/lib/associations'
// const debug = require('debug')('sol2uml')
@ -216,11 +221,7 @@ import { findAssociatedClass } from 'sol2uml/lib/associations'
* @param classOptions command line options for the `class` command
* @return dotString Graphviz's DOT format for defining nodes, edges and clusters.
*/
export function convertUmlClasses2Dot(
umlClasses: UmlClass[],
clusterFolders: boolean = false,
classOptions: Sol2umlClassOptions = {}
): string {
export function convertUmlClasses2Dot(umlClasses: UmlClass[], clusterFolders: boolean = false, classOptions: Sol2umlClassOptions = {}): string {
let dotString: string = `
digraph UmlClassDiagram {
rankdir=BT
@ -285,10 +286,7 @@ function sortUmlClassesByCodePath(umlClasses: UmlClass[]): UmlClass[] {
})
}
export function addAssociationsToDot(
umlClasses: UmlClass[],
classOptions: ClassOptions = {}
): string {
export function addAssociationsToDot(umlClasses: UmlClass[], classOptions: ClassOptions = {}): string {
let dotString: string = ''
// for each class
@ -318,18 +316,9 @@ export function addAssociationsToDot(
// for each association in that class
for (const association of Object.values(sourceUmlClass.associations)) {
const targetUmlClass = findAssociatedClass(
association,
sourceUmlClass,
umlClasses
)
const targetUmlClass = findAssociatedClass(association, sourceUmlClass, umlClasses)
if (targetUmlClass) {
dotString += addAssociationToDot(
sourceUmlClass,
targetUmlClass,
association,
classOptions
)
dotString += addAssociationToDot(sourceUmlClass, targetUmlClass, association, classOptions)
}
}
}
@ -337,41 +326,23 @@ export function addAssociationsToDot(
return dotString
}
function addAssociationToDot(
sourceUmlClass: UmlClass,
targetUmlClass: UmlClass,
association: Association,
classOptions: ClassOptions = {}
): string {
function addAssociationToDot(sourceUmlClass: UmlClass, targetUmlClass: UmlClass, association: Association, classOptions: ClassOptions = {}): string {
// do not include library or interface associations if hidden
// Or associations to Structs, Enums or Constants if they are hidden
if (
(classOptions.hideLibraries &&
(sourceUmlClass.stereotype === ClassStereotype.Library ||
targetUmlClass.stereotype === ClassStereotype.Library)) ||
(classOptions.hideInterfaces &&
(targetUmlClass.stereotype === ClassStereotype.Interface ||
sourceUmlClass.stereotype === ClassStereotype.Interface)) ||
(classOptions.hideAbstracts &&
(targetUmlClass.stereotype === ClassStereotype.Abstract ||
sourceUmlClass.stereotype === ClassStereotype.Abstract)) ||
(classOptions.hideStructs &&
targetUmlClass.stereotype === ClassStereotype.Struct) ||
(classOptions.hideEnums &&
targetUmlClass.stereotype === ClassStereotype.Enum) ||
(classOptions.hideConstants &&
targetUmlClass.stereotype === ClassStereotype.Constant)
(classOptions.hideLibraries && (sourceUmlClass.stereotype === ClassStereotype.Library || targetUmlClass.stereotype === ClassStereotype.Library)) ||
(classOptions.hideInterfaces && (targetUmlClass.stereotype === ClassStereotype.Interface || sourceUmlClass.stereotype === ClassStereotype.Interface)) ||
(classOptions.hideAbstracts && (targetUmlClass.stereotype === ClassStereotype.Abstract || sourceUmlClass.stereotype === ClassStereotype.Abstract)) ||
(classOptions.hideStructs && targetUmlClass.stereotype === ClassStereotype.Struct) ||
(classOptions.hideEnums && targetUmlClass.stereotype === ClassStereotype.Enum) ||
(classOptions.hideConstants && targetUmlClass.stereotype === ClassStereotype.Constant)
) {
return ''
}
let dotString = `\n${sourceUmlClass.id} -> ${targetUmlClass.id} [`
if (
association.referenceType == ReferenceType.Memory ||
(association.realization &&
targetUmlClass.stereotype === ClassStereotype.Interface)
) {
if (association.referenceType == ReferenceType.Memory || (association.realization && targetUmlClass.stereotype === ClassStereotype.Interface)) {
dotString += 'style=dashed, '
}

@ -1,30 +1,30 @@
import { Plugin } from '@remixproject/engine'
import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
import {Plugin} from '@remixproject/engine'
import {AppModal, AlertModal, ModalTypes} from '@remix-ui/app'
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
export type SuccessRequest = (data: JsonDataResult) => void
export interface IProvider {
options: { [id: string] : any }
init(): Promise<{ [id: string] : any }>
options: {[id: string]: any}
init(): Promise<{[id: string]: any}>
body(): JSX.Element
sendAsync (data: JsonDataRequest): Promise<JsonDataResult>
sendAsync(data: JsonDataRequest): Promise<JsonDataResult>
}
export abstract class AbstractProvider extends Plugin implements IProvider {
@ -33,9 +33,9 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
defaultUrl: string
connected: boolean
nodeUrl: string
options: { [id: string] : any } = {}
options: {[id: string]: any} = {}
constructor (profile, blockchain, defaultUrl) {
constructor(profile, blockchain, defaultUrl) {
super(profile)
this.defaultUrl = defaultUrl
this.provider = null
@ -46,11 +46,11 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
abstract body(): JSX.Element
onDeactivation () {
onDeactivation() {
this.provider = null
}
async init () {
async init() {
this.nodeUrl = await ((): Promise<string> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
@ -61,15 +61,15 @@ 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,
return {
valid: true,
message: ''
}
} else {
return {
valid: false,
valid: false,
message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
}
}
@ -94,7 +94,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
}
}
sendAsync (data: JsonDataRequest): Promise<JsonDataResult> {
sendAsync(data: JsonDataRequest): Promise<JsonDataResult> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
if (!this.provider) return reject(new Error('provider node set'))
@ -102,7 +102,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
})
}
private async switchAway (showError) {
private async switchAway(showError) {
if (!this.provider) return
this.provider = null
this.connected = false
@ -110,19 +110,19 @@ 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)
}
await this.call('udapp', 'setEnvironmentMode', { context: 'vm-merge'})
await this.call('udapp', 'setEnvironmentMode', {context: 'vm-merge'})
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 })
resolve({jsonrpc: '2.0', result, id: data.id})
} catch (error) {
if (error && error.message && error.message.includes('net_version') && error.message.includes('SERVER_ERROR')) {
this.switchAway(true)
@ -131,7 +131,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
}
} else {
const result = data.method === 'net_listening' ? 'canceled' : []
resolve({ jsonrpc: '2.0', result: result, id: data.id })
resolve({jsonrpc: '2.0', result: result, id: data.id})
}
}
}

@ -1,23 +1,26 @@
import React, { useRef } from 'react' // eslint-disable-line
import React, {useRef} from 'react' // eslint-disable-line
import * as packageJson from '../../../../../package.json'
import { AppModal, ModalTypes } from '@remix-ui/app'
import { BasicVMProvider } from './vm-provider'
import { Hardfork } from '@ethereumjs/common'
import {AppModal, ModalTypes} from '@remix-ui/app'
import {BasicVMProvider} from './vm-provider'
import {Hardfork} from '@ethereumjs/common'
export class CustomForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
inputs: any
constructor (blockchain) {
super({
name: 'vm-custom-fork',
displayName: 'Custom fork - Remix VM',
kind: 'provider',
description: 'Custom fork - Remix VM',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-custom-fork',
displayName: 'Custom fork - Remix VM',
kind: 'provider',
description: 'Custom fork - Remix VM',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = ''
this.nodeUrl = ''
@ -25,28 +28,41 @@ export class CustomForkVMProvider extends BasicVMProvider {
this.inputs = {}
}
async init () {
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>
<label className="mt-3 mb-1">Node URL</label>
<input data-id="CustomForkNodeUrl" name="nodeUrl" type="text" className="border form-control border-right-0" />
<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" />
</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"
/>
</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">
{Object.keys(Hardfork).map((value, index) => {
return (
<option value={Hardfork[value]} key={index}>
{value}
</option>
)
})}
</select>
</div>
</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" />
</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">
{Object.keys(Hardfork).map((value, index) => {
return <option value={Hardfork[value]} key={index}>{value}</option>
})}
</select>
</div>
</div>
}
)
}
const result = await ((): Promise<any> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
@ -54,7 +70,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'
@ -96,11 +112,11 @@ export class CustomForkVMProvider extends BasicVMProvider {
this.nodeUrl = undefined
this.blockNumber = undefined
}
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
fork: this.fork,
nodeUrl: this.nodeUrl,
blockNumber: this.blockNumber
}
}
}

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

@ -1,6 +1,6 @@
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { AbstractProvider } from './abstract-provider'
import {AbstractProvider} from './abstract-provider'
const profile = {
name: 'foundry-provider',
@ -12,20 +12,29 @@ const profile = {
}
export class FoundryProvider extends AbstractProvider {
constructor (blockchain) {
constructor(blockchain) {
super(profile, blockchain, 'http://127.0.0.1:8545')
}
body (): JSX.Element {
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>
)
}
}
}

@ -1,6 +1,6 @@
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { AbstractProvider } from './abstract-provider'
import {AbstractProvider} from './abstract-provider'
const profile = {
name: 'ganache-provider',
@ -12,20 +12,29 @@ const profile = {
}
export class GanacheProvider extends AbstractProvider {
constructor (blockchain) {
constructor(blockchain) {
super(profile, blockchain, 'http://127.0.0.1:8545')
}
body (): JSX.Element {
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>
)
}
}
}

@ -1,29 +1,32 @@
import * as packageJson from '../../../../../package.json'
import { BasicVMProvider } from './vm-provider'
import {BasicVMProvider} from './vm-provider'
export class GoerliForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
constructor (blockchain) {
super({
name: 'vm-goerli-fork',
displayName: 'Goerli fork - Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-goerli-fork',
displayName: 'Goerli fork - Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = 'shanghai'
this.nodeUrl = 'https://remix-sepolia.ethdevops.io'
this.blockNumber = 'latest'
}
async init () {
async init() {
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
fork: this.fork,
nodeUrl: this.nodeUrl,
blockNumber: this.blockNumber
}
}
}

@ -1,6 +1,6 @@
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { AbstractProvider } from './abstract-provider'
import {AbstractProvider} from './abstract-provider'
const profile = {
name: 'hardhat-provider',
@ -12,19 +12,26 @@ const profile = {
}
export class HardhatProvider extends AbstractProvider {
constructor (blockchain) {
constructor(blockchain) {
super(profile, blockchain, 'http://127.0.0.1:8545')
}
body (): JSX.Element {
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>
)
}
}
}

@ -1,32 +1,31 @@
import { InjectedProviderDefaultBase } from './injected-provider-default'
import {InjectedProviderDefaultBase} from './injected-provider-default'
export class InjectedL2Provider extends InjectedProviderDefaultBase {
export class InjectedL2Provider extends InjectedProviderDefaultBase {
chainName: string
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
this.rpcUrls = rpcUrls
}
async init () {
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')
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,19 +36,19 @@ 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
}
}
// handle other "switch" errors
}
}
}

@ -1,5 +1,5 @@
import * as packageJson from '../../../../../package.json'
import { InjectedL2Provider } from './injected-L2-provider'
import {InjectedL2Provider} from './injected-L2-provider'
const profile = {
name: 'injected-arbitrum-one-provider',
@ -11,8 +11,7 @@ const profile = {
}
export class InjectedArbitrumOneProvider extends InjectedL2Provider {
constructor () {
constructor() {
super(profile, 'Arbitrum One', '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
}
}
}

@ -1,5 +1,5 @@
import * as packageJson from '../../../../../package.json'
import { InjectedL2Provider } from './injected-L2-provider'
import {InjectedL2Provider} from './injected-L2-provider'
const profile = {
name: 'injected-optimism-provider',
@ -11,8 +11,7 @@ const profile = {
}
export class Injected0ptimismProvider extends InjectedL2Provider {
constructor () {
constructor() {
super(profile, 'Optimism', '0xa', ['https://mainnet.optimism.io'])
}
}
}

@ -1,25 +1,25 @@
/* global ethereum */
import * as packageJson from '../../../../../package.json'
import { InjectedProvider } from './injected-provider'
import {InjectedProvider} from './injected-provider'
export class InjectedProviderDefaultBase extends InjectedProvider {
constructor (profile) {
export class InjectedProviderDefaultBase extends InjectedProvider {
constructor(profile) {
super(profile)
}
async init () {
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 (!(await injectedProvider._metamask.isUnlocked())) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
}
return super.init()
}
getInjectedProvider () {
getInjectedProvider() {
return (window as any).ethereum
}
notFound () {
notFound() {
return 'No injected provider found. Make sure your provider (e.g. MetaMask, ...) is active and running (when recently activated you may have to reload the page).'
}
}
@ -34,7 +34,7 @@ const profile = {
}
export class InjectedProviderDefault extends InjectedProviderDefaultBase {
constructor () {
constructor() {
super(profile)
}
}

@ -1,6 +1,6 @@
/* global ethereum */
import * as packageJson from '../../../../../package.json'
import { InjectedProvider } from './injected-provider'
import {InjectedProvider} from './injected-provider'
const profile = {
name: 'injected-trustwallet',
@ -11,16 +11,16 @@ const profile = {
version: packageJson.version
}
export class InjectedProviderTrustWallet extends InjectedProvider {
constructor () {
export class InjectedProviderTrustWallet extends InjectedProvider {
constructor() {
super(profile)
}
getInjectedProvider () {
getInjectedProvider() {
return (window as any).trustwallet
}
notFound () {
notFound() {
return 'Could not find Trust Wallet provider. Please make sure the Trust Wallet extension is active. Download the latest version from https://trustwallet.com/browser-extension'
}
}

@ -1,15 +1,15 @@
/* global ethereum */
import React from 'react' // eslint-disable-line
import { Plugin } from '@remixproject/engine'
import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider'
import { IProvider } from './abstract-provider'
import {Plugin} from '@remixproject/engine'
import {JsonDataRequest, RejectRequest, SuccessRequest} from '../providers/abstract-provider'
import {IProvider} from './abstract-provider'
export abstract class InjectedProvider extends Plugin implements IProvider {
options: { [id: string] : any } = {}
options: {[id: string]: any} = {}
listenerAccountsChanged: (accounts: Array<string>) => void
listenerChainChanged: (chainId: number) => void
constructor (profile) {
constructor(profile) {
super(profile)
this.listenerAccountsChanged = (accounts: Array<string>) => {
this.emit('accountsChanged', accounts)
@ -25,8 +25,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')
}
@ -42,22 +42,20 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
}
}
askPermission (throwIfNoInjectedProvider) {
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>
)
body(): JSX.Element {
return <div></div>
}
async init () {
async init() {
const injectedProvider = this.getInjectedProvider()
if (injectedProvider === undefined) {
this.call('notification', 'toast', this.notFound())
@ -68,38 +66,46 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
return {}
}
sendAsync (data: JsonDataRequest): Promise<any> {
sendAsync(data: JsonDataRequest): Promise<any> {
return new Promise((resolve, reject) => {
this.sendAsyncInternal(data, resolve, reject)
})
}
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 })
return resolve({
jsonrpc: '2.0',
error: 'no injected provider found',
id: data.id
})
}
try {
let resultData
if (web3Provider.request) resultData = await web3Provider.request({ method: data.method, params: data.params})
if (web3Provider.request) resultData = await web3Provider.request({method: data.method, params: data.params})
else if (web3Provider.send) resultData = await web3Provider.send(data.method, data.params)
else {
resolve({ jsonrpc: '2.0', error: 'provider not valid', id: data.id })
resolve({jsonrpc: '2.0', error: 'provider not valid', id: data.id})
return
}
if (resultData) {
if (resultData.jsonrpc && resultData.jsonrpc === '2.0') {
resultData = resultData.result
}
resolve({ jsonrpc: '2.0', result: resultData, id: data.id })
resolve({jsonrpc: '2.0', result: resultData, id: data.id})
} else {
resolve({ jsonrpc: '2.0', error: 'no return data provided', id: data.id })
resolve({jsonrpc: '2.0', error: 'no return data provided', id: data.id})
}
} catch (error) {
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
})
}
}
}

@ -1,29 +1,32 @@
import * as packageJson from '../../../../../package.json'
import { BasicVMProvider } from './vm-provider'
import {BasicVMProvider} from './vm-provider'
export class MainnetForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
constructor (blockchain) {
super({
name: 'vm-mainnet-fork',
displayName: 'Mainet fork -Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-mainnet-fork',
displayName: 'Mainet fork -Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = 'shanghai'
this.nodeUrl = 'https://mainnet.infura.io/v3/08b2a484451e4635a28b3d8234f24332'
this.blockNumber = 'latest'
}
async init () {
async init() {
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
fork: this.fork,
nodeUrl: this.nodeUrl,
blockNumber: this.blockNumber
}
}
}

@ -1,29 +1,32 @@
import * as packageJson from '../../../../../package.json'
import { BasicVMProvider } from './vm-provider'
import {BasicVMProvider} from './vm-provider'
export class SepoliaForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
constructor (blockchain) {
super({
name: 'vm-sepolia-fork',
displayName: 'Sepolia fork - Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-sepolia-fork',
displayName: 'Sepolia fork - Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = 'shanghai'
this.nodeUrl = 'https://remix-sepolia.ethdevops.io'
this.blockNumber = 'latest'
}
async init () {
async init() {
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
fork: this.fork,
nodeUrl: this.nodeUrl,
blockNumber: this.blockNumber
}
}
}

@ -1,41 +1,41 @@
import React from 'react' // eslint-disable-line
import * as packageJson from '../../../../../package.json'
import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider'
import { Plugin } from '@remixproject/engine'
import { IProvider } from './abstract-provider'
import {JsonDataRequest, RejectRequest, SuccessRequest} from '../providers/abstract-provider'
import {Plugin} from '@remixproject/engine'
import {IProvider} from './abstract-provider'
export class BasicVMProvider extends Plugin implements IProvider {
blockchain
fork: string
options: { [id: string] : any } = {}
constructor (profile, blockchain) {
options: {[id: string]: any} = {}
constructor(profile, blockchain) {
super(profile)
this.blockchain = blockchain
this.fork = ''
}
async init (): Promise<{ [id: string] : any }> { return {} }
async init(): Promise<{[id: string]: any}> {
return {}
}
body (): JSX.Element {
return (
<div></div>
)
body(): JSX.Element {
return <div></div>
}
sendAsync (data: JsonDataRequest): Promise<any> {
sendAsync(data: JsonDataRequest): Promise<any> {
return new Promise((resolve, reject) => {
this.sendAsyncInternal(data, resolve, reject)
})
}
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) => {
if (error) return reject(error)
else {
resolve({ jsonrpc: '2.0', result, id: data.id })
resolve({jsonrpc: '2.0', result, id: data.id})
}
})
})
} catch (error) {
reject(error)
}
@ -43,61 +43,73 @@ export class BasicVMProvider extends Plugin implements IProvider {
}
export class MergeVMProvider extends BasicVMProvider {
constructor (blockchain) {
super({
name: 'vm-merge',
displayName: 'Remix VM (Merge)',
kind: 'provider',
description: 'Remix VM (Merge)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-merge',
displayName: 'Remix VM (Merge)',
kind: 'provider',
description: 'Remix VM (Merge)',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = 'merge'
}
}
export class LondonVMProvider extends BasicVMProvider {
constructor (blockchain) {
super({
name: 'vm-london',
displayName: 'Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-london',
displayName: 'Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = 'london'
}
}
export class BerlinVMProvider extends BasicVMProvider {
constructor (blockchain) {
super({
name: 'vm-berlin',
displayName: 'Remix VM (Berlin)',
kind: 'provider',
description: 'Remix VM (Berlin)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-berlin',
displayName: 'Remix VM (Berlin)',
kind: 'provider',
description: 'Remix VM (Berlin)',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = 'berlin'
}
}
export class ShanghaiVMProvider extends BasicVMProvider {
constructor (blockchain) {
super({
name: 'vm-shanghai',
displayName: 'Remix VM (Shanghai)',
kind: 'provider',
description: 'Remix VM (Shanghai)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
constructor(blockchain) {
super(
{
name: 'vm-shanghai',
displayName: 'Remix VM (Shanghai)',
kind: 'provider',
description: 'Remix VM (Shanghai)',
methods: ['sendAsync', 'init'],
version: packageJson.version
},
blockchain
)
this.blockchain = blockchain
this.fork = 'shanghai'
}
}
}

@ -1,7 +1,7 @@
import { ViewPlugin } from '@remixproject/engine-web'
import {ViewPlugin} from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line
import { SearchTab } from '@remix-ui/search'
import {SearchTab} from '@remix-ui/search'
const profile = {
name: 'search',
displayName: 'Search in files',
@ -17,17 +17,15 @@ const profile = {
}
export class SearchPlugin extends ViewPlugin {
constructor () {
constructor() {
super(profile)
}
render() {
return (
<div id='searchTab'>
<div id="searchTab">
<SearchTab plugin={this}></SearchTab>
</div>
);
)
}
}

@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React from 'react' // eslint-disable-line
import { ViewPlugin } from '@remixproject/engine-web'
import {ViewPlugin} from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
import { RemixUiSettings } from '@remix-ui/settings' //eslint-disable-line
import {RemixUiSettings} from '@remix-ui/settings' //eslint-disable-line
import Registry from '../state/registry'
import { PluginViewWrapper } from '@remix-ui/helper'
import {PluginViewWrapper} from '@remix-ui/helper'
const profile = {
name: 'settings',
@ -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 {
@ -31,7 +31,7 @@ module.exports = class SettingsTab extends ViewPlugin {
element: HTMLDivElement
public useMatomoAnalytics: any
dispatch: React.Dispatch<any> = () => {}
constructor (config, editor) {
constructor(config, editor) {
super(profile)
this.config = config
this.config.events.on('configChanged', (changedConfig) => {
@ -47,37 +47,41 @@ module.exports = class SettingsTab extends ViewPlugin {
this.useMatomoAnalytics = null
}
setDispatch (dispatch: React.Dispatch<any>) {
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
this.renderComponent()
}
render() {
return <div id='settingsTab'>
<PluginViewWrapper plugin={this} />
</div>
return (
<div id="settingsTab">
<PluginViewWrapper plugin={this} />
</div>
)
}
updateComponent(state: any){
return <RemixUiSettings
config={state.config}
editor={state.editor}
_deps={state._deps}
useMatomoAnalytics={state.useMatomoAnalytics}
themeModule = {state._deps.themeModule}
localeModule={state._deps.localeModule}
/>
updateComponent(state: any) {
return (
<RemixUiSettings
config={state.config}
editor={state.editor}
_deps={state._deps}
useMatomoAnalytics={state.useMatomoAnalytics}
themeModule={state._deps.themeModule}
localeModule={state._deps.localeModule}
/>
)
}
renderComponent () {
renderComponent() {
this.dispatch(this)
}
get (key) {
get(key) {
return this.config.get(key)
}
updateMatomoAnalyticsChoice (isChecked) {
updateMatomoAnalyticsChoice(isChecked) {
this.config.set('settings/matomo-analytics', isChecked)
this.useMatomoAnalytics = isChecked
this.dispatch({

@ -1,23 +1,23 @@
import React from 'react' // eslint-disable-line
import { fromWei, toBigInt, toWei } from 'web3-utils'
import { Plugin } from '@remixproject/engine'
import { toBuffer, addHexPrefix } from '@ethereumjs/util'
import { EventEmitter } from 'events'
import { format } from 'util'
import { ExecutionContext } from './execution-context'
import {fromWei, toBigInt, toWei} from 'web3-utils'
import {Plugin} from '@remixproject/engine'
import {toBuffer, addHexPrefix} from '@ethereumjs/util'
import {EventEmitter} from 'events'
import {format} from 'util'
import {ExecutionContext} from './execution-context'
import Config from '../config'
import { VMProvider } from './providers/vm'
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
const { txResultHelper } = helpers
const { resultToRemixTx } = txResultHelper
import {VMProvider} from './providers/vm'
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
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',
@ -28,19 +28,19 @@ const profile = {
}
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
}
@ -54,16 +54,16 @@ export class Blockchain extends Plugin {
networkcallid: number
networkStatus: {
network: {
name: string,
name: string
id: string
}
error?: string
}
providers: { [key: string]: VMProvider | InjectedProvider | NodeProvider }
providers: {[key: string]: VMProvider | InjectedProvider | NodeProvider}
transactionContextAPI: TransactionContextAPI
// NOTE: the config object will need to be refactored out in remix-lib
constructor (config: Config) {
constructor(config: Config) {
super(profile)
this.active = false
this.event = new EventManager()
@ -71,55 +71,61 @@ export class Blockchain extends Plugin {
this.events = new EventEmitter()
this.config = config
const web3Runner = new TxRunnerWeb3({
config: this.config,
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
const web3Runner = new TxRunnerWeb3(
{
config: this.config,
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
},
isVM: () => {
return this.executionContext.isVM()
},
personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
}
},
isVM: () => { return this.executionContext.isVM() },
personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
}
}, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit())
(_) => this.executionContext.web3(),
(_) => this.executionContext.currentblockGasLimit()
)
this.txRunner = new TxRunner(web3Runner, {})
this.networkcallid = 0
this.networkStatus = { network: { name: ' - ', id: ' - ' } }
this.networkStatus = {network: {name: ' - ', id: ' - '}}
this.setupEvents()
this.setupProviders()
}
_triggerEvent (name, args) {
_triggerEvent(name, args) {
if (!this.active) return
this.event.trigger(name, args)
this.emit(name, ...args)
}
onActivation () {
onActivation() {
this.active = true
this.on('injected', 'chainChanged', () => {
this.detectNetwork((error, network) => {
this.networkStatus = { network, error }
this.networkStatus = {network, error}
this._triggerEvent('networkStatus', [this.networkStatus])
})
})
this.on('injected-trustwallet', 'chainChanged', () => {
this.detectNetwork((error, network) => {
this.networkStatus = { network, error }
this.networkStatus = {network, error}
this._triggerEvent('networkStatus', [this.networkStatus])
})
})
this.on('walletconnect', 'chainChanged', () => {
this.detectNetwork((error, network) => {
this.networkStatus = { network, error }
this.networkStatus = {network, error}
this._triggerEvent('networkStatus', [this.networkStatus])
})
})
}
onDeactivation () {
onDeactivation() {
this.active = false
this.off('injected', 'chainChanged')
this.off('injected-trustwallet', 'chainChanged')
@ -127,12 +133,12 @@ export class Blockchain extends Plugin {
this.off('walletconnect', 'accountsChanged')
}
setupEvents () {
setupEvents() {
this.executionContext.event.register('contextChanged', async (context) => {
await this.resetEnvironment()
this._triggerEvent('contextChanged', [context])
this.detectNetwork((error, network) => {
this.networkStatus = { network, error }
this.networkStatus = {network, error}
this._triggerEvent('networkStatus', [this.networkStatus])
})
})
@ -147,17 +153,17 @@ export class Blockchain extends Plugin {
setInterval(() => {
this.detectNetwork((error, network) => {
this.networkStatus = { network, error }
this.networkStatus = {network, error}
this._triggerEvent('networkStatus', [this.networkStatus])
})
}, 30000)
}
getCurrentNetworkStatus () {
getCurrentNetworkStatus() {
return this.networkStatus
}
setupProviders () {
setupProviders() {
const vmProvider = new VMProvider(this.executionContext)
this.providers = {}
this.providers['vm'] = vmProvider
@ -165,7 +171,7 @@ export class Blockchain extends Plugin {
this.providers.web3 = new NodeProvider(this.executionContext, this.config)
}
getCurrentProvider () {
getCurrentProvider() {
const provider = this.getProvider()
if (provider && provider.startsWith('vm')) return this.providers['vm']
if (this.providers[provider]) return this.providers[provider]
@ -174,11 +180,11 @@ export class Blockchain extends Plugin {
/** Return the list of accounts */
// note: the dual promise/callback is kept for now as it was before
getAccounts (cb) {
getAccounts(cb) {
console.log('getAccounts')
return new Promise((resolve, reject) => {
this.getCurrentProvider().getAccounts((error, accounts) => {
console.log('get accounts res',error, accounts)
console.log('get accounts res', error, accounts)
if (cb) {
return cb(error, accounts)
}
@ -190,36 +196,53 @@ export class Blockchain extends Plugin {
})
}
deployContractAndLibraries (selectedContract, args, contractMetadata, compilerContracts, callbacks, confirmationCb) {
const { continueCb, promptCb, statusCb, finalCb } = callbacks
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) => {
if (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
}
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}`)
}
statusCb(`creation of ${selectedContract.name} pending...`)
this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb)
}, statusCb, (data, runTxCallback) => {
// called for libraries deployment
this.runTx(data, confirmationCb, continueCb, promptCb, runTxCallback)
})
statusCb(`creation of ${selectedContract.name} pending...`)
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) {
const { continueCb, promptCb, statusCb, finalCb } = callbacks
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) => {
if (error) {
return statusCb(`creation of ${selectedContract.name} errored: ${error.message ? error.message : error}`)
}
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}`)
}
statusCb(`creation of ${selectedContract.name} pending...`)
this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb)
})
statusCb(`creation of ${selectedContract.name} pending...`)
this.createContract(selectedContract, data, continueCb, promptCb, confirmationCb, finalCb)
}
)
}
async deployProxy (proxyData, implementationContractObject) {
async deployProxy(proxyData, implementationContractObject) {
const proxyModal = {
id: 'confirmProxyDeployment',
title: 'Confirm Deploy Proxy (ERC1967)',
@ -241,16 +264,20 @@ export class Blockchain extends Plugin {
this.call('notification', 'modal', proxyModal)
}
async runProxyTx (proxyData, implementationContractObject) {
const args = { useCall: false, data: proxyData }
async runProxyTx(proxyData, implementationContractObject) {
const args = {useCall: false, data: proxyData}
let networkInfo
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)
@ -288,16 +315,20 @@ export class Blockchain extends Plugin {
this.call('notification', 'modal', upgradeModal)
}
async runUpgradeTx (proxyAddress, data, newImplementationContractObject) {
const args = { useCall: false, data, to: proxyAddress }
async runUpgradeTx(proxyAddress, data, newImplementationContractObject) {
const args = {useCall: false, data, to: proxyAddress}
let networkInfo
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)
@ -312,8 +343,8 @@ export class Blockchain extends Plugin {
this.runTx(args, confirmationCb, continueCb, promptCb, finalCb)
}
async saveDeployedContractStorageLayout (contractObject, proxyAddress, networkInfo) {
const { contractName, implementationAddress } = contractObject
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`)
// TODO: make deploys folder read only.
@ -336,32 +367,59 @@ 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({
solcInput: contractObject.compiler.source,
solcOutput: contractObject.compiler.data
}, null, 2))
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))
} else {
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({
id: networkInfo.id,
network: networkInfo.name,
deployments: {
[proxyAddress]: {
date: new Date().toISOString(),
contractName: contractName,
fork: networkInfo.currentFork,
implementationAddress: implementationAddress
}
}
}, null, 2))
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(
{
id: networkInfo.id,
network: networkInfo.name,
deployments: {
[proxyAddress]: {
date: new Date().toISOString(),
contractName: contractName,
fork: networkInfo.currentFork,
implementationAddress: implementationAddress
}
}
},
null,
2
)
)
}
}
async getEncodedFunctionHex (args, funABI) {
async getEncodedFunctionHex(args, funABI) {
return new Promise((resolve, reject) => {
txFormat.encodeFunctionCall(args, funABI, (error, data) => {
if (error) return reject(error)
@ -370,7 +428,7 @@ export class Blockchain extends Plugin {
})
}
async getEncodedParams (args, funABI) {
async getEncodedParams(args, funABI) {
return new Promise((resolve, reject) => {
txFormat.encodeParams(args, funABI, (error, encodedParams) => {
if (error) return reject(error)
@ -379,27 +437,25 @@ 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,
(error, txResult, address) => {
if (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`)
}
finalCb(null, selectedContract, address)
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}`)
}
)
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)
})
}
determineGasPrice (cb) {
determineGasPrice(cb) {
this.getCurrentProvider().getGasPrice((error, gasPrice) => {
const warnMessage = ' Please fix this issue before sending any transaction. '
if (error) {
@ -414,29 +470,29 @@ export class Blockchain extends Plugin {
})
}
getInputs (funABI) {
getInputs(funABI) {
if (!funABI.inputs) {
return ''
}
return txHelper.inputParametersDeclarationToString(funABI.inputs)
}
fromWei (value, doTypeConversion, unit) {
fromWei(value, doTypeConversion, unit) {
if (doTypeConversion) {
return fromWei(typeConversion.toInt(value), unit || 'ether')
}
return fromWei(value.toString(10), unit || 'ether')
}
toWei (value, unit) {
toWei(value, unit) {
return toWei(value, unit || 'gwei')
}
calculateFee (gas, gasPrice, unit?) {
calculateFee(gas, gasPrice, unit?) {
return toBigInt(gas) * toBigInt(toWei(gasPrice.toString(10) as string, unit || 'gwei'))
}
determineGasFees (tx) {
determineGasFees(tx) {
const determineGasFeesCb = (gasPrice, cb) => {
let txFeeText, priceStatus
// TODO: this try catch feels like an anti pattern, can/should be
@ -455,19 +511,19 @@ export class Blockchain extends Plugin {
return determineGasFeesCb
}
changeExecutionContext (context, confirmCb, infoCb, cb) {
changeExecutionContext(context, confirmCb, infoCb, cb) {
return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb)
}
detectNetwork (cb) {
detectNetwork(cb) {
return this.executionContext.detectNetwork(cb)
}
getProvider () {
getProvider() {
return this.executionContext.getProvider()
}
getInjectedWeb3Address () {
getInjectedWeb3Address() {
return this.executionContext.getSelectedAddress()
}
@ -475,29 +531,29 @@ export class Blockchain extends Plugin {
* return the fork name applied to the current envionment
* @return {String} - fork name
*/
getCurrentFork () {
getCurrentFork() {
return this.executionContext.getCurrentFork()
}
isWeb3Provider () {
isWeb3Provider() {
const isVM = this.executionContext.isVM()
const isInjected = this.getProvider() === 'injected'
return (!isVM && !isInjected)
return !isVM && !isInjected
}
isInjectedWeb3 () {
isInjectedWeb3() {
return this.getProvider() === 'injected'
}
signMessage (message, account, passphrase, cb) {
signMessage(message, account, passphrase, cb) {
this.getCurrentProvider().signMessage(message, account, passphrase, cb)
}
web3VM () {
web3VM() {
return (this.providers.vm as VMProvider).web3
}
web3 () {
web3() {
// @todo(https://github.com/ethereum/remix-project/issues/431)
const isVM = this.executionContext.isVM()
if (isVM) {
@ -506,7 +562,7 @@ export class Blockchain extends Plugin {
return this.executionContext.web3()
}
getTxListener (opts) {
getTxListener(opts) {
opts.event = {
// udapp: this.udapp.event
udapp: this.event
@ -515,84 +571,107 @@ 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) => {
if (error) {
return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`)
}
if (!lookupOnly) {
logCallback(`${logMsg} pending ... `)
} else {
logCallback(`${logMsg}`)
}
if (funABI.type === 'fallback') data.dataHex = value
if (data) {
data.contractName = contractName
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) => {
txFormat.buildData(
contractName,
contractAbi,
{},
false,
funABI,
callType,
(error, data) => {
if (error) {
return logCallback(`${logMsg} errored: ${error.message ? error.message : error}`)
}
if (lookupOnly) {
outputCb(returnValue)
if (!lookupOnly) {
logCallback(`${logMsg} pending ... `)
} else {
logCallback(`${logMsg}`)
}
})
},
(msg) => {
logCallback(msg)
},
(data, runTxCallback) => {
// called for libraries deployment
this.runTx(data, confirmationCb, runTxCallback, promptCb, () => { /* Do nothing. */ })
})
if (funABI.type === 'fallback') data.dataHex = value
if (data) {
data.contractName = contractName
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) => {
if (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. */
})
}
)
}
context () {
return (this.executionContext.isVM() ? 'memory' : 'blockchain')
context() {
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()
this.executionContext.listenOnLastBlock()
}
addProvider (provider) {
addProvider(provider) {
this.executionContext.addProvider(provider)
}
removeProvider (name) {
removeProvider(name) {
this.executionContext.removeProvider(name)
}
// TODO : event should be triggered by Udapp instead of TxListener
/** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */
startListening (txlistener) {
startListening(txlistener) {
txlistener.event.register('newTransaction', (tx, receipt) => {
this.events.emit('newTransaction', tx, receipt)
})
}
async resetEnvironment () {
async resetEnvironment() {
await this.getCurrentProvider().resetEnvironment()
// TODO: most params here can be refactored away in txRunner
const web3Runner = new TxRunnerWeb3({
config: this.config,
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
const web3Runner = new TxRunnerWeb3(
{
config: this.config,
detectNetwork: (cb) => {
this.executionContext.detectNetwork(cb)
},
isVM: () => {
return this.executionContext.isVM()
},
personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
}
},
isVM: () => { return this.executionContext.isVM() },
personalMode: () => {
return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
}
}, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit())
// isVM: () => { return this.executionContext.isVM() },
// personalMode: () => {
// return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
// }
// }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit())
(_) => this.executionContext.web3(),
(_) => this.executionContext.currentblockGasLimit()
)
web3Runner.event.register('transactionBroadcasted', (txhash) => {
this.executionContext.detectNetwork((error, network) => {
@ -601,10 +680,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">
view on etherscan
</a>))
this.call(
'terminal',
'logHtml',
<a href={etherScanLink(network.name, txhash)} target="_blank">
view on etherscan
</a>
)
}
})
})
@ -615,23 +697,23 @@ export class Blockchain extends Plugin {
* Create a VM Account
* @param {{privateKey: string, balance: string}} newAccount The new account to create
*/
createVMAccount (newAccount) {
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')
}
return (this.providers.vm as VMProvider).createVMAccount(newAccount)
}
newAccount (_password, passwordPromptCb, cb) {
newAccount(_password, passwordPromptCb, cb) {
return this.getCurrentProvider().newAccount(passwordPromptCb, cb)
}
/** Get the balance of an address, and convert wei to ether */
getBalanceInEther (address) {
getBalanceInEther(address) {
return this.getCurrentProvider().getBalanceInEther(address)
}
pendingTransactionsCount () {
pendingTransactionsCount() {
return Object.keys(this.txRunner.pendingTxs).length
}
@ -639,7 +721,7 @@ export class Blockchain extends Plugin {
return await this.web3().eth.getCode(address)
}
async getTransactionReceipt (hash) {
async getTransactionReceipt(hash) {
return await this.web3().eth.getTransactionReceipt(hash)
}
@ -649,7 +731,7 @@ export class Blockchain extends Plugin {
*
* @param {Object} tx - transaction.
*/
sendTransaction (tx: Transaction) {
sendTransaction(tx: Transaction) {
return new Promise((resolve, reject) => {
this.executionContext.detectNetwork((error, network) => {
if (error) return reject(error)
@ -659,17 +741,26 @@ export class Blockchain extends Plugin {
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().testPlugin.getExecutionResultFromSimulator(result.transactionHash)
resolve(resultToRemixTx(result, execResult))
} else
resolve(resultToRemixTx(result))
} else resolve(resultToRemixTx(result))
} catch (e) {
reject(e)
}
@ -679,7 +770,7 @@ export class Blockchain extends Plugin {
})
}
async runTx (args, confirmationCb, continueCb, promptCb, cb) {
async runTx(args, confirmationCb, continueCb, promptCb, cb) {
const getGasLimit = () => {
return new Promise((resolve, reject) => {
if (this.transactionContextAPI.getGasLimit) {
@ -713,7 +804,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)
})
}
@ -744,46 +838,67 @@ 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,
async (error, result) => {
if (error) {
if (typeof (error) !== 'string') {
if (error.message) error = error.message
else {
try { error = 'error: ' + JSON.stringify(error) } catch (e) { console.log(e) }
this.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, async (error, result) => {
if (error) {
if (typeof error !== 'string') {
if (error.message) error = error.message
else {
try {
error = 'error: ' + JSON.stringify(error)
} catch (e) {
console.log(e)
}
}
return reject(error)
}
return reject(error)
}
const isVM = this.executionContext.isVM()
if (isVM && tx.useCall) {
try {
result.transactionHash = await this.web3().testPlugin.getHashFromTagBySimulator(timestamp)
} catch (e) {
console.log('unable to retrieve back the "call" hash', e)
}
const isVM = this.executionContext.isVM()
if (isVM && tx.useCall) {
try {
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])
return resolve({ result, tx })
}
)
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)
@ -808,9 +923,12 @@ export class Blockchain extends Plugin {
const hhlogs = await this.web3().testPlugin.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
@ -823,14 +941,17 @@ export class Blockchain extends Plugin {
}
return <div>{formattedLog}</div>
})}
</div>
</div>
)
_paq.push(['trackEvent', 'udapp', 'hardhat', 'console.log'])
this.call('terminal', 'logHtml', finalLogs)
}
execResult = await this.web3().testPlugin.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')
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) {

@ -1,19 +1,18 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { render } from 'react-dom'
import {render} from 'react-dom'
import './index.css'
import { ThemeModule } from './app/tabs/theme-module'
import { Preload } from './app/components/preload'
import {ThemeModule} from './app/tabs/theme-module'
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 () {
import {Storage} from '@remix-project/remix-lib'
;(async function () {
try {
const configStorage = new Storage('config-v0.8:')
const config = new Config(configStorage);
Registry.getInstance().put({ api: config, name: 'config' })
} catch (e) { }
const config = new Config(configStorage)
Registry.getInstance().put({api: config, name: 'config'})
} catch (e) {}
const theme = new ThemeModule()
theme.initTheme()
@ -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 {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,7 +16,7 @@ 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)
}
@ -32,9 +32,8 @@ const implicitDependencies = JSON.parse(project).implicitDependencies
const copyPatterns = implicitDependencies.map((dep) => {
try {
fs.statSync(__dirname + `/../../dist/apps/${dep}`).isDirectory()
return { from: `../../dist/apps/${dep}`, to: `plugins/${dep}` }
}
catch (e) {
return {from: `../../dist/apps/${dep}`, to: `plugins/${dep}`}
} catch (e) {
console.log('error', e)
return false
}
@ -50,30 +49,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 +81,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 +96,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,23 +118,23 @@ 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) {
apply(compiler) {
const onEnd = async () => {
try {
console.log('runnning CopyFileAfterBuild')
@ -142,17 +142,15 @@ 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)
}
}
})
} catch (e) {
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 {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
})

@ -1,9 +1,9 @@
/* eslint-disable no-use-before-define */
import React from 'react'
import { SolidityCompiler } from '@remix-ui/solidity-compiler' // eslint-disable-line
import {SolidityCompiler} from '@remix-ui/solidity-compiler' // eslint-disable-line
import { CompilerClientApi } from './compiler'
import {CompilerClientApi} from './compiler'
const remix = new CompilerClientApi()

@ -1,10 +1,10 @@
const { composePlugins, withNx } = require('@nrwl/webpack')
const { withReact } = require('@nrwl/react')
const {composePlugins, withNx} = require('@nrwl/webpack')
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,
@ -22,56 +22,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 +77,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
})

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'
import React, {useState, useEffect} from 'react'
import { VyperCompilationOutput, remixClient } from './utils'
import { CompilationResult } from '@remixproject/plugin-api'
import {VyperCompilationOutput, remixClient} from './utils'
import {CompilationResult} from '@remixproject/plugin-api'
// Components
import CompilerButton from './components/CompilerButton'
@ -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)
@ -53,17 +53,15 @@ const App: React.FC = () => {
/** Update the environment state value */
function setEnvironment(environment: 'local' | 'remote') {
setState({ ...state, environment })
setState({...state, environment})
}
function setLocalUrl(url: string) {
setState({ ...state, localUrl: url })
setState({...state, localUrl: url})
}
function compilerUrl() {
return state.environment === 'remote'
? 'https://vyper.remixproject.org/compile'
: state.localUrl
return state.environment === 'remote' ? 'https://vyper.remixproject.org/compile' : state.localUrl
}
return (
@ -73,12 +71,8 @@ const App: React.FC = () => {
<img src={'assets/logo.svg'} alt="Vyper logo" />
<h4>yper Compiler</h4>
</div>
<a
rel="noopener noreferrer"
href="https://github.com/ethereum/remix-project/tree/master/apps/vyper"
target="_blank"
>
<i className="fab fa-github"></i>
<a rel="noopener noreferrer" href="https://github.com/ethereum/remix-project/tree/master/apps/vyper" target="_blank">
<i className="fab fa-github"></i>
</a>
</header>
<section>
@ -87,12 +81,7 @@ const App: React.FC = () => {
Clone Vyper examples repository
</Button>
</div>
<ToggleButtonGroup
name="remote"
onChange={setEnvironment}
type="radio"
value={state.environment}
>
<ToggleButtonGroup name="remote" onChange={setEnvironment} type="radio" value={state.environment}>
<ToggleButton data-id="remote-compiler" variant="secondary" name="remote" value="remote">
Remote Compiler v0.2.16
</ToggleButton>
@ -100,20 +89,10 @@ const App: React.FC = () => {
Local Compiler
</ToggleButton>
</ToggleButtonGroup>
<LocalUrlInput
url={state.localUrl}
setUrl={setLocalUrl}
environment={state.environment}
/>
<LocalUrlInput url={state.localUrl} setUrl={setLocalUrl} environment={state.environment} />
<WarnRemote environment={state.environment} />
<div className="px-4" id="compile-btn">
<CompilerButton
compilerUrl={compilerUrl()}
contract={contract}
setOutput={(name, update) =>
setOutput({ ...output, [name]: update })
}
/>
<CompilerButton compilerUrl={compilerUrl()} contract={contract} setOutput={(name, update) => setOutput({...output, [name]: update})} />
</div>
<article id="result" className="px-2">
<VyperResult output={contract ? output[contract] : undefined} />

@ -1,22 +1,14 @@
import React from 'react'
import {
isVyper,
compile,
toStandardOutput,
VyperCompilationOutput,
isCompilationError,
remixClient
} from '../utils'
import {isVyper, compile, toStandardOutput, VyperCompilationOutput, isCompilationError, remixClient} from '../utils'
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) {
function CompilerButton({contract, setOutput, compilerUrl}: Props) {
if (!contract || !contract) {
return <Button disabled>No contract selected</Button>
}
@ -33,9 +25,9 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
try {
_contract = await remixClient.getContract()
} catch (e: any) {
setOutput('', { status: 'failed', message: e.message})
setOutput('', {status: 'failed', message: e.message})
return
}
}
remixClient.changeStatus({
key: 'loading',
type: 'info',
@ -45,17 +37,17 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
try {
output = await compile(compilerUrl, _contract)
} catch (e: any) {
setOutput(_contract.name, { status: 'failed', message: e.message})
setOutput(_contract.name, {status: 'failed', message: e.message})
return
}
}
setOutput(_contract.name, output)
// ERROR
if (isCompilationError(output)) {
const line = output.line
if (line) {
const lineColumnPos = {
start: { line: line - 1, column: 10 },
end: { line: line - 1, column: 10 }
start: {line: line - 1, column: 10},
end: {line: line - 1, column: 10}
}
remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
} else {
@ -70,12 +62,12 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
if (message && message.split('\n\n').length > 0) {
try {
message = message.split('\n\n')[message.split('\n\n').length - 1]
} catch (e) {}
} catch (e) {}
}
if (location.length > 0) {
const lineColumnPos = {
start: { line: parseInt(location[0]) - 1, column: 10 },
end: { line: parseInt(location[0]) - 1, column: 10 }
start: {line: parseInt(location[0]) - 1, column: 10},
end: {line: parseInt(location[0]) - 1, column: 10}
}
remixClient.highlight(lineColumnPos as any, _contract.name, message)
}
@ -105,7 +97,7 @@ function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
return (
<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) {
function LocalUrlInput({url, setUrl, environment}: Props) {
if (environment === 'remote') {
return <></>
}
@ -20,17 +19,13 @@ 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}
defaultValue={url}
type="email"
placeholder="eg http://localhost:8000/compile" />
<Form.Text className="text-muted">
</Form.Text>
<Form.Control onBlur={updateUrl} defaultValue={url} type="email" placeholder="eg http://localhost:8000/compile" />
<Form.Text className="text-muted"></Form.Text>
</Form.Group>
</Form>
)
}
export default LocalUrlInput;
export default LocalUrlInput

@ -1,43 +1,45 @@
import React, { useState } from 'react';
import {
VyperCompilationResult,
VyperCompilationOutput,
isCompilationError,
} from '../utils';
import React, {useState} from 'react'
import {VyperCompilationResult, VyperCompilationOutput, 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'
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')
function VyperResult({output}: VyperResultProps) {
const [active, setActive] = useState<keyof VyperCompilationResult>('abi')
if (!output) return (
<div id="result">
<p className="my-3">No contract compiled yet.</p>
</div>
)
if (!output)
return (
<div id="result">
<p className="my-3">No contract compiled yet.</p>
</div>
)
if (isCompilationError(output)) {
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>
)
}
@ -46,30 +48,38 @@ function VyperResult({ output }: VyperResultProps) {
<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

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

@ -1,16 +1,16 @@
import { CompilationResult, ABIDescription } from "@remixproject/plugin-api";
import {CompilationResult, ABIDescription} from '@remixproject/plugin-api'
export interface Contract {
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
}
@ -32,7 +32,7 @@ export function isCompilationError(output: VyperCompilationOutput): output is Vy
/**
* Compile the a contract
* @param url The url of the compiler
* @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> {
@ -45,8 +45,8 @@ export async function compile(url: string, contract: Contract): Promise<VyperCom
}
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: contract.content })
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({code: contract.content})
})
if (response.status === 404) {
@ -64,8 +64,8 @@ export async function compile(url: string, contract: Contract): Promise<VyperCom
* @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,''));
const contractName = fileName.split('/').slice(-1)[0].split('.')[0]
const methodIdentifiers = JSON.parse(JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g, ''))
return {
sources: {
[fileName]: {
@ -84,23 +84,22 @@ export function toStandardOutput(fileName: string, compilationResult: VyperCompi
evm: {
bytecode: {
linkReferences: {},
object: compilationResult['bytecode'].replace('0x',''),
opcodes: ""
object: compilationResult['bytecode'].replace('0x', ''),
opcodes: ''
},
deployedBytecode: {
linkReferences: {},
object: compilationResult['bytecode_runtime'].replace('0x',''),
opcodes: ""
object: compilationResult['bytecode_runtime'].replace('0x', ''),
opcodes: ''
},
methodIdentifiers: methodIdentifiers
}
}
} as any
}
};
}
}
/*
export function createCompilationResultMessage(name: string, result: any) {
if(result.status == 'success') {
@ -136,4 +135,4 @@ export function createCompilationResultMessage(name: string, result: any) {
ir: ""
}
}
*/
*/

@ -1,12 +1,12 @@
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 {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'
export class RemixClient extends PluginClient {
private client = createClient<Api, Readonly<RemixApi>>(this);
private client = createClient<Api, Readonly<RemixApi>>(this)
loaded() {
return this.client.onload()
@ -42,10 +42,19 @@ export class RemixClient extends PluginClient {
// @ts-ignore
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')
// @ts-ignore
this.call('notification', 'toast', 'Vyper repository cloned, the workspace Vyper has been created.')
await this.call(
'dGitProvider',
'clone',
{url: 'https://github.com/vyperlang/vyper', token: null},
// @ts-ignore
'vyper-lang'
)
this.call(
// @ts-ignore
'notification',
'toast',
'Vyper repository cloned, the workspace Vyper has been created.'
)
} catch (e) {
// @ts-ignore
this.call('notification', 'toast', e.message)
@ -54,7 +63,7 @@ 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 */
@ -94,15 +103,15 @@ 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)
}
}
export const remixClient = new RemixClient()
// export const RemixClientContext = React.createContext(new RemixClient())
// export const RemixClientContext = React.createContext(new RemixClient())

@ -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 {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
})

@ -1,9 +1,9 @@
import React, { useEffect, useState } from 'react'
import React, {useEffect, useState} from 'react'
import '../css/app.css'
import '@fortawesome/fontawesome-free/css/all.css'
import type { EthereumClient } from '@web3modal/ethereum'
import { WalletConnectRemixClient } from '../services/WalletConnectRemixClient'
import { WalletConnectUI } from './walletConnectUI'
import type {EthereumClient} from '@web3modal/ethereum'
import {WalletConnectRemixClient} from '../services/WalletConnectRemixClient'
import {WalletConnectUI} from './walletConnectUI'
const remix = new WalletConnectRemixClient()
@ -13,12 +13,12 @@ function App() {
const [theme, setTheme] = useState<string>('dark')
useEffect(() => {
(async () => {
;(async () => {
await remix.initClient()
remix.internalEvents.on('themeChanged', (theme: string) => {
setTheme(theme)
})
setWagmiConfig(remix.wagmiConfig)
setEthereumClient(remix.ethereumClient)
})()
@ -26,10 +26,10 @@ 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>
)
}
export default App
export default App

@ -1,17 +1,16 @@
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' />
</WagmiConfig>
</div>
<Web3Modal projectId={PROJECT_ID} ethereumClient={ethereumClient} themeMode={theme} />
</div>
)
export function WalletConnectUI({ethereumClient, wagmiConfig, theme}) {
return (
<div>
<div style={{display: 'inline-block'}}>
<WagmiConfig config={wagmiConfig}>
<Web3Button label="Connect to a wallet" />
</WagmiConfig>
</div>
<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 {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,26 @@ 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 +72,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,
@ -19,14 +18,16 @@ const inputJson = {
},
outputSelection: {
'*': {
'': [ 'ast' ],
'*': [ 'abi', 'metadata', 'devdoc', 'userdoc', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates' ]
'': ['ast'],
'*': ['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)
@ -36,7 +37,7 @@ const compilationData = JSON.parse(solc.compileStandardWrapper(JSON.stringify(in
console.dir(Object.keys(compilationData))
const compilation = {}
compilation['data'] = compilationData
compilation['source'] = { sources: inputJson.sources }
compilation['source'] = {sources: inputJson.sources}
console.dir(compilation)
console.dir(compilation['data'].errors)
@ -106,4 +107,3 @@ repl.start({
})
module.exports = cmdLine

@ -1,10 +1,10 @@
import React, { useEffect, useState, useRef } from 'react'
import React, {useEffect, useState, useRef} from 'react'
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
@ -53,8 +53,7 @@ const DragBar = (props: IRemixDragBarUi) => {
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 +63,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 +74,14 @@ const DragBar = (props: IRemixDragBarUi) => {
function startDrag() {
setDragState(true)
}
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>
</>
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>
</>
)
}
export default DragBar

@ -1,13 +1,13 @@
import React, { useContext, useEffect } from 'react'
import { AppContext } from '../../context/context'
import { useDialogDispatchers } from '../../context/provider'
import React, {useContext, useEffect} from 'react'
import {AppContext} from '../../context/context'
import {useDialogDispatchers} from '../../context/provider'
const DialogViewPlugin = () => {
const { modal, alert, toast } = useDialogDispatchers()
const {modal, alert, toast} = useDialogDispatchers()
const app = useContext(AppContext)
useEffect(() => {
app.modal.setDispatcher({ modal, alert, toast })
app.modal.setDispatcher({modal, alert, toast})
}, [])
return <></>
}

@ -1,16 +1,17 @@
import React from 'react'
import { useDialogDispatchers, useDialogs } from '../../context/provider'
import { Toaster } from '@remix-ui/toaster'
import {useDialogDispatchers, useDialogs} from '../../context/provider'
import {Toaster} from '@remix-ui/toaster'
import ModalWrapper from './modal-wrapper'
const AppDialogs = () => {
const { handleHideModal, handleToaster } = useDialogDispatchers()
const { focusModal, focusToaster } = useDialogs()
const {handleHideModal, handleToaster} = useDialogDispatchers()
const {focusModal, focusToaster} = useDialogs()
return (
<>
<ModalWrapper {...focusModal} handleHide={handleHideModal}></ModalWrapper>
<Toaster message={focusToaster.message} timestamp={focusToaster.timestamp} handleHide={handleToaster} />
</>)
</>
)
}
export default AppDialogs

@ -1,30 +1,60 @@
import React, { useContext, useEffect, useState } from 'react'
import { AppContext } from '../../context/context'
import { useDialogDispatchers } from '../../context/provider'
import React, {useContext, useEffect, useState} from 'react'
import {AppContext} from '../../context/context'
import {useDialogDispatchers} from '../../context/provider'
declare global {
interface Window {
_paq: any
}
}
const _paq = window._paq = window._paq || []
const _paq = (window._paq = window._paq || [])
const MatomoDialog = (props) => {
const { settings, showMatamo, appManager } = useContext(AppContext)
const { modal } = useDialogDispatchers()
const {settings, showMatamo, appManager} = useContext(AppContext)
const {modal} = useDialogDispatchers()
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])
@ -44,7 +74,7 @@ const MatomoDialog = (props) => {
setVisible(false)
}
return (<></>)
return <></>
}
export default MatomoDialog

@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react'
import { ModalDialog, ModalDialogProps, ValidationResult } from '@remix-ui/modal-dialog'
import { ModalTypes } from '../../types'
import React, {useEffect, useRef, useState} from 'react'
import {ModalDialog, ModalDialogProps, ValidationResult} from '@remix-ui/modal-dialog'
import {ModalTypes} from '../../types'
interface ModalWrapperProps extends ModalDialogProps {
modalType?: ModalTypes
@ -28,26 +28,33 @@ const ModalWrapper = (props: ModalWrapperProps) => {
if (ref.current === undefined && formRef.current === undefined) {
onOkFn()
} else if (formRef.current) {
(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(getFormData()) : props.resolve(getFormData())
} else if (ref.current) {
props.okFn
? // @ts-ignore: Object is possibly 'null'.
props.okFn(ref.current.value)
: // @ts-ignore: Object is possibly 'null'.
props.resolve(ref.current.value)
}
}
const onOkFn = async () => {
(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
}
})
}
}
@ -56,8 +63,15 @@ const ModalWrapper = (props: ModalWrapperProps) => {
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,8 +79,8 @@ const ModalWrapper = (props: ModalWrapperProps) => {
const onFormChanged = () => {
if (props.validationFn) {
const validation = props.validationFn(getFormData())
setState(prevState => {
return { ...prevState, message: createForm(validation), validation }
setState((prevState) => {
return {...prevState, message: createForm(validation), validation}
})
}
}
@ -77,7 +91,7 @@ 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>}
</>
)
}
@ -92,7 +106,7 @@ const ModalWrapper = (props: ModalWrapperProps) => {
...props,
okFn: onFinishPrompt,
cancelFn: onCancelFn,
message: createModalMessage(props.defaultValue, { valid: true })
message: createModalMessage(props.defaultValue, {valid: true})
})
break
case ModalTypes.form:
@ -100,7 +114,7 @@ const ModalWrapper = (props: ModalWrapperProps) => {
...props,
okFn: onFinishPrompt,
cancelFn: onCancelFn,
message: createForm({ valid: true })
message: createForm({valid: true})
})
break
default:
@ -122,14 +136,12 @@ const ModalWrapper = (props: ModalWrapperProps) => {
// reset the message and input if any, so when the modal is shown again it doesn't show the previous value.
const handleHide = () => {
setState(prevState => {
return { ...prevState, message: '' }
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

@ -1,22 +1,26 @@
import React, { useEffect, useState } from 'react'
import { ModalDialog } from '@remix-ui/modal-dialog'
import { useDialogDispatchers } from '../../context/provider'
import React, {useEffect, useState} from 'react'
import {ModalDialog} from '@remix-ui/modal-dialog'
import {useDialogDispatchers} from '../../context/provider'
const OriginWarning = () => {
const { alert } = useDialogDispatchers()
const {alert} = useDialogDispatchers()
const [content, setContent] = useState<string>(null)
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)) {
} 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') {
} 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'
) {
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`)
@ -25,11 +29,11 @@ const OriginWarning = () => {
useEffect(() => {
if (content) {
alert({ id: 'warningOriging', title: null, message: content })
alert({id: 'warningOriging', title: null, message: content})
}
}, [content])
return (<></>)
return <></>
}
export default OriginWarning

@ -1,16 +1,19 @@
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">
<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>
</div></>)
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>
</div>
</>
)
}
export default RemixSplashScreen

@ -1,6 +1,6 @@
import React from 'react'
import { AlertModal, AppModal } from '../interface'
import { ModalInitialState } from '../state/modals'
import {AlertModal, AppModal} from '../interface'
import {ModalInitialState} from '../state/modals'
export const AppContext = React.createContext<any>(null)
@ -8,12 +8,12 @@ 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>({
modal: (data: AppModal) => { },
modal: (data: AppModal) => {},
toast: (message: string | JSX.Element) => {},
alert: (data: AlertModal) => {},
handleHideModal: () => {},

@ -1,13 +1,13 @@
import React, { useReducer } from 'react'
import { modalActionTypes } from '../actions/modals'
import { AlertModal, AppModal } from '../interface'
import { modalReducer } from '../reducer/modals'
import { ModalInitialState } from '../state/modals'
import { ModalTypes } from '../types'
import { AppContext, dispatchModalContext, modalContext } from './context'
import React, {useReducer} from 'react'
import {modalActionTypes} from '../actions/modals'
import {AlertModal, AppModal} from '../interface'
import {modalReducer} from '../reducer/modals'
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 +16,40 @@ 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 = () => {
@ -39,7 +62,7 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
const toast = (message: string | JSX.Element) => {
dispatch({
type: modalActionTypes.setToast,
payload: { message, timestamp: Date.now() }
payload: {message, timestamp: Date.now()}
})
}
@ -50,17 +73,19 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
})
}
return (<dispatchModalContext.Provider value={{ modal, toast, alert, handleHideModal, handleToaster }}>
<modalContext.Provider value={{ modals, toasters, focusModal, focusToaster }}>
{children}
</modalContext.Provider>
</dispatchModalContext.Provider>)
return (
<dispatchModalContext.Provider value={{modal, toast, alert, handleHideModal, handleToaster}}>
<modalContext.Provider value={{modals, toasters, focusModal, focusToaster}}>{children}</modalContext.Provider>
</dispatchModalContext.Provider>
)
}
export const AppProvider = ({ children = [], value = {} } = {}) => {
return <AppContext.Provider value={value}>
<ModalProvider>{children}</ModalProvider>
</AppContext.Provider>
export const AppProvider = ({children = [], value = {}} = {}) => {
return (
<AppContext.Provider value={value}>
<ModalProvider>{children}</ModalProvider>
</AppContext.Provider>
)
}
export const useDialogs = () => {

@ -1,15 +1,15 @@
import React, { useEffect, useRef, useState } from 'react'
import React, {useEffect, useRef, useState} from 'react'
import './style/remix-app.css'
import { RemixUIMainPanel } from '@remix-ui/panel'
import {RemixUIMainPanel} from '@remix-ui/panel'
import MatomoDialog from './components/modals/matomo'
import OriginWarning from './components/modals/origin-warning'
import DragBar from './components/dragbar/dragbar'
import { AppProvider } from './context/provider'
import {AppProvider} from './context/provider'
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 {AppContext} from './context/context'
import {IntlProvider} from 'react-intl'
import {CustomTooltip} from '@remix-ui/helper'
interface IRemixAppUi {
app: any
@ -20,11 +20,14 @@ 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(() => {
async function activateApp () {
async function activateApp() {
props.app.themeModule.initTheme(() => {
setAppReady(true)
props.app.activate()
@ -37,9 +40,9 @@ const RemixApp = (props: IRemixAppUi) => {
}
}, [])
function setListeners () {
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
})
})
@ -85,17 +88,24 @@ const RemixApp = (props: IRemixAppUi) => {
<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 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>
<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>
</CustomTooltip>
</div>
</div>

@ -1,8 +1,7 @@
import { CustomTooltip } from '@remix-ui/helper';
import React, { CSSProperties } from 'react' //eslint-disable-line
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,27 +37,23 @@ 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}>
<input
id={id}
type={inputType}
onChange={onChange}
style={{ verticalAlign: 'bottom' }}
name={name}
className="custom-control-input"
checked={checked}
disabled={disabled}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }} aria-disabled={disabled}>
<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}
>
<input id={id} type={inputType} onChange={onChange} style={{verticalAlign: 'bottom'}} name={name} className="custom-control-input" checked={checked} disabled={disabled} />
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{paddingTop: '0.15rem'}} aria-disabled={disabled}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label}
</label>
@ -66,25 +61,25 @@ 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}>
<input
id={id}
type={inputType}
onChange={onChange}
style={{ verticalAlign: 'bottom' }}
name={name}
className="custom-control-input"
checked={checked}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }}>
<div
className="listenOnNetwork_2A0YE0 custom-control custom-checkbox"
style={
{
display: display,
alignItems: 'center',
visibility: visibility
} as CSSProperties
}
onClick={onClick}
>
<input id={id} type={inputType} onChange={onChange} style={{verticalAlign: 'bottom'}} name={name} className="custom-control-input" checked={checked} />
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{paddingTop: '0.15rem'}}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label}
</label>
</div>
)
return (
title ? (childJSXWithTooltip) : (childJSX)
)
return title ? childJSXWithTooltip : childJSX
}
export default RemixUiCheckbox

@ -1,23 +1,23 @@
import React, { useState } from 'react'
import React, {useState} from 'react'
import copy from 'copy-to-clipboard'
import { Placement } from 'react-bootstrap/esm/Overlay'
import {Placement} from 'react-bootstrap/esm/Overlay'
import './copy-to-clipboard.css'
import { CustomTooltip } from '@remix-ui/helper'
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
let { content } = props
const {tip = 'Copy', icon = 'fa-copy', direction = 'right', getContent, children, ...otherProps} = props
let {content} = props
const [message, setMessage] = useState(tip)
const copyData = () => {
@ -37,7 +37,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,19 +51,11 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
setTimeout(() => setMessage(tip), 500)
}
const childJSX = (
children || (<i className={`far ${icon} ml-1 p-2`} aria-hidden="true"
{...otherProps}
></i>)
)
const childJSX = children || <i className={`far ${icon} ml-1 p-2`} aria-hidden="true" {...otherProps}></i>
return (
<a href='#' onClick={handleClick} onMouseLeave={reset}>
<CustomTooltip
tooltipText={message}
tooltipId="overlay-tooltip"
placement={direction}
>
<a href="#" onClick={handleClick} onMouseLeave={reset}>
<CustomTooltip tooltipText={message} tooltipId="overlay-tooltip" placement={direction}>
{childJSX}
</CustomTooltip>
</a>

@ -1,11 +1,11 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line
import { ExtractData, ExtractFunc } from '../types' // eslint-disable-line
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,
@ -31,14 +31,14 @@ export const useExtractData = (json, extractFunc?: ExtractFunc): Array<{ key: st
if (item instanceof Array) {
ret.children = item.map((item, index) => {
return { key: index, value: item }
return {key: index, value: item}
})
ret.self = 'Array'
ret.isNode = true
ret.isLeaf = false
} else if (item instanceof Object) {
ret.children = Object.keys(item).map((key) => {
return { key: key, value: item[key] }
return {key: key, value: item[key]}
})
ret.self = 'Object'
ret.isNode = true

@ -1,8 +1,20 @@
import { CustomTooltip } from '@remix-ui/helper'
import React, { useState, useEffect } from 'react' // eslint-disable-line
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,7 +58,7 @@ 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
}
})
}
@ -55,20 +67,51 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
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' }}>
<span className="fas fa-reply"></span>
</button>
</div>),
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'}}
>
<span className="fas fa-reply"></span>
</button>
</div>
),
placement: 'top-start',
tagId: 'overbackTooltip',
tooltipMsg: 'Step over back'
},
stepBackJSX : {
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>
@ -77,12 +120,26 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
tagId: 'intobackTooltip',
tooltipMsg: 'Step back'
},
stepIntoJSX : {
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}
style={{ pointerEvents: 'none', color: 'white' }}
<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>
</button>
@ -92,24 +149,59 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
tagId: 'intoforwardTooltip',
tooltipMsg: 'Step into'
},
stepOverForwardJSX : {
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 : {
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>
@ -118,10 +210,30 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
tagId: 'jumppreviousbreakpointTooltip',
tooltipMsg: 'Jump to the previous breakpoint'
},
jumpOutJSX : {
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>
@ -130,10 +242,26 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
tagId: 'jumpoutTooltip',
tooltipMsg: 'Jump out'
},
jumpNextBreakpointJSX : {
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,39 +275,58 @@ export const ButtonNavigation = ({ stepOverBack, stepIntoBack, stepIntoForward,
return (
<div className="buttons">
<div className="stepButtons btn-group py-1">
{
Object.keys(stepMarkupStructure).map(x => (
<CustomTooltip
placement={stepMarkupStructure[x].placement}
tooltipId={stepMarkupStructure[x].tagId}
tooltipText={stepMarkupStructure[x].tooltipMsg}
key={`${stepMarkupStructure[x].placement}-${stepMarkupStructure[x].tooltipMsg}-${stepMarkupStructure[x].tagId}`}
>
{stepMarkupStructure[x].markup}
</CustomTooltip>
))
}
{Object.keys(stepMarkupStructure).map((x) => (
<CustomTooltip
placement={stepMarkupStructure[x].placement}
tooltipId={stepMarkupStructure[x].tagId}
tooltipText={stepMarkupStructure[x].tooltipMsg}
key={`${stepMarkupStructure[x].placement}-${stepMarkupStructure[x].tooltipMsg}-${stepMarkupStructure[x].tagId}`}
>
{stepMarkupStructure[x].markup}
</CustomTooltip>
))}
</div>
<div className="jumpButtons btn-group py-1">
{
Object.keys(jumpMarkupStructure).map(x => (
<CustomTooltip
placement={jumpMarkupStructure[x].placement}
tooltipText={jumpMarkupStructure[x].tooltipMsg}
tooltipId={jumpMarkupStructure[x].tooltipId}
key={`${jumpMarkupStructure[x].placement}-${jumpMarkupStructure[x].tooltipMsg}-${jumpMarkupStructure[x].tagId}`}
>
{jumpMarkupStructure[x].markup}
</CustomTooltip>
))
}
{Object.keys(jumpMarkupStructure).map((x) => (
<CustomTooltip
placement={jumpMarkupStructure[x].placement}
tooltipText={jumpMarkupStructure[x].tooltipMsg}
tooltipId={jumpMarkupStructure[x].tooltipId}
key={`${jumpMarkupStructure[x].placement}-${jumpMarkupStructure[x].tooltipMsg}-${jumpMarkupStructure[x].tagId}`}
>
{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>
)

@ -1,16 +1,16 @@
import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line
import { FormattedMessage } from 'react-intl'
import React, {useState, useEffect, useRef} from 'react' // eslint-disable-line
import {FormattedMessage} from 'react-intl'
import TxBrowser from './tx-browser/tx-browser' // eslint-disable-line
import StepManager from './step-manager/step-manager' // eslint-disable-line
import VmDebugger from './vm-debugger/vm-debugger' // eslint-disable-line
import VmDebuggerHead from './vm-debugger/vm-debugger-head' // eslint-disable-line
import { TransactionDebugger as Debugger } from '@remix-project/remix-debug' // eslint-disable-line
import { DebuggerUIProps } from './idebugger-api' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import { CustomTooltip, isValidHash } from '@remix-ui/helper'
import {TransactionDebugger as Debugger} from '@remix-project/remix-debug' // eslint-disable-line
import {DebuggerUIProps} from './idebugger-api' // eslint-disable-line
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,7 @@ 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 +65,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 +84,15 @@ 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,9 +104,9 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const providerChanged = () => {
debuggerModule.onEnvChanged((provider) => {
setState(prevState => {
setState((prevState) => {
const isLocalNodeUsed = !provider.startsWith('vm') && provider !== 'injected'
return { ...prevState, isLocalNodeUsed: isLocalNodeUsed }
return {...prevState, isLocalNodeUsed: isLocalNodeUsed}
})
})
}
@ -116,34 +119,38 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
debuggerInstance.event.register('debuggerStatus', async (isActive) => {
await debuggerModule.discardHighlight()
setState(prevState => {
return { ...prevState, isActive }
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 => {
return { ...prevState, sourceLocationStatus: '' }
setState((prevState) => {
return {...prevState, sourceLocationStatus: ''}
})
})
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)
const contracts = await debuggerModule.fetchContractAndCompile(address || currentReceipt.contractAddress || currentReceipt.to, currentReceipt)
if (contracts) {
let path = contracts.getSourceName(rawLocation.file)
if (!path) {
@ -155,7 +162,7 @@ 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,8 +173,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
}
if (path) {
setState(prevState => {
return { ...prevState, sourceLocationStatus: '' }
setState((prevState) => {
return {...prevState, sourceLocationStatus: ''}
})
await debuggerModule.discardHighlight()
await debuggerModule.highlight(lineColumnPos, path, rawLocation, stepDetail, lineGasCost)
@ -183,7 +190,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
const updateTxNumberFlag = (empty: boolean) => {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
txNumberIsEmpty: empty,
@ -194,7 +201,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const unloadRequested = (blockNumber, txIndex, tx) => {
unLoad()
setState(prevState => {
setState((prevState) => {
return {
...prevState,
sourceLocationStatus: ''
@ -205,7 +212,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 +235,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 +246,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
})
if (!isValidHash(txNumber)) {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
validationError: 'Invalid transaction hash.'
@ -253,7 +260,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
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.'
@ -272,7 +279,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
@ -298,12 +305,12 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
debugWithGeneratedSources: state.opt.debugWithGeneratedSources
})
setTimeout(async() => {
setTimeout(async () => {
debuggerModule.onStartDebugging(debuggerInstance)
try {
await debuggerInstance.debug(blockNumber, txNumber, tx, () => {
listenToEvents(debuggerInstance, currentReceipt)
setState(prevState => {
setState((prevState) => {
return {
...prevState,
blockNumber,
@ -320,7 +327,7 @@ 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.'
@ -338,7 +345,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}
const debug = (txHash, web3?) => {
setState(prevState => {
setState((prevState) => {
return {
...prevState,
validationError: '',
@ -370,16 +377,22 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
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 } }
})
}} type="checkbox" />
<label
data-id="debugGeneratedSourcesLabel"
className="form-check-label custom-control-label"
htmlFor="debugGeneratedSourcesInput">
<FormattedMessage id='debugger.useGeneratedSources' />(Solidity {'>='} v0.7.2)
<input
className="custom-control-input"
id="debugGeneratedSourcesInput"
onChange={({target: {checked}}) => {
setState((prevState) => {
return {
...prevState,
opt: {...prevState.opt, debugWithGeneratedSources: checked}
}
})
}}
type="checkbox"
/>
<label data-id="debugGeneratedSourcesLabel" className="form-check-label custom-control-label" htmlFor="debugGeneratedSourcesInput">
<FormattedMessage id="debugger.useGeneratedSources" />
(Solidity {'>='} v0.7.2)
</label>
</span>
)
@ -389,50 +402,74 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
<div className="px-2" ref={debuggerTopRef}>
<div>
<div className="mt-2 mb-2 debuggerConfig custom-control custom-checkbox">
<CustomTooltip
tooltipId="debuggerGenSourceCheckbox"
tooltipText={<FormattedMessage id='debugger.debugWithGeneratedSources' />}
placement="bottom-start"
>
<CustomTooltip tooltipId="debuggerGenSourceCheckbox" tooltipText={<FormattedMessage id="debugger.debugWithGeneratedSources" />} placement="bottom-start">
{customJSX}
</CustomTooltip>
</div>
{ 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'
>
<input
className="custom-control-input"
id="debugWithLocalNodeInput"
onChange={({ target: { 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>
</div>
}
{ state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span> }
{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">
<input
className="custom-control-input"
id="debugWithLocalNodeInput"
onChange={({target: {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>
</div>
)}
{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> }
{ !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>
</span>
</div> }
{ state.debugging && <StepManager stepManager={ stepManager } /> }
<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 && (
<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>
</span>
</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>
)

@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef } from 'react' // eslint-disable-line
import React, {useState, useEffect, useRef} from 'react' // eslint-disable-line
export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
export const Slider = ({jumpTo, sliderValue, traceLength}) => {
const onChangeId = useRef(null)
const slider = useRef(null)
@ -9,13 +9,13 @@ export const Slider = ({ jumpTo, sliderValue, traceLength }) => {
}, [sliderValue])
const setValue = (value) => {
if (value < 0) return
if (value < 0) return
if (value === slider.current.value) return
slider.current.value = value
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}

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

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

Loading…
Cancel
Save