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

pull/3951/head
Oleksii Kosynskyi 1 year ago
commit d15c3b3f2d
No known key found for this signature in database
GPG Key ID: B4A8D3CCE22EA65E
  1. 36
      .circleci/config.yml
  2. 2
      .husky/pre-commit
  3. 3
      .lintstagedrc.json
  4. 9
      .prettierrc.json
  5. 6
      README.md
  6. 59
      apps/circuit-compiler/project.json
  7. 17
      apps/circuit-compiler/src/app/app.tsx
  8. 224
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  9. 0
      apps/circuit-compiler/src/css/app.css
  10. 11
      apps/circuit-compiler/src/example/simple.circom
  11. 15
      apps/circuit-compiler/src/index.html
  12. 8
      apps/circuit-compiler/src/main.tsx
  13. 7
      apps/circuit-compiler/src/polyfills.ts
  14. 17
      apps/circuit-compiler/src/profile.json
  15. 24
      apps/circuit-compiler/tsconfig.app.json
  16. 17
      apps/circuit-compiler/tsconfig.json
  17. 92
      apps/circuit-compiler/webpack.config.js
  18. 64
      apps/etherscan/src/app/utils/networks.ts
  19. 4
      apps/remix-ide-e2e/src/commands/checkAnnotations.ts
  20. 2
      apps/remix-ide-e2e/src/commands/checkAnnotationsNotPresent.ts
  21. 70
      apps/remix-ide-e2e/src/tests/editor.test.ts
  22. 77
      apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts
  23. 30
      apps/remix-ide-e2e/src/tests/editorReferences.test.ts
  24. 12
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  25. 120
      apps/remix-ide-e2e/src/types/index.d.ts
  26. 2
      apps/remix-ide/project.json
  27. 4
      apps/remix-ide/release-process.md
  28. 129
      apps/remix-ide/src/app.js
  29. 10
      apps/remix-ide/src/app/editor/editor.js
  30. 47
      apps/remix-ide/src/app/files/fileManager.ts
  31. 8
      apps/remix-ide/src/app/panels/layout.ts
  32. 47
      apps/remix-ide/src/app/plugins/openaigpt.tsx
  33. 56
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  34. 7
      apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts
  35. 5
      apps/remix-ide/src/app/plugins/parser/services/code-parser-gas-service.ts
  36. 4
      apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx
  37. 27
      apps/remix-ide/src/app/providers/injected-custom-provider.tsx
  38. 30
      apps/remix-ide/src/app/providers/injected-ephemery-testnet-provider.tsx
  39. 4
      apps/remix-ide/src/app/providers/injected-optimism-provider.tsx
  40. 27
      apps/remix-ide/src/app/providers/injected-skale-chaos-testnet-provider.tsx
  41. 40
      apps/remix-ide/src/app/udapp/run-tab.js
  42. 144
      apps/remix-ide/src/remixAppManager.js
  43. 1
      apps/remix-ide/src/remixEngine.js
  44. 24
      apps/remix-ide/team-best-practices.md
  45. 77
      apps/walletconnect/src/services/WalletConnectRemixClient.ts
  46. 9
      libs/ghaction-helper/package.json
  47. 2
      libs/remix-analyzer/README.md
  48. 8
      libs/remix-analyzer/package.json
  49. 2
      libs/remix-astwalker/README.md
  50. 6
      libs/remix-astwalker/package.json
  51. 4
      libs/remix-debug/README.md
  52. 12
      libs/remix-debug/package.json
  53. 2
      libs/remix-debug/src/init.ts
  54. 2
      libs/remix-lib/README.md
  55. 4
      libs/remix-lib/package.json
  56. 2
      libs/remix-simulator/README.md
  57. 15
      libs/remix-simulator/bin/ethsim
  58. 6
      libs/remix-simulator/package.json
  59. 6
      libs/remix-solidity/package.json
  60. 21
      libs/remix-solidity/src/compiler/compiler.ts
  61. 2
      libs/remix-solidity/src/compiler/types.ts
  62. 10
      libs/remix-tests/package.json
  63. 114
      libs/remix-ui/editor/src/lib/providers/codeActionProvider.ts
  64. 14
      libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts
  65. 82
      libs/remix-ui/editor/src/lib/providers/quickfixes.ts
  66. 6
      libs/remix-ui/editor/src/lib/remix-ui-editor.css
  67. 138
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  68. 7288
      libs/remix-ui/editor/src/types/monaco.ts
  69. 8
      libs/remix-ui/helper/src/lib/helper-components.tsx
  70. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx
  71. 16
      libs/remix-ui/renderer/src/lib/renderer.tsx
  72. 42
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  73. 2
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  74. 4
      libs/remix-ui/run-tab/src/lib/css/run-tab.css
  75. 2
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  76. 32
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  77. 2
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  78. 17
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  79. 125
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  80. 3
      libs/remix-ui/terminal/src/lib/types/terminalTypes.ts
  81. 2
      libs/remix-url-resolver/README.md
  82. 4
      libs/remix-url-resolver/package.json
  83. 2
      libs/remix-ws-templates/README.md
  84. 4
      libs/remix-ws-templates/package.json
  85. 304
      libs/remix-ws-templates/src/script-templates/contract-deployer/create2-factory-deploy.ts
  86. 2
      libs/remixd/README.md
  87. 20
      package.json
  88. 0
      pkg/index.js
  89. 3
      removedlintstagedrc.json
  90. 277
      yarn.lock

@ -5,7 +5,7 @@ parameters:
type: boolean
default: false
orbs:
browser-tools: circleci/browser-tools@1.4.3
browser-tools: circleci/browser-tools@1.4.4
jobs:
build:
docker:
@ -144,8 +144,9 @@ jobs:
- browser-tools/install-browser-tools:
install-firefox: false
install-chrome: true
install-chromedriver: false
install-geckodriver: false
install-chromedriver: true
- install-chromedriver-custom-linux
- run: google-chrome --version
- run: chromedriver --version
- run: rm LICENSE.chromedriver 2> /dev/null || true
@ -202,7 +203,8 @@ jobs:
install-firefox: false
install-chrome: true
install-geckodriver: false
install-chromedriver: true
install-chromedriver: false
- install-chromedriver-custom-linux
- run: google-chrome --version
- run: chromedriver --version
- run: rm LICENSE.chromedriver 2> /dev/null || true
@ -367,3 +369,31 @@ workflows:
only: remix_beta
# VS Code Extension Version: 1.5.1
commands:
install-chromedriver-custom-linux:
description: Custom script to install chromedriver with better version support for linux
steps:
- run:
name: install-chromedriver-custom-linux
command: |
CHROMEDRIVER_URL=$(curl -s 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json' | jq '.channels.Stable.downloads.chromedriver[] | select(.platform == "linux64") | .url' | tr -d '"')
echo $CHROMEDRIVER_URL
ZIPFILEPATH="/tmp/chromedriver.zip"
echo "Downloading from $CHROMEDRIVER_URL"
curl -f --silent $CHROMEDRIVER_URL > "$ZIPFILEPATH"
BINFILEPATH="$HOME/bin/chromedriver-linux"
echo "Extracting to $BINFILEPATH"
unzip -p "$ZIPFILEPATH" chromedriver-linux64/chromedriver > "$BINFILEPATH"
echo Setting execute flag
chmod +x "$BINFILEPATH"
echo Updating symlink
ln -nfs "$BINFILEPATH" ~/bin/chromedriver
echo Removing ZIP file
rm "$ZIPFILEPATH"
echo Done
chromedriver -v

@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged

@ -1,3 +0,0 @@
{
"*.{tsx,ts,js,jsx,mjs,cjs}": ["prettier --write","eslint --fix"]
}

@ -3,12 +3,5 @@
"useTabs": false,
"printWidth": 180,
"semi": false,
"singleQuote": true,
"quoteProps": "consistent",
"jsxSingleQuote": false,
"bracketSpacing": false,
"trailingComma": "none",
"jsxBracketSameLine": false,
"arrowParens": "always",
"singleAttributePerLine": false
"singleQuote": true
}

@ -12,7 +12,7 @@
[![GitHub contributors](https://img.shields.io/github/contributors/ethereum/remix-project?style=flat&logo=github)](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md)
[![Awesome Remix](https://img.shields.io/badge/Awesome--Remix-resources-green?logo=awesomelists)](https://github.com/ethereum/awesome-remix)
![GitHub](https://img.shields.io/github/license/ethereum/remix-project)
[![Gitter Chat](https://img.shields.io/badge/Gitter%20-chat-brightgreen?style=plastic&logo=gitter)](https://gitter.im/ethereum/remix)
[![Discord](https://img.shields.io/badge/join-discord-brightgreen.svg?style=flat&logo=discord)](https://discord.gg/q4vS2GVn)
[![Twitter Follow](https://img.shields.io/twitter/follow/ethereumremix?style=flat&logo=twitter&color=green)](https://twitter.com/ethereumremix)
</div>
@ -282,7 +282,9 @@ parameters:
## Important Links
- Official website: https://remix-project.org
- Official documentation: https://remix-ide.readthedocs.io/en/latest/
- Curated list of Remix resources, tutorials etc.: https://github.com/ethereum/awesome-remix
- Curated list of Remix resources: https://github.com/ethereum/awesome-remix
- Medium: https://medium.com/remix-ide
- Twitter: https://twitter.com/ethereumremix
- Join Discord: https://discord.gg/q4vS2GVn

@ -0,0 +1,59 @@
{
"name": "circuit-compiler",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/circuit-compiler/src",
"projectType": "application",
"implicitDependencies": ["remixd"],
"targets": {
"build": {
"executor": "@nrwl/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "development",
"options": {
"compiler": "babel",
"outputPath": "dist/apps/circuit-compiler",
"index": "apps/circuit-compiler/src/index.html",
"baseHref": "./",
"main": "apps/circuit-compiler/src/main.tsx",
"polyfills": "apps/circuit-compiler/src/polyfills.ts",
"tsConfig": "apps/circuit-compiler/tsconfig.app.json",
"assets": ["apps/circuit-compiler/src/profile.json"],
"styles": ["apps/circuit-compiler/src/css/app.css"],
"scripts": [],
"webpackConfig": "apps/circuit-compiler/webpack.config.js"
},
"configurations": {
"development": {
},
"production": {
"fileReplacements": [
{
"replace": "apps/circuit-compiler/src/environments/environment.ts",
"with": "apps/circuit-compiler/src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"executor": "@nrwl/webpack:dev-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "circuit-compiler:build",
"hmr": true,
"baseHref": "/"
},
"configurations": {
"development": {
"buildTarget": "circuit-compiler:build:development",
"port": 2023
},
"production": {
"buildTarget": "circuit-compiler:build:production"
}
}
}
},
"tags": []
}

@ -0,0 +1,17 @@
import React, { useEffect } from 'react'
import { CircomPluginClient } from './services/circomPluginClient'
function App() {
useEffect(() => {
new CircomPluginClient()
}, [])
return (
<div className="App">
</div>
)
}
export default App

@ -0,0 +1,224 @@
import {PluginClient} from '@remixproject/plugin'
import {createClient} from '@remixproject/plugin-webview'
import EventManager from 'events'
import pathModule from 'path'
import {parse} from 'circom_wasm'
export class CircomPluginClient extends PluginClient {
public internalEvents: EventManager
constructor() {
super()
createClient(this)
this.internalEvents = new EventManager()
this.methods = ['init', 'parse']
this.onload()
}
init(): void {
console.log('initializing circom plugin...')
}
onActivation(): void {
// @ts-ignore
this.on('editor', 'contentChanged', (path: string, fileContent) => {
if (path.endsWith('.circom')) {
this.parse(path, fileContent)
}
})
}
async parse(path: string, fileContent: string): Promise<void> {
let buildFiles = {
[path]: fileContent
}
buildFiles = await this.resolveDependencies(path, fileContent, buildFiles)
const parsedOutput = parse(path, buildFiles)
try {
const result = JSON.parse(parsedOutput)
if (result.length === 0) {
// @ts-ignore
await this.call('editor', 'clearErrorMarkers', [path])
} else {
const markers = []
for (const report of result) {
for (const label in report.labels) {
if (report.labels[label].file_id === '0') {
// @ts-ignore
const startPosition: {lineNumber: number; column: number} =
await this.call(
'editor',
// @ts-ignore
'getPositionAt',
report.labels[label].range.start
)
// @ts-ignore
const endPosition: {lineNumber: number; column: number} =
await this.call(
'editor',
// @ts-ignore
'getPositionAt',
report.labels[label].range.end
)
markers.push({
message: report.message,
severity: report.type.toLowerCase(),
position: {
start: {
line: startPosition.lineNumber,
column: startPosition.column
},
end: {
line: endPosition.lineNumber,
column: endPosition.column
}
},
file: path
})
}
}
}
if (markers.length > 0) {
// @ts-ignore
await this.call('editor', 'addErrorMarker', markers)
} else {
// @ts-ignore
await this.call('editor', 'clearErrorMarkers', [path])
}
}
} catch (e) {
console.log(e)
}
}
async resolveDependencies(
filePath: string,
fileContent: string,
output = {},
depPath: string = '',
blackPath: string[] = []
): Promise<Record<string, string>> {
// extract all includes
const includes = (fileContent.match(/include ['"].*['"]/g) || []).map(
(include) => include.replace(/include ['"]/g, '').replace(/['"]/g, '')
)
await Promise.all(
includes.map(async (include) => {
// fix for endless recursive includes
if (blackPath.includes(include)) return
let dependencyContent = ''
let path = include
// @ts-ignore
const pathExists = await this.call('fileManager', 'exists', path)
if (pathExists) {
// fetch file content if include import (path) exists within same level as current file opened in editor
dependencyContent = await this.call('fileManager', 'readFile', path)
} else {
// if include import (path) does not exist, try to construct relative path using the original file path (current file opened in editor)
let relativePath = pathModule.resolve(
filePath.slice(0, filePath.lastIndexOf('/')),
include
)
if (relativePath.indexOf('/') === 0)
relativePath = relativePath.slice(1)
const relativePathExists = await this.call(
'fileManager',
// @ts-ignore
'exists',
relativePath
)
if (relativePathExists) {
// fetch file content if include import exists as a relative path
dependencyContent = await this.call(
'fileManager',
'readFile',
relativePath
)
} else {
if (depPath) {
// if depPath is provided, try to resolve include import from './deps' folder in remix
path = pathModule.resolve(
depPath.slice(0, depPath.lastIndexOf('/')),
include
)
if (path.indexOf('/') === 0) path = path.slice(1)
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
} else {
if (include.startsWith('circomlib')) {
// try to resolve include import from github if it is a circomlib dependency
const splitInclude = include.split('/')
const version = splitInclude[1].match(/v[0-9]+.[0-9]+.[0-9]+/g)
if (version && version[0]) {
path = `https://raw.githubusercontent.com/iden3/circomlib/${
version[0]
}/circuits/${splitInclude.slice(2).join('/')}`
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
} else {
path = `https://raw.githubusercontent.com/iden3/circomlib/master/circuits/${splitInclude
.slice(1)
.join('/')}`
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
}
} else {
// If all import cases are not true, use the default import to try fetching from node_modules and unpkg
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
}
}
}
}
// extract all includes from the dependency content
const dependencyIncludes = (
dependencyContent.match(/include ['"].*['"]/g) || []
).map((include) =>
include.replace(/include ['"]/g, '').replace(/['"]/g, '')
)
blackPath.push(include)
// recursively resolve all dependencies of the dependency
if (dependencyIncludes.length > 0) {
await this.resolveDependencies(
filePath,
dependencyContent,
output,
path,
blackPath
)
output[include] = dependencyContent
} else {
output[include] = dependencyContent
}
})
)
return output
}
}

@ -0,0 +1,11 @@
pragma circom 2.0.0;
template Multiplier2() {
signal input a;
signal input b;
signal output c;
c <== a*b;
}
component main = Multiplier2();

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Circuit - Compiler</title>
<base href="./" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"/>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<body>
<div id="root"></div>
</body>
</html>

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

@ -0,0 +1,7 @@
/**
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
*
* See: https://github.com/zloirock/core-js#babel
*/
import 'core-js/stable';
import 'regenerator-runtime/runtime';

@ -0,0 +1,17 @@
{
"name": "circuit-compiler",
"kind": "provider",
"displayName": "Circuit Compiler",
"events": [],
"version": "2.0.0",
"methods": ["init", "parse"],
"canActivate": [],
"url": "",
"description": "Enables circuit compilation and computing a witness for ZK proofs",
"icon": "https://docs.circom.io/assets/images/favicon.png",
"location": "sidePanel",
"documentation": "",
"repo": "https://github.com/ethereum/remix-project/tree/master/apps/circuit-compiler",
"maintainedBy": "Remix",
"authorContact": ""
}

@ -0,0 +1,24 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"jest.config.ts",
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
}
]
}

@ -0,0 +1,92 @@
const { composePlugins, withNx } = require('@nrwl/webpack')
const webpack = require('webpack')
const TerserPlugin = require("terser-webpack-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")
// Nx plugins for webpack.
module.exports = composePlugins(withNx(), (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,
"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',
}
// 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',
})
)
// 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),
})
)
// souce-map loader
config.module.rules.push({
test: /\.js$/,
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({
parallel: true,
terserOptions: {
ecma: 2015,
compress: false,
mangle: false,
format: {
comments: false,
},
},
extractComments: false,
}),
new CssMinimizerPlugin(),
];
config.watchOptions = {
ignored: /node_modules/
}
config.experiments.syncWebAssembly = true
return config;
});

@ -1,36 +1,38 @@
export const scanAPIurls = {
// all mainnet
1: "https://api.etherscan.io/api",
56: "https://api.bscscan.com/api",
137: "https://api.polygonscan.com/api",
250: "https://api.ftmscan.com/api",
42161: "https://api.arbiscan.io/api",
43114: "https://api.snowtrace.io/api",
1285: "https://api-moonriver.moonscan.io/api",
1284: "https://api-moonbeam.moonscan.io/api",
25: "https://api.cronoscan.com/api",
199: "https://api.bttcscan.com/api",
10: "https://api-optimistic.etherscan.io/api",
42220: "https://api.celoscan.io/api",
288: "https://api.bobascan.com/api",
100: "https://api.gnosisscan.io/api",
1101: "https://api-zkevm.polygonscan.com/api",
1: 'https://api.etherscan.io/api',
56: 'https://api.bscscan.com/api',
137: 'https://api.polygonscan.com/api',
250: 'https://api.ftmscan.com/api',
42161: 'https://api.arbiscan.io/api',
43114: 'https://api.snowtrace.io/api',
1285: 'https://api-moonriver.moonscan.io/api',
1284: 'https://api-moonbeam.moonscan.io/api',
25: 'https://api.cronoscan.com/api',
199: 'https://api.bttcscan.com/api',
10: 'https://api-optimistic.etherscan.io/api',
42220: 'https://api.celoscan.io/api',
288: 'https://api.bobascan.com/api',
100: 'https://api.gnosisscan.io/api',
1101: 'https://api-zkevm.polygonscan.com/api',
59144: 'https://api.lineascan.build/api',
// all testnet
5: "https://api-goerli.etherscan.io/api",
11155111: "https://api-sepolia.etherscan.io/api",
97: "https://api-testnet.bscscan.com/api",
80001: "https://api-testnet.polygonscan.com/api",
4002: "https://api-testnet.ftmscan.com/api",
421611: "https://api-testnet.arbiscan.io/api",
42170: "https://api-nova.arbiscan.io/api",
43113: "https://api-testnet.snowtrace.io/api",
1287: "https://api-moonbase.moonscan.io/api",
338: "https://api-testnet.cronoscan.com/api",
1028: "https://api-testnet.bttcscan.com/api",
420: "https://api-goerli-optimistic.etherscan.io/api",
44787: "https://api-alfajores.celoscan.io/api",
2888: "https://api-testnet.bobascan.com/api",
84531: "https://api-goerli.basescan.org/api",
1442: "https://api-testnet-zkevm.polygonscan.com/api"
5: 'https://api-goerli.etherscan.io/api',
11155111: 'https://api-sepolia.etherscan.io/api',
97: 'https://api-testnet.bscscan.com/api',
80001: 'https://api-testnet.polygonscan.com/api',
4002: 'https://api-testnet.ftmscan.com/api',
421611: 'https://api-testnet.arbiscan.io/api',
42170: 'https://api-nova.arbiscan.io/api',
43113: 'https://api-testnet.snowtrace.io/api',
1287: 'https://api-moonbase.moonscan.io/api',
338: 'https://api-testnet.cronoscan.com/api',
1028: 'https://api-testnet.bttcscan.com/api',
420: 'https://api-goerli-optimistic.etherscan.io/api',
44787: 'https://api-alfajores.celoscan.io/api',
2888: 'https://api-testnet.bobascan.com/api',
84531: 'https://api-goerli.basescan.org/api',
1442: 'https://api-testnet-zkevm.polygonscan.com/api',
59140: 'https://api-testnet.lineascan.build/api',
}

@ -2,8 +2,8 @@ import EventEmitter from 'events'
import {NightwatchBrowser} from 'nightwatch'
class checkAnnotations extends EventEmitter {
command (this: NightwatchBrowser, type: string, line: number): NightwatchBrowser {
this.api.assert.containsText(`.margin-view-overlays .${type} + div`, line.toString()).perform(() => this.emit('complete'))
command(this: NightwatchBrowser, type: string): NightwatchBrowser {
this.api.waitForElementPresent(`.glyph-margin-widgets .${type}`).perform(() => this.emit('complete'))
return this
}
}

@ -3,7 +3,7 @@ import { NightwatchBrowser } from 'nightwatch'
class checkAnnotationsNotPresent extends EventEmitter {
command(this: NightwatchBrowser, type: string): NightwatchBrowser {
this.api.waitForElementNotPresent(`.margin-view-overlays .${type}`).perform(() => this.emit('complete'))
this.api.waitForElementNotPresent(`.glyph-margin-widgets .${type}`).perform(() => this.emit('complete'))
return this
}
}

@ -5,12 +5,13 @@ import init from '../helpers/init'
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
'before': function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', true)
},
'Should zoom in editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]')
browser
.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]')
.clickLaunchIcon('filePanel')
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('contracts')
@ -23,7 +24,8 @@ module.exports = {
},
'Should zoom out editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
browser
.waitForElementVisible('#editorView')
.checkElementStyle('.view-lines', 'font-size', '16px')
.click('*[data-id="tabProxyZoomOut"]')
.click('*[data-id="tabProxyZoomOut"]')
@ -31,19 +33,23 @@ module.exports = {
},
'Should display compile error in editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
browser
.waitForElementVisible('#editorView')
.setEditorValue(storageContractWithError + 'error')
.pause(2000)
.waitForElementVisible('.margin-view-overlays .fa-exclamation-square', 120000)
.checkAnnotations('fa-exclamation-square', 29) // error
.waitForElementVisible('.glyph-margin-widgets .fa-exclamation-square', 120000)
.checkAnnotations('fa-exclamation-square') // error
.clickLaunchIcon('udapp')
.checkAnnotationsNotPresent('fa-exclamation-square') // error
.clickLaunchIcon('solidity')
.checkAnnotations('fa-exclamation-square', 29) // error
.checkAnnotations('fa-exclamation-square') // error
},
'Should minimize and maximize codeblock in editor #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
'Should minimize and maximize codeblock in editor #group1':
'' +
function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('#editorView')
.waitForElementVisible('.ace_open')
.click('.ace_start:nth-of-type(1)')
.waitForElementVisible('.ace_closed')
@ -52,24 +58,35 @@ module.exports = {
},
'Should add breakpoint to editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
.waitForElementNotPresent('.margin-view-overlays .fa-circle')
.execute(() => {
(window as any).addRemixBreakpoint(1)
}, [], () => {})
.waitForElementVisible('.margin-view-overlays .fa-circle')
browser
.waitForElementVisible('#editorView')
.waitForElementNotPresent('.glyph-margin-widgets .fa-circle')
.execute(
() => {
;(window as any).addRemixBreakpoint(1)
},
[],
() => {}
)
.waitForElementVisible('.glyph-margin-widgets .fa-circle')
},
'Should load syntax highlighter for ace light theme #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView')
'Should load syntax highlighter for ace light theme #group1':
'' +
function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('#editorView')
.checkElementStyle('.ace_keyword', 'color', aceThemes.light.keyword)
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.light.comment)
.checkElementStyle('.ace_function', 'color', aceThemes.light.function)
.checkElementStyle('.ace_variable', 'color', aceThemes.light.variable)
},
'Should load syntax highlighter for ace dark theme #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]')
'Should load syntax highlighter for ace dark theme #group1':
'' +
function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="verticalIconsKindsettings"]')
.click('*[data-id="verticalIconsKindsettings"]')
.waitForElementVisible('*[data-id="settingsTabThemeLabelDark"]')
.click('*[data-id="settingsTabThemeLabelDark"]')
@ -101,8 +118,11 @@ module.exports = {
.checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
},
'Should remove 1 highlight from source code #group1': '' + function (browser: NightwatchBrowser) {
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
'Should remove 1 highlight from source code #group1':
'' +
function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.click('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.pause(2000)
.executeScriptInTerminal('remix.exeCurrent()')
@ -116,7 +136,8 @@ module.exports = {
},
'Should remove all highlights from source code #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]')
browser
.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]')
.click('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]')
.pause(2000)
.executeScriptInTerminal('remix.exeCurrent()')
@ -126,8 +147,7 @@ module.exports = {
.waitForElementNotPresent('.highlightLine33', 60000)
.waitForElementNotPresent('.highlightLine41', 60000)
.waitForElementNotPresent('.highlightLine51', 60000)
},
}
}
const aceThemes = {
@ -233,5 +253,3 @@ contract Storage {
return number;
}
}`

@ -17,7 +17,6 @@ module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'Should load the test file': function (browser: NightwatchBrowser) {
browser.openFile('contracts')
.openFile('contracts/3_Ballot.sol')
@ -86,7 +85,47 @@ module.exports = {
const path = "//*[@class='view-line' and contains(.,'Voter') and contains(.,'struct')]//span//span[contains(.,'Voter')]"
const expectedContent = 'StructDefinition'
checkEditorHoverContent(browser, path, expectedContent)
}
},
'Add token file': function (browser: NightwatchBrowser) {
browser.addFile('contracts/mytoken.sol', {
content: myToken
}).useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]")
},
// here we change quickly between files to test the files being parsed correctly when switching between them
'Should show ERC20 hover over contract in editor #group1': function (browser: NightwatchBrowser) {
browser.scrollToLine(10)
const path = "//*[@class='view-line' and contains(.,'MyToken') and contains(.,'Pausable')]//span//span[contains(.,'ERC20Burnable')]"
const expectedContent = 'contract ERC20Burnable is ERC20Burnable, ERC20, IERC20Metadata, IERC20, Context'
checkEditorHoverContent(browser, path, expectedContent, 25)
},
'Go back to ballot file': function (browser: NightwatchBrowser) {
browser.openFile('contracts/3_Ballot.sol')
.useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]")
},
'Should show hover over function in editor again #group1': function (browser: NightwatchBrowser) {
browser
.scrollToLine(58)
const path: string = "//*[@class='view-line' and contains(.,'giveRightToVote(address') and contains(.,'function') and contains(.,'public')]//span//span[contains(.,'giveRightToVote')]"
let expectedContent = 'Estimated execution cost'
checkEditorHoverContent(browser, path, expectedContent)
expectedContent = 'function giveRightToVote (address internal voter) public nonpayable returns ()'
checkEditorHoverContent(browser, path, expectedContent)
expectedContent = "@dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'"
checkEditorHoverContent(browser, path, expectedContent)
},
'Open token file': function (browser: NightwatchBrowser) {
browser.openFile('contracts/mytoken.sol')
.useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]")
},
'Should show ERC20 hover over contract in editor again #group1': function (browser: NightwatchBrowser) {
browser.scrollToLine(10)
const path = "//*[@class='view-line' and contains(.,'MyToken') and contains(.,'Pausable')]//span//span[contains(.,'ERC20Burnable')]"
const expectedContent = 'contract ERC20Burnable is ERC20Burnable, ERC20, IERC20Metadata, IERC20, Context'
checkEditorHoverContent(browser, path, expectedContent, 25)
},
}
@ -234,3 +273,37 @@ contract BallotHoverTest {
}
}
`
const myToken = `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, ERC20Burnable, Pausable, Ownable {
constructor() ERC20("MyToken", "MTK") {}
function pause() public onlyOwner {
_pause();
}
function unpause() public onlyOwner {
_unpause();
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
function _beforeTokenTransfer(address from, address to, uint256 amount)
internal
whenNotPaused
override
{
super._beforeTokenTransfer(from, to, amount);
}
}
`

@ -3,25 +3,25 @@ import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
const openReferences = (browser: NightwatchBrowser, path: string) => {
(browser as any).useXpath()
;(browser as any)
.useXpath()
.useXpath()
.waitForElementVisible(path)
.click(path)
.perform(function () {
const actions = this.actions({ async: true });
return actions.
keyDown(this.Keys.SHIFT).
sendKeys(this.Keys.F12)
const actions = this.actions({async: true})
return actions.keyDown(this.Keys.SHIFT).sendKeys(this.Keys.F12)
})
}
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
'before': function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false)
},
'Should load the test file': function (browser: NightwatchBrowser) {
browser.openFile('contracts')
browser
.openFile('contracts')
.openFile('contracts/3_Ballot.sol')
.waitForElementVisible('#editorView')
.setEditorValue(BallotWithARefToOwner)
@ -32,19 +32,21 @@ module.exports = {
browser.scrollToLine(48)
const path = "//*[@class='view-line' and contains(.,'length') and contains(.,'proposalNames')]//span//span[contains(.,'proposalNames')]"
openReferences(browser, path)
browser.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'length; i++')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'name:')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'constructor')]")
browser
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'length; i++')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'name:')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'constructor')]")
.keys(browser.Keys.ESCAPE)
},
'Should show references of getOwner': function (browser: NightwatchBrowser) {
browser.scrollToLine(39)
const path = "//*[@class='view-line' and contains(.,'getOwner') and contains(.,'cowner')]//span//span[contains(.,'getOwner')]"
openReferences(browser, path)
browser.useXpath()
.waitForElementVisible("//*[@class='monaco-highlighted-label']//span[contains(.,'2_Owner.sol')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label']//span[contains(.,'3_Ballot.sol')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'cowner.getOwner')]")
browser
.useXpath()
.waitForElementVisible("//*[@class='monaco-highlighted-label'][contains(.,'2_Owner.sol')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label'][contains(.,'3_Ballot.sol')]")
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'cowner.getOwner')]")
.waitForElementVisible("//*[contains(@class, 'results-loaded') and contains(., 'References (2)')]")
.keys(browser.Keys.ESCAPE)
}

@ -238,8 +238,8 @@ module.exports = {
})
.addFile('tests/hhLogs_test.sol', sources[0]['tests/hhLogs_test.sol'])
.clickLaunchIcon('solidityUnitTesting')
.waitForElementVisible('*[id="singleTesttests/Ballot_test.sol"]', 60000)
.click('*[id="singleTesttests/Ballot_test.sol"]')
.waitForElementVisible('*[id="idsingleTesttests/Ballot_test.sol"]', 60000)
.click('*[id="idsingleTesttests/Ballot_test.sol"]')
.click('#runTestsTabRunAction')
.pause(2000)
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
@ -261,8 +261,8 @@ module.exports = {
.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('tests/ballotFailedLog_test.sol', sources[0]['tests/ballotFailedLog_test.sol'])
.clickLaunchIcon('solidityUnitTesting')
.waitForElementVisible('*[id="singleTesttests/Ballot_test.sol"]', 60000)
.click('*[id="singleTesttests/Ballot_test.sol"]')
.waitForElementVisible('*[id="idsingleTesttests/Ballot_test.sol"]', 60000)
.click('*[id="idsingleTesttests/Ballot_test.sol"]')
.click('#runTestsTabRunAction')
.pause(2000)
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
@ -278,8 +278,8 @@ module.exports = {
.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('tests/ballotFailedDebug_test.sol', sources[0]['tests/ballotFailedDebug_test.sol'])
.clickLaunchIcon('solidityUnitTesting')
.waitForElementVisible('*[id="singleTesttests/Ballot_test.sol"]', 60000)
.click('*[id="singleTesttests/Ballot_test.sol"]')
.waitForElementVisible('*[id="idsingleTesttests/Ballot_test.sol"]', 60000)
.click('*[id="idsingleTesttests/Ballot_test.sol"]')
.click('#runTestsTabRunAction')
.waitForElementVisible('*[data-id="testTabSolidityUnitTestsOutputheader"]', 120000)
.waitForElementContainsText('#solidityUnittestsOutput', 'tests/ballotFailedDebug_test.sol', 60000)

@ -1,62 +1,62 @@
// Merge custom command types with nightwatch types
/* eslint-disable no-use-before-define */
import {NightwatchBrowser} from 'nightwatch' // eslint-disable-line @typescript-eslint/no-unused-vars
export type callbackCheckVerifyCallReturnValue = (values: string[]) => { message: string, pass: boolean }
export type callbackCheckVerifyCallReturnValue = (values: string[]) => {message: string; pass: boolean}
declare module 'nightwatch' {
export interface NightwatchCustomCommands {
clickLaunchIcon(icon: string): NightwatchBrowser,
switchBrowserTab(index: number): NightwatchBrowser,
scrollAndClick(target: string): NightwatchBrowser,
scrollInto(target: string): NightwatchBrowser,
testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser,
setEditorValue(value: string, callback?: () => void): NightwatchBrowser,
addFile(name: string, content: NightwatchContractContent): NightwatchBrowser,
verifyContracts(compiledContractNames: string[], opts?: { wait: number, version?: string, runs?: string }): NightwatchBrowser,
selectAccount(account?: string): NightwatchBrowser,
clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser,
testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser,
goToVMTraceStep(step: number, incr?: number): NightwatchBrowser,
checkVariableDebug(id: string, debugValue: NightwatchCheckVariableDebugValue): NightwatchBrowser,
addAtAddressInstance(address: string, isValidFormat: boolean, isValidChecksum: boolean, isAbi?: boolean): NightwatchBrowser,
modalFooterOKClick(id?: string): NightwatchBrowser,
clickInstance(index: number): NightwatchBrowser,
journalLastChildIncludes(val: string): NightwatchBrowser,
executeScriptInTerminal(script: string): NightwatchBrowser,
clearEditableContent(cssSelector: string): NightwatchBrowser,
journalChildIncludes(val: string, opts = { shouldHaveOnlyOneOccurence: boolean }): NightwatchBrowser,
debugTransaction(index: number): NightwatchBrowser,
checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser,
openFile(name: string): NightwatchBrowser,
refreshPage(): NightwatchBrowser,
verifyLoad(): NightwatchBrowser,
renamePath(path: string, newFileName: string, renamedPath: string): NightwatchBrowser,
rightClickCustom(cssSelector: string): NightwatchBrowser,
scrollToLine(line: number): NightwatchBrowser,
waitForElementContainsText(id: string, value: string, timeout?: number): NightwatchBrowser,
getModalBody(callback: (value: string, cb: VoidFunction) => void): NightwatchBrowser,
modalFooterCancelClick(id?: string): NightwatchBrowser,
selectContract(contractName: string): NightwatchBrowser,
createContract(inputParams: string): NightwatchBrowser,
getAddressAtPosition(index: number, cb: (pos: string) => void): NightwatchBrowser,
testConstantFunction(address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput | null, expectedOutput: string): NightwatchBrowser,
getEditorValue(callback: (content: string) => void): NightwatchBrowser,
getInstalledPlugins(cb: (plugins: string[]) => void): NightwatchBrowser,
verifyCallReturnValue(address: string, checks: string[] | callbackCheckVerifyCallReturnValue): NightwatchBrowser,
testEditorValue(testvalue: string): NightwatchBrowser,
removeFile(path: string, workspace: string): NightwatchBrowser,
switchBrowserWindow(url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult<Window>) => void): NightwatchBrowser,
setupMetamask(passphrase: string, password: string): NightwatchBrowser,
signMessage(msg: string, callback: (hash: { value: string }, signature: { value: string }) => void): NightwatchBrowser,
setSolidityCompilerVersion(version: string): NightwatchBrowser,
clickElementAtPosition(cssSelector: string, index: number, opt?: { forceSelectIfUnselected: boolean }): NightwatchBrowser,
notContainsText(cssSelector: string, text: string): NightwatchBrowser,
sendLowLevelTx(address: string, value: string, callData: string): NightwatchBrowser,
journalLastChild(val: string): NightwatchBrowser,
checkTerminalFilter(filter: string, test: string): NightwatchBrowser,
noWorkerErrorFor(version: string): NightwatchBrowser,
clickLaunchIcon(icon: string): NightwatchBrowser
switchBrowserTab(index: number): NightwatchBrowser
scrollAndClick(target: string): NightwatchBrowser
scrollInto(target: string): NightwatchBrowser
testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser
setEditorValue(value: string, callback?: () => void): NightwatchBrowser
addFile(name: string, content: NightwatchContractContent): NightwatchBrowser
verifyContracts(compiledContractNames: string[], opts?: {wait: number; version?: string; runs?: string}): NightwatchBrowser
selectAccount(account?: string): NightwatchBrowser
clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser
testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser
goToVMTraceStep(step: number, incr?: number): NightwatchBrowser
checkVariableDebug(id: string, debugValue: NightwatchCheckVariableDebugValue): NightwatchBrowser
addAtAddressInstance(address: string, isValidFormat: boolean, isValidChecksum: boolean, isAbi?: boolean): NightwatchBrowser
modalFooterOKClick(id?: string): NightwatchBrowser
clickInstance(index: number): NightwatchBrowser
journalLastChildIncludes(val: string): NightwatchBrowser
executeScriptInTerminal(script: string): NightwatchBrowser
clearEditableContent(cssSelector: string): NightwatchBrowser
journalChildIncludes(val: string, opts = {shouldHaveOnlyOneOccurence: boolean}): NightwatchBrowser
debugTransaction(index: number): NightwatchBrowser
checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser
openFile(name: string): NightwatchBrowser
refreshPage(): NightwatchBrowser
verifyLoad(): NightwatchBrowser
renamePath(path: string, newFileName: string, renamedPath: string): NightwatchBrowser
rightClickCustom(cssSelector: string): NightwatchBrowser
scrollToLine(line: number): NightwatchBrowser
waitForElementContainsText(id: string, value: string, timeout?: number): NightwatchBrowser
getModalBody(callback: (value: string, cb: VoidFunction) => void): NightwatchBrowser
modalFooterCancelClick(id?: string): NightwatchBrowser
selectContract(contractName: string): NightwatchBrowser
createContract(inputParams: string): NightwatchBrowser
getAddressAtPosition(index: number, cb: (pos: string) => void): NightwatchBrowser
testConstantFunction(address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput | null, expectedOutput: string): NightwatchBrowser
getEditorValue(callback: (content: string) => void): NightwatchBrowser
getInstalledPlugins(cb: (plugins: string[]) => void): NightwatchBrowser
verifyCallReturnValue(address: string, checks: string[] | callbackCheckVerifyCallReturnValue): NightwatchBrowser
testEditorValue(testvalue: string): NightwatchBrowser
removeFile(path: string, workspace: string): NightwatchBrowser
switchBrowserWindow(url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult<Window>) => void): NightwatchBrowser
setupMetamask(passphrase: string, password: string): NightwatchBrowser
signMessage(msg: string, callback: (hash: {value: string}, signature: {value: string}) => void): NightwatchBrowser
setSolidityCompilerVersion(version: string): NightwatchBrowser
clickElementAtPosition(cssSelector: string, index: number, opt?: {forceSelectIfUnselected: boolean}): NightwatchBrowser
notContainsText(cssSelector: string, text: string): NightwatchBrowser
sendLowLevelTx(address: string, value: string, callData: string): NightwatchBrowser
journalLastChild(val: string): NightwatchBrowser
checkTerminalFilter(filter: string, test: string): NightwatchBrowser
noWorkerErrorFor(version: string): NightwatchBrowser
validateValueInput(selector: string, valueTosSet: string, expectedValue: string): NightwatchBrowser
checkAnnotations(type: string, line: number): NightwatchBrowser
checkAnnotations(type: string): NightwatchBrowser
checkAnnotationsNotPresent(type: string): NightwatchBrowser
getLastTransactionHash(callback: (hash: string) => void)
currentWorkspaceIs(name: string): NightwatchBrowser
@ -65,17 +65,17 @@ declare module 'nightwatch' {
clearConsole(this: NightwatchBrowser): NightwatchBrowser
clearTransactions(this: NightwatchBrowser): NightwatchBrowser
getBrowserLogs(this: NightwatchBrowser): NightwatchBrowser
currentSelectedFileIs (name: string): NightwatchBrowser,
currentSelectedFileIs(name: string): NightwatchBrowser
switchWorkspace: (workspaceName: string) => NightwatchBrowser
switchEnvironment: (provider: string) => NightwatchBrowser
connectToExternalHttpProvider: (url: string, identifier: string) => NightwatchBrowser
}
export interface NightwatchBrowser {
api: this,
emit: (status: string) => void,
fullscreenWindow: (result?: any) => this,
keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchBrowser,
api: this
emit: (status: string) => void
fullscreenWindow: (result?: any) => this
keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchBrowser
sendKeys: (selector: string, inputValue: string | string[], callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void) => NightwatchBrowser
}
@ -84,11 +84,11 @@ declare module 'nightwatch' {
}
export interface NightwatchContractContent {
content: string;
content: string
}
export interface NightwatchClickFunctionExpectedInput {
types: string,
types: string
values: string
}
@ -97,7 +97,7 @@ declare module 'nightwatch' {
}
export interface NightwatchTestConstantFunctionExpectedInput {
types: string,
types: string
values: string
}

@ -3,7 +3,7 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/remix-ide/src",
"projectType": "application",
"implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "vyper", "solhint", "walletconnect"],
"implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "vyper", "solhint", "walletconnect", "circuit-compiler"],
"targets": {
"build": {
"executor": "@nrwl/webpack:webpack",

@ -45,9 +45,9 @@ This is not strictly speaking a release. Updating the remix site is done through
- git reset --hard -master-commit-hash-
- git push -f origin remix_live
CircleCI will build automaticaly and remix.ethereum.org will be updated
CircleCI will build automatically and remix.ethereum.org will be updated
# remix-alpha.ethereum.org update
remix-alpha.ethereum.org is automaticaly updated every time commits are pushed to master
remix-alpha.ethereum.org is automatically updated every time commits are pushed to master

@ -41,10 +41,13 @@ import { InjectedProviderDefault } from './app/providers/injected-provider-defau
import {InjectedProviderTrustWallet} from './app/providers/injected-provider-trustwallet'
import {Injected0ptimismProvider} from './app/providers/injected-optimism-provider'
import {InjectedArbitrumOneProvider} from './app/providers/injected-arbitrum-one-provider'
import {InjectedEphemeryTestnetProvider} from './app/providers/injected-ephemery-testnet-provider'
import {InjectedSKALEChaosTestnetProvider} from './app/providers/injected-skale-chaos-testnet-provider'
import {FileDecorator} from './app/plugins/file-decorator'
import {CodeFormat} from './app/plugins/code-format'
import {SolidityUmlGen} from './app/plugins/solidity-umlgen'
import {ContractFlattener} from './app/plugins/contractFlattener'
import {OpenAIGpt} from './app/plugins/openaigpt'
const isElectron = require('is-electron')
@ -93,9 +96,7 @@ class AppComponent {
api: this._components.filesProviders.browser,
name: 'fileproviders/browser'
})
this._components.filesProviders.localhost = new RemixDProvider(
this.appManager
)
this._components.filesProviders.localhost = new RemixDProvider(this.appManager)
Registry.getInstance().put({
api: this._components.filesProviders.localhost,
name: 'fileproviders/localhost'
@ -119,7 +120,7 @@ class AppComponent {
this.panels = {}
this.workspace = pluginLoader.get()
this.engine = new RemixEngine()
this.engine.register(appManager);
this.engine.register(appManager)
const matomoDomains = {
'remix-alpha.ethereum.org': 27,
@ -127,15 +128,8 @@ class AppComponent {
'remix.ethereum.org': 23,
'6fd22d6fe5549ad4c4d8fd3ca0b7816b.mod': 35 // remix desktop
}
this.showMatamo =
matomoDomains[window.location.hostname] &&
!Registry.getInstance()
.get('config')
.api.exists('settings/matomo-analytics')
this.walkthroughService = new WalkthroughService(
appManager,
this.showMatamo
)
this.showMatamo = matomoDomains[window.location.hostname] && !Registry.getInstance().get('config').api.exists('settings/matomo-analytics')
this.walkthroughService = new WalkthroughService(appManager, this.showMatamo)
const hosts = ['127.0.0.1:8080', '192.168.0.101:8080', 'localhost:8080']
// workaround for Electron support
@ -159,9 +153,10 @@ class AppComponent {
// ----------------- editor service ----------------------------
const editor = new Editor() // wrapper around ace editor
Registry.getInstance().put({api: editor, name: 'editor'})
editor.event.register('requiringToSaveCurrentfile', () =>
editor.event.register('requiringToSaveCurrentfile', (currentFile) => {
fileManager.saveCurrentFile()
)
if (currentFile.endsWith('.circom')) this.appManager.activatePlugin(['circuit-compiler'])
})
// ----------------- fileManager service ----------------------------
const fileManager = new FileManager(editor, appManager)
@ -187,6 +182,9 @@ class AppComponent {
// ----------------- ContractFlattener ----------------------------
const contractFlattener = new ContractFlattener()
// ----------------- Open AI --------------------------------------
const openaigpt = new OpenAIGpt()
// ----------------- import content service ------------------------
const contentImport = new CompilerImports()
@ -220,9 +218,11 @@ class AppComponent {
const foundryProvider = new FoundryProvider(blockchain)
const externalHttpProvider = new ExternalHttpProvider(blockchain)
const trustWalletInjectedProvider = new InjectedProviderTrustWallet()
const defaultInjectedProvider = new InjectedProviderDefault
const defaultInjectedProvider = new InjectedProviderDefault()
const injected0ptimismProvider = new Injected0ptimismProvider()
const injectedArbitrumOneProvider = new InjectedArbitrumOneProvider()
const injectedEphemeryTestnetProvider = new InjectedEphemeryTestnetProvider()
const injectedSKALEChaosTestnetProvider = new InjectedSKALEChaosTestnetProvider()
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
Registry.getInstance().put({
@ -232,11 +232,11 @@ class AppComponent {
// ----------------- run script after each compilation results -----------
const compileAndRun = new CompileAndRun()
// -------------------Terminal----------------------------------------
makeUdapp(blockchain, compilersArtefacts, domEl => terminal.logHtml(domEl))
makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl))
const terminal = new Terminal(
{appManager, blockchain},
{
getPosition: event => {
getPosition: (event) => {
const limitUp = 36
const limitDown = 20
const height = window.innerHeight
@ -298,11 +298,14 @@ class AppComponent {
trustWalletInjectedProvider,
injected0ptimismProvider,
injectedArbitrumOneProvider,
injectedEphemeryTestnetProvider,
injectedSKALEChaosTestnetProvider,
this.walkthroughService,
search,
solidityumlgen,
contractFlattener,
solidityScript
solidityScript,
openaigpt
])
// LAYOUT & SYSTEM VIEWS
@ -316,42 +319,18 @@ class AppComponent {
this.sidePanel = new SidePanel()
this.hiddenPanel = new HiddenPanel()
const pluginManagerComponent = new PluginManagerComponent(
appManager,
this.engine
)
const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine)
const filePanel = new FilePanel(appManager)
const landingPage = new LandingPage(
appManager,
this.menuicons,
fileManager,
filePanel,
contentImport
)
this.settings = new SettingsTab(
Registry.getInstance().get('config').api,
editor,
appManager
)
const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport)
this.settings = new SettingsTab(Registry.getInstance().get('config').api, editor, appManager)
this.engine.register([
this.menuicons,
landingPage,
this.hiddenPanel,
this.sidePanel,
filePanel,
pluginManagerComponent,
this.settings
])
this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, filePanel, pluginManagerComponent, this.settings])
// CONTENT VIEWS & DEFAULT PLUGINS
const openZeppelinProxy = new OpenZeppelinProxy(blockchain)
const linkLibraries = new LinkLibraries(blockchain)
const deployLibraries = new DeployLibraries(blockchain)
const compileTab = new CompileTab(
Registry.getInstance().get('config').api,
Registry.getInstance().get('filemanager').api
)
const compileTab = new CompileTab(Registry.getInstance().get('config').api, Registry.getInstance().get('filemanager').api)
const run = new RunTab(
blockchain,
Registry.getInstance().get('config').api,
@ -410,27 +389,44 @@ class AppComponent {
await this.appManager.activatePlugin(['layout'])
await this.appManager.activatePlugin(['notification'])
await this.appManager.activatePlugin(['editor'])
await this.appManager.activatePlugin(['permissionhandler', 'theme', 'locale', 'fileManager', 'compilerMetadata', 'compilerArtefacts', 'network', 'web3Provider', 'offsetToLineColumnConverter'])
await this.appManager.activatePlugin([
'permissionhandler',
'theme',
'locale',
'fileManager',
'compilerMetadata',
'compilerArtefacts',
'network',
'web3Provider',
'offsetToLineColumnConverter'
])
await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await this.appManager.activatePlugin(['home'])
await this.appManager.activatePlugin(['settings', 'config'])
await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'codeParser', 'codeFormatter', 'fileDecorator', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler'])
await this.appManager.activatePlugin([
'hiddenPanel',
'pluginManager',
'codeParser',
'codeFormatter',
'fileDecorator',
'terminal',
'blockchain',
'fetchAndCompile',
'contentImport',
'gistHandler'
])
await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder'])
await this.appManager.activatePlugin(['solidity-script'])
await this.appManager.activatePlugin(['solidity-script', 'openaigpt'])
this.appManager.on(
'filePanel',
'workspaceInitializationCompleted',
async () => {
this.appManager.on('filePanel', 'workspaceInitializationCompleted', async () => {
// for e2e tests
const loadedElement = document.createElement('span')
loadedElement.setAttribute('data-id', 'workspaceloaded')
document.body.appendChild(loadedElement)
await this.appManager.registerContextMenuItems()
}
)
})
await this.appManager.activatePlugin(['filePanel'])
// Set workspace after initial activation
@ -441,9 +437,7 @@ class AppComponent {
.then(async () => {
try {
if (params.deactivate) {
await this.appManager.deactivatePlugin(
params.deactivate.split(',')
)
await this.appManager.deactivatePlugin(params.deactivate.split(','))
}
} catch (e) {
console.log(e)
@ -453,10 +447,7 @@ class AppComponent {
this.menuicons.select('solidity')
} else {
// If plugins are loaded from the URL params, we focus on the last one.
if (
this.appManager.pluginLoader.current === 'queryParams' &&
this.workspace.length > 0
) {
if (this.appManager.pluginLoader.current === 'queryParams' && this.workspace.length > 0) {
this.menuicons.select(this.workspace[this.workspace.length - 1])
} else {
this.appManager.call('tabs', 'focus', 'home')
@ -473,17 +464,13 @@ class AppComponent {
}
if (params.calls) {
const calls = params.calls.split("///");
const calls = params.calls.split('///')
// call all functions in the list, one after the other
for (const call of calls) {
const callDetails = call.split("//");
const callDetails = call.split('//')
if (callDetails.length > 1) {
this.appManager.call(
"notification",
"toast",
`initiating ${callDetails[0]} and calling "${callDetails[1]}" ...`
);
this.appManager.call('notification', 'toast', `initiating ${callDetails[0]} and calling "${callDetails[1]}" ...`)
// @todo(remove the timeout when activatePlugin is on 0.3.0)
try {

@ -13,7 +13,7 @@ const profile = {
name: 'editor',
description: 'service - editor',
version: packageJson.version,
methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel','addErrorMarker', 'clearErrorMarkers', 'getText'],
methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel','addErrorMarker', 'clearErrorMarkers', 'getText', 'getPositionAt'],
}
class Editor extends Plugin {
@ -175,8 +175,8 @@ class Editor extends Plugin {
}
this.saveTimeout = window.setTimeout(() => {
this.triggerEvent('contentChanged', [])
this.triggerEvent('requiringToSaveCurrentfile', [])
this.triggerEvent('contentChanged', [currentFile, input])
this.triggerEvent('requiringToSaveCurrentfile', [currentFile])
}, 500)
}
@ -579,6 +579,10 @@ class Editor extends Plugin {
this.clearDecorationsByPlugin(session, from, 'lineTextPerFile', this.registeredDecorations, this.currentDecorations)
}
}
getPositionAt(offset) {
return this.api.getPositionAt(offset)
}
}
module.exports = Editor

@ -1,3 +1,4 @@
'use strict'
import { saveAs } from 'file-saver'
import JSZip from 'jszip'
@ -21,7 +22,7 @@ const profile = {
icon: 'assets/img/fileManager.webp',
permission: true,
version: packageJson.version,
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir',
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir',
'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh',
'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles', 'isGitRepo'],
kind: 'file-system'
@ -217,6 +218,36 @@ class FileManager extends Plugin {
}
}
/**
* Set the content of multiple files
* @param {string[]} filePaths paths of the files
* @param {string[]} data content to write to each file
* @param {string} folderPath base folder path
* @returns {void}
*/
async writeMultipleFiles(filePaths, fileData, folderPath) {
try {
let alert = true
for (let i = 0; i < filePaths.length; i++) {
const installPath = folderPath + "/" + filePaths[i]
let path = this.normalize(installPath)
path = this.limitPluginScope(path)
if (await this.exists(path)) {
await this._handleIsFile(path, `Cannot write file ${path}`)
await this.setMultipleFileContent(path, fileData[i], folderPath, alert)
} else {
await this.setMultipleFileContent(path, fileData[i], folderPath, alert)
this.emit('fileAdded', path)
}
alert = false
}
} catch (e) {
throw new Error(e)
}
}
/**
* Return the content of a specific file
* @param {string} path path of the file
@ -572,13 +603,25 @@ class FileManager extends Plugin {
return await this._setFileInternal(path, content)
}
async setMultipleFileContent(path, content, folderPath, alert) {
if (this.currentRequest) {
const canCall = await this.askUserPermission(`writeFile`, `modifying ${folderPath} ...`)
const required = this.appManager.isRequired(this.currentRequest.from)
if (canCall && !required && alert) {
// inform the user about modification after permission is granted and even if permission was saved before
this.call('notification', 'toast', fileChangedToastMsg(this.currentRequest.from, folderPath))
}
}
return await this._setFileInternal(path, content)
}
_setFileInternal(path, content) {
const provider = this.fileProviderOf(path)
if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` })
// TODO : Add permission
// TODO : Change Provider to Promise
return new Promise((resolve, reject) => {
provider.set(path, content, (error) => {
provider.set(path, content, async (error) => {
if (error) reject(error)
this.syncEditor(path)
this.emit('fileSaved', path)

@ -6,7 +6,7 @@ import { QueryParams } from '@remix-project/remix-lib'
const profile: Profile = {
name: 'layout',
description: 'layout',
methods: ['minimize', 'maximiseSidePanel', 'resetSidePanel']
methods: ['minimize', 'maximiseSidePanel', 'resetSidePanel', 'maximizeTerminal']
}
interface panelState {
@ -109,6 +109,12 @@ export class Layout extends Plugin {
this.maximised[current] = true
}
async maximizeTerminal() {
this.panels.terminal.minimized = false
this.event.emit('change', this.panels)
this.emit('change', this.panels)
}
async resetSidePanel () {
this.event.emit('resetsidepanel')
const current = await this.call('sidePanel', 'currentFocus')

@ -0,0 +1,47 @@
import { Plugin } from '@remixproject/engine'
import { CreateChatCompletionResponse } from 'openai'
const _paq = (window._paq = window._paq || [])
const profile = {
name: 'openaigpt',
displayName: 'openaigpt',
description: 'openaigpt',
methods: ['message'],
events: [],
maintainedBy: 'Remix',
}
export class OpenAIGpt extends Plugin {
constructor() {
super(profile)
}
async message(prompt): Promise<CreateChatCompletionResponse> {
this.call('layout', 'maximizeTerminal')
this.call('terminal', 'log', 'Waiting for GPT answer...')
let result
try {
result = await (
await fetch('https://openai-gpt.remixproject.org', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
})
).json()
} catch (e) {
this.call('terminal', 'log', { type: 'typewritererror', value: `Unable to get a response ${e.message}` })
return
}
if (result && result.choices && result.choices.length) {
this.call('terminal', 'log', { type: 'typewritersuccess', value: result.choices[0].message.content })
} else {
this.call('terminal', 'log', { type: 'typewritersuccess', value: 'No response...' })
}
return result.data
}
}

@ -477,40 +477,68 @@ export class CodeParser extends Plugin {
*/
async definitionAtPosition(position: number) {
const nodes = await this.nodesAtPosition(position)
let nodeDefinition: any
const nodeDefinition = {
'ast': null,
'parser': null
}
let node: genericASTNode
if (nodes && nodes.length && !this.errorState) {
if (nodes && nodes.length) {
node = nodes[nodes.length - 1]
nodeDefinition = node
let astNodeDefinition = node
if (!isNodeDefinition(node)) {
nodeDefinition = (await this.declarationOf(node)) || node
astNodeDefinition = (await this.declarationOf(node)) || node
}
if (node.nodeType === 'ImportDirective') {
for (const key in this.nodeIndex.flatReferences) {
if (this.nodeIndex.flatReferences[key].id === node.sourceUnit) {
nodeDefinition = this.nodeIndex.flatReferences[key]
astNodeDefinition = this.nodeIndex.flatReferences[key]
}
}
}
return nodeDefinition
} else {
nodeDefinition.ast = astNodeDefinition
}
const astNodes = await this.antlrService.listAstNodes()
let parserNodeDefinition = null
if (astNodes && astNodes.length) {
for (const node of astNodes) {
if (node.range[0] <= position && node.range[1] >= position) {
if (nodeDefinition && nodeDefinition.range[0] < node.range[0]) {
nodeDefinition = node
if (parserNodeDefinition && parserNodeDefinition.range[0] < node.range[0]) {
parserNodeDefinition = node
}
if (!parserNodeDefinition) parserNodeDefinition = node
}
}
if (parserNodeDefinition && parserNodeDefinition.type && parserNodeDefinition.type === 'Identifier') {
const nodeForIdentifier = await this.findIdentifier(parserNodeDefinition)
if (nodeForIdentifier) parserNodeDefinition = nodeForIdentifier
}
if (!nodeDefinition) nodeDefinition = node
nodeDefinition.parser = parserNodeDefinition
}
/* if the AST node name & type is the same as the parser node name & type,
/ then we can assume that the AST node is the definition,
/ because the parser will always return most nodes it can find even with an error in the code
*/
if (nodeDefinition.ast && nodeDefinition.parser) {
if (nodeDefinition.ast.name === nodeDefinition.parser.name && nodeDefinition.ast.nodeType === nodeDefinition.parser.type) {
return nodeDefinition.ast
}else{
// if there is a difference and the compiler has compiled correctly assume the ast node is the definition
if(this.compilerService.errorState === false){
return nodeDefinition.ast
}
if (nodeDefinition && nodeDefinition.type && nodeDefinition.type === 'Identifier') {
const nodeForIdentifier = await this.findIdentifier(nodeDefinition)
if (nodeForIdentifier) nodeDefinition = nodeForIdentifier
}
return nodeDefinition
}
if (nodeDefinition.ast && !nodeDefinition.parser) {
return nodeDefinition.ast
}
return nodeDefinition.parser
}
async getContractNodes(contractName: string) {

@ -99,11 +99,11 @@ export default class CodeParserCompiler {
await this.clearDecorators(result.getSourceCode().sources)
}
if (!data.sources) return
if (data.sources && Object.keys(data.sources).length === 0) return
this.plugin.compilerAbstract = new CompilerAbstract('soljson', data, source, input)
this.errorState = false
this.plugin.nodeIndex = {
declarations: {},
flatReferences: {},
@ -113,7 +113,10 @@ export default class CodeParserCompiler {
this.plugin._buildIndex(data, source)
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult)
const extractedFiledNodes = this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult)
if(extractedFiledNodes) {
this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = extractedFiledNodes
}
await this.plugin.gasService.showGasEstimates()
this.plugin.emit('astFinished')
}

@ -42,7 +42,10 @@ export default class CodeParserGasService {
}
this.plugin.currentFile = await this.plugin.call('fileManager', 'file')
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = await this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult)
const extractedFiledNodes = await this.plugin._extractFileNodes(this.plugin.currentFile, this.plugin.compilerAbstract as unknown as lastCompilationResult)
if(extractedFiledNodes) {
this.plugin.nodeIndex.nodesPerFile[this.plugin.currentFile] = extractedFiledNodes
}
const gasEstimates = await this.getGasEstimates(this.plugin.currentFile)

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

@ -1,26 +1,30 @@
import {InjectedProviderDefaultBase} from './injected-provider-default'
export class InjectedL2Provider extends InjectedProviderDefaultBase {
export class InjectedCustomProvider extends InjectedProviderDefaultBase {
chainName: string
chainId: string
rpcUrls: Array<string>
nativeCurrency: Record<string, any>
blockExplorerUrls: Array<string>
constructor(profile: any, chainName: string, chainId: string, rpcUrls: Array<string>) {
constructor(profile: any, chainName: string, chainId: string, rpcUrls: Array<string>, nativeCurrency?: Record<string, any>, blockExplorerUrls?: Array<string>) {
super(profile)
this.chainName = chainName
this.chainId = chainId
this.rpcUrls = rpcUrls
this.nativeCurrency = nativeCurrency
this.blockExplorerUrls = blockExplorerUrls
}
async init() {
await super.init()
if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addL2Network(this.chainName, this.chainId, this.rpcUrls)
else throw new Error('Cannot add the L2 network to main injected provider')
if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addCustomNetwork(this.chainName, this.chainId, this.rpcUrls, this.nativeCurrency, this.blockExplorerUrls)
else throw new Error('Cannot add the custom network to main injected provider')
return {}
}
}
export const addL2Network = async (chainName: string, chainId: string, rpcUrls: Array<string>) => {
export const addCustomNetwork = async (chainName: string, chainId: string, rpcUrls: Array<string>, nativeCurrency?: Record<string, any>, blockExplorerUrls?: Array<string>) => {
try {
await (window as any).ethereum.request({
method: 'wallet_switchEthereumChain',
@ -30,15 +34,16 @@ export const addL2Network = async (chainName: string, chainId: string, rpcUrls:
// This error code indicates that the chain has not been added to MetaMask.
if (switchError.code === 4902) {
try {
await (window as any).ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
const paramsObj: Record<string, any> = {
chainId: chainId,
chainName: chainName,
rpcUrls: rpcUrls
rpcUrls: rpcUrls,
}
]
if (nativeCurrency) paramsObj.nativeCurrency = nativeCurrency
if (blockExplorerUrls) paramsObj.blockExplorerUrls = blockExplorerUrls
await (window as any).ethereum.request({
method: 'wallet_addEthereumChain',
params: [ paramsObj ]
})
await (window as any).ethereum.request({

@ -0,0 +1,30 @@
import * as packageJson from '../../../../../package.json'
import {InjectedCustomProvider} from './injected-custom-provider'
const profile = {
name: 'injected-ephemery-testnet-provider',
displayName: 'Injected Ephemery Testnet Provider',
kind: 'provider',
description: 'Injected Ephemery Testnet Provider',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class InjectedEphemeryTestnetProvider extends InjectedCustomProvider {
constructor() {
super(profile,
'Ephemery Testnet',
'',
['https://otter.bordel.wtf/erigon', 'https://eth.ephemeral.zeus.fyi'],
{
"name": "Ephemery ETH",
"symbol": "ETH",
"decimals": 18
},
[
'https://otter.bordel.wtf/',
'https://explorer.ephemery.dev/'
]
)
}
}

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

@ -0,0 +1,27 @@
import * as packageJson from '../../../../../package.json'
import { InjectedCustomProvider } from './injected-custom-provider'
const profile = {
name: 'injected-skale-chaos-testnet-provider',
displayName: 'Injected SKALE Chaos Testnet',
kind: 'provider',
description: 'Injected SKALE Chaos Testnet Provider',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class InjectedSKALEChaosTestnetProvider extends InjectedCustomProvider {
constructor () {
super(profile,
'SKALE Chaos Testnet',
'0x50877ed6',
['https://staging-v3.skalenodes.com/v1/staging-fast-active-bellatrix'],
{
"name": "sFUEL",
"symbol": "sFUEL",
"decimals": 18
}
)
}
}

@ -6,7 +6,7 @@ import * as packageJson from '../../../../../package.json'
const EventManager = require('../../lib/events')
const Recorder = require('../tabs/runTab/model/recorder.js')
const _paq = window._paq = window._paq || []
const _paq = (window._paq = window._paq || [])
const profile = {
name: 'udapp',
@ -20,7 +20,17 @@ const profile = {
maintainedBy: 'Remix',
permission: true,
events: ['newTransaction'],
methods: ['createVMAccount', 'sendTransaction', 'getAccounts', 'pendingTransactionsCount', 'getSettings', 'setEnvironmentMode', 'clearAllInstances', 'addInstance', 'resolveContractAndAddInstance']
methods: [
'createVMAccount',
'sendTransaction',
'getAccounts',
'pendingTransactionsCount',
'getSettings',
'setEnvironmentMode',
'clearAllInstances',
'addInstance',
'resolveContractAndAddInstance'
]
}
export class RunTab extends ViewPlugin {
@ -91,7 +101,11 @@ export class RunTab extends ViewPlugin {
}
render() {
return <div><RunTabUI plugin={this} /></div>
return (
<div>
<RunTabUI plugin={this} />
</div>
)
}
onReady(api) {
@ -119,6 +133,7 @@ export class RunTab extends ViewPlugin {
}
},
provider: {
async sendAsync (payload) {
return udapp.call(name, 'sendAsync', payload)
}
@ -129,10 +144,17 @@ export class RunTab extends ViewPlugin {
// basic injected
// if it's the trust wallet provider, we have a specific provider for that, see below
if (window && window.ethereum && !(window.ethereum.isTrustWallet || window.ethereum.selectedProvider?.isTrustWallet)) {
const displayNameInjected = `Injected Provider${(window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)) ?
window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet ? ' - Coinbase' :
window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' :
window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}`
const displayNameInjected = `Injected Provider${
window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)
? window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet
? ' - Coinbase'
: window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet
? ' - Brave'
: window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask
? ' - MetaMask'
: ''
: ''
}`
await addProvider('injected', displayNameInjected, true, false)
} else if (window && !window.ethereum) {
// we still add "injected" if there's no provider (just so it's visible to the user).
@ -158,6 +180,10 @@ export class RunTab extends ViewPlugin {
// wallet connect
await addProvider('walletconnect', 'WalletConnect', false, false)
// testnet
await addProvider('injected-ephemery-testnet-provider', 'Ephemery Testnet', true, false)
await addProvider('injected-skale-chaos-testnet-provider', 'SKALE Chaos Testnet', true, false)
// external provider
await addProvider('basic-http-provider', 'Custom - External Http Provider', false, false)
await addProvider('hardhat-provider', 'Dev - Hardhat Provider', false, false)

@ -2,34 +2,117 @@ import { PluginManager } from '@remixproject/engine'
import {EventEmitter} from 'events'
import {QueryParams} from '@remix-project/remix-lib'
import {IframePlugin} from '@remixproject/engine-web'
const _paq = window._paq = window._paq || []
const _paq = (window._paq = window._paq || [])
// requiredModule removes the plugin from the plugin manager list on UI
const requiredModules = [ // services + layout views + system views
'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'locale',
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy',
'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-trustwallet', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin',
const requiredModules = [
// services + layout views + system views
'manager',
'config',
'compilerArtefacts',
'compilerMetadata',
'contextualListener',
'editor',
'offsetToLineColumnConverter',
'network',
'theme',
'locale',
'fileManager',
'contentImport',
'blockchain',
'web3Provider',
'scriptRunner',
'fetchAndCompile',
'mainPanel',
'hiddenPanel',
'sidePanel',
'menuicons',
'filePanel',
'terminal',
'settings',
'pluginManager',
'tabs',
'udapp',
'dGitProvider',
'solidity',
'solidity-logic',
'gistHandler',
'layout',
'notification',
'permissionhandler',
'walkthrough',
'storage',
'restorebackupzip',
'link-libraries',
'deploy-libraries',
'openzeppelin-proxy',
'hardhat-provider',
'ganache-provider',
'foundry-provider',
'basic-http-provider',
'injected',
'injected-trustwallet',
'injected-optimism-provider',
'injected-arbitrum-one-provider',
'injected-ephemery-testnet-provider',
'injected-skale-chaos-testnet-provider',
'vm-custom-fork',
'vm-goerli-fork',
'vm-mainnet-fork',
'vm-sepolia-fork',
'vm-merge',
'vm-london',
'vm-berlin',
'vm-shanghai',
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener', 'solidity-script']
'compileAndRun',
'search',
'recorder',
'fileDecorator',
'codeParser',
'codeFormatter',
'solidityumlgen',
'contractflattener',
'solidity-script'
]
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither']
const loadLocalPlugins = ["doc-gen", "doc-viewer", "etherscan", "vyper", 'solhint', 'walletconnect']
const loadLocalPlugins = ['doc-gen', 'doc-viewer', 'etherscan', 'vyper', 'solhint', 'walletconnect', 'circuit-compiler']
const sensitiveCalls = {
'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'],
'contentImport': ['resolveAndSave'],
'web3Provider': ['sendAsync'],
fileManager: ['writeFile', 'copyFile', 'rename', 'copyDir'],
contentImport: ['resolveAndSave'],
web3Provider: ['sendAsync']
}
export function isNative(name) {
// nativePlugin allows to bypass the permission request
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting',
'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider',
'tabs', 'injected-arbitrum-one-provider', 'injected', 'doc-gen', 'doc-viewer']
const nativePlugins = [
'vyper',
'workshops',
'debugger',
'remixd',
'menuicons',
'solidity',
'solidity-logic',
'solidityStaticAnalysis',
'solidityUnitTesting',
'layout',
'notification',
'hardhat-provider',
'ganache-provider',
'foundry-provider',
'basic-http-provider',
'injected-optimism-provider',
'tabs',
'injected-arbitrum-one-provider',
'injected-skale-chaos-testnet-provider',
'injected-ephemery-testnet-provider',
'injected',
'doc-gen',
'doc-viewer'
]
return nativePlugins.includes(name) || requiredModules.includes(name)
}
@ -44,9 +127,7 @@ export function isNative(name) {
* @returns {boolean}
*/
export function canActivate(from, to) {
return ['ethdoc'].includes(from.name) ||
isNative(from.name) ||
(to && from && from.canActivate && from.canActivate.includes(to.name))
return ['ethdoc'].includes(from.name) || isNative(from.name) || (to && from && from.canActivate && from.canActivate.includes(to.name))
}
export class RemixAppManager extends PluginManager {
@ -72,10 +153,7 @@ export class RemixAppManager extends PluginManager {
async deactivatePlugin(name) {
const profile = await this.getProfile(name)
const [to, from] = [
profile,
await this.getProfile(this.requestFrom)
]
const [to, from] = [profile, await this.getProfile(this.requestFrom)]
if (this.canDeactivatePlugin(from, to)) {
if (profile.methods.includes('deactivate')) {
try {
@ -104,7 +182,10 @@ export class RemixAppManager extends PluginManager {
}
onPluginActivated(plugin) {
this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin)))
this.pluginLoader.set(
plugin,
this.actives.filter((plugin) => !this.isDependent(plugin))
)
this.event.emit('activate', plugin)
this.emit('activate', plugin)
if (!requiredModules.includes(plugin.name)) _paq.push(['trackEvent', 'pluginManager', 'activate', plugin.name])
@ -121,7 +202,10 @@ export class RemixAppManager extends PluginManager {
}
onPluginDeactivated(plugin) {
this.pluginLoader.set(plugin, this.actives.filter((plugin) => !this.isDependent(plugin)))
this.pluginLoader.set(
plugin,
this.actives.filter((plugin) => !this.isDependent(plugin))
)
this.event.emit('deactivate', plugin)
_paq.push(['trackEvent', 'pluginManager', 'deactivate', plugin.name])
}
@ -142,7 +226,7 @@ export class RemixAppManager extends PluginManager {
plugins = await res.json()
plugins = plugins.filter((plugin) => {
if (plugin.targets && Array.isArray(plugin.targets) && plugin.targets.length > 0) {
return (plugin.targets.includes('remix'))
return plugin.targets.includes('remix')
}
return true
})
@ -177,7 +261,7 @@ export class RemixAppManager extends PluginManager {
}
}
return plugins.map(plugin => {
return plugins.map((plugin) => {
if (plugin.name === testPluginName) plugin.url = testPluginUrl
return new IframePlugin(plugin)
})
@ -249,11 +333,15 @@ class PluginLoader {
const saved = actives.filter((name) => !this.donotAutoReload.includes(name))
localStorage.setItem('workspace', JSON.stringify(saved))
},
get: () => { return JSON.parse(localStorage.getItem('workspace')) }
get: () => {
return JSON.parse(localStorage.getItem('workspace'))
}
}
this.loaders.queryParams = {
set: () => { /* Do nothing. */ },
set: () => {
/* Do nothing. */
},
get: () => {
const {activate} = queryParams.get()
if (!activate) return []

@ -20,6 +20,7 @@ export class RemixEngine extends Engine {
if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 }
if (name === 'walletconnect') return { queueTimeout: 60000 * 4 }
if (name === 'udapp') return { queueTimeout: 60000 * 4 }
if (name === 'circuit-compiler') return { queueTimeout: 60000 * 4 }
return { queueTimeout: 10000 }
}

@ -1,11 +1,11 @@
# Team best practices
This document aims to address contibutors best practices of the following repositories:
This document aims to address contributors best practices of the following repositories:
- remix-ide https://github.com/ethereum/remix-project
- remix-plugin https://github.com/ethereum/remix-plugin
This document is not in its final version, **a team meeting which aim to address new/old best practices, feedback, workflows, all kind of issues related to how the team work together occurs every 2 weeks.**
This document is not in its final version, **a team meeting which aims to address new/old best practices, feedback, workflows, all kind of issues related to how the team work together occurs every 2 weeks.**
This document link to other specialised best practices (like coding best practices).
Related links:
@ -32,22 +32,22 @@ Related links:
- Daily standup (1pm CET - 11am GMT) for taking care of the current issues.
- A regular standup - each Tuesday (3pm CET - 1pm GMT) - which aim to
- A regular standup - each Tuesday (3pm CET - 1pm GMT) - which aims to
- Update every contributor on what others are doing.
- Update the prioritised issues / PRs list.
- Address little issues (possibly related to the current ongoing milestone).
- High level demo, explanation about specific points of the codebase or Ethereum related things.
- A milestone standup - scheduled before the beginning of each milestone, roughly on a monthly basis - which aim to define what will be included in the **next milestone** and who will work on what. This standup also help to set a clear long term vision.
- A milestone standup - scheduled before the beginning of each milestone, roughly on a monthly basis - which aims to define what will be included in the **next milestone** and who will work on what. This standup also help to set a clear long term vision.
- A retrospective standup - after each releases - which aim to talk about **best practices in general**: what is good, what is bad, how we can improve workflows.
- A retrospective standup - after each releases - which aims to talk about **best practices in general**: what is good, what is bad, how we can improve workflows.
- A tour standup - Just after a release or whenever it is needed - which aim to demo, **explain in details** features, bug fixes or any part of the codebase.
- A tour standup - Just after a release or whenever it is needed - which aims to demo, **explain in details** features, bug fixes or any part of the codebase.
### 2) Group meetings:
- When a story / bug fix is divided in parts, there should be a kickstart meeting with all the developers involved, so that all the devs have an good overview / understanding on:
- When a story / bug fix is divided in parts, there should be a kickstart meeting with all the developers involved, so that all the devs have a good overview / understanding on:
- How the story fits into the Ethereum tech.
- How the backend (if any) works / will work (could be a smart contract).
- How the frontend works / will work.
@ -72,7 +72,7 @@ Before starting coding, we should ensure all devs / contributors are aware of:
- Prioritised list of PRs / issues are tracked in a GitHub Project, Remix IDE issues are managed by a prioritized backlog.
- Every story can be executed by a single developer or a group of 2 or more developers (depending on the size and complexity)
- Each dev should take the part he/she feels the most confortable with.
- Each dev should take the part he/she feels the most comfortable with.
- Later progress and discussion is updated directly on the issue or pull request (github).
- When a developer or team decides on the story they want to work on (at the start of milestone for instance), they assign themselves to the issue.
- Documentation update should be done together with the story, or an issue with the label "documentation" has to be created.
@ -195,21 +195,21 @@ Before starting coding, we should ensure all devs / contributors are aware of:
### release process:
- We release an `x.0.0` if there's a fundamental change in our UX design, which means users will need to readapt the way they use the app
- after a week finishes, we publish/release a new version as **remix-beta.ethereum.org** and inform users so early adopters can test. after another week, when then next finished work is released as **remix-beta.ethereum.org**, the previous one becomes **remix.ethereum.org** and all users can start using it
- after a week finishes, we publish/release a new version as **remix-beta.ethereum.org** and inform users so early adopters can test. after another week, when the next finished work is released as **remix-beta.ethereum.org**, the previous one becomes **remix.ethereum.org** and all users can start using it
- a bot to automatically notify users about upcoming features on all channels whenever **remix-beta.ethereum.org** is updated
- in case it's a major version increase - this announcement should be specially marked so ppl can check early instead of being confronted with drastic changes when **remix.ethereum.org** updates
### maintenance:
- Setting up a "bug" time where we each take a bug for which:
- We feel confortable to deal with
- We feel comfortable to deal with
or
- We don't feel confortable but interested in fixing it - that will need the help of another dev
- We don't feel comfortable but interested in fixing it - that will need the help of another dev
### documentation:
- We set up a special day where we address all the necessary documentation work in a team effort.
- Change to markdown or gitbook
### support:
- Remix channel - we rotate and each day one or two are responsible for support (It would also be important to know for those of us who are contracted, how this can be billed.). If they don't know the answer they ping team member who they think could answer. That team member checks the support chat that day only if she/he is mentioned.
- We should have a FAQ where basic answer are written down so we can drop the link regularly in the channel
- We should have a FAQ where basic answers are written down so we can drop the link regularly in the channel
- People should be pointed to the appropriate `best practice.md` files by team members
### code best practices:

@ -2,9 +2,20 @@ import { PluginClient } from '@remixproject/plugin'
import {createClient} from '@remixproject/plugin-webview'
import {w3mConnectors, w3mProvider} from '@web3modal/ethereum'
import {createConfig, configureChains} from 'wagmi'
import { arbitrum, arbitrumGoerli, mainnet, polygon, polygonMumbai, optimism, optimismGoerli, Chain, goerli, sepolia } from 'viem/chains'
import {
arbitrum,
arbitrumGoerli,
mainnet,
polygon,
polygonMumbai,
optimism,
optimismGoerli,
Chain,
goerli,
sepolia
} from 'viem/chains'
import {EthereumClient} from '@web3modal/ethereum'
import EventManager from "events"
import EventManager from 'events'
import {PROJECT_ID} from './constant'
export class WalletConnectRemixClient extends PluginClient {
@ -18,7 +29,7 @@ export class WalletConnectRemixClient extends PluginClient {
super()
createClient(this)
this.internalEvents = new EventManager()
this.methods = ["sendAsync", "init", "deactivate"]
this.methods = ['sendAsync', 'init', 'deactivate']
this.onload()
}
@ -35,8 +46,20 @@ export class WalletConnectRemixClient extends PluginClient {
async initClient() {
try {
this.chains = [arbitrum, arbitrumGoerli, mainnet, polygon, polygonMumbai, optimism, optimismGoerli, goerli, sepolia]
const { publicClient } = configureChains(this.chains, [w3mProvider({ projectId: PROJECT_ID })])
this.chains = [
mainnet,
arbitrum,
arbitrumGoerli,
polygon,
polygonMumbai,
optimism,
optimismGoerli,
goerli,
sepolia
]
const {publicClient} = configureChains(this.chains, [
w3mProvider({projectId: PROJECT_ID})
])
this.wagmiConfig = createConfig({
autoConnect: false,
@ -45,7 +68,7 @@ export class WalletConnectRemixClient extends PluginClient {
})
this.ethereumClient = new EthereumClient(this.wagmiConfig, this.chains)
} catch (e) {
return console.error("Could not get a wallet connection", e)
return console.error('Could not get a wallet connection', e)
}
}
@ -68,19 +91,41 @@ export class WalletConnectRemixClient extends PluginClient {
})
}
sendAsync (data: { method: string, params: string, id: string }) {
return new Promise((resolve, reject) => {
if (this.wagmiConfig) {
this.wagmiConfig.publicClient.request(data).then((message) => {
resolve({"jsonrpc": "2.0", "result": message, "id": data.id})
}).catch((error) => {
reject(error)
})
async sendAsync(data: {method: string; params: string; id: string}) {
if (this.wagmiConfig.status === 'connected') {
if (data.method === 'eth_accounts') {
return {
jsonrpc: '2.0',
result: [this.wagmiConfig.data.account],
id: data.id
}
} else {
console.error(`Cannot make ${data.method} request. Remix client is not connect to walletconnect client`)
resolve({"jsonrpc": "2.0", "result": [], "id": data.id})
const provider = await this.wagmiConfig.connector.getProvider({
chainId: this.wagmiConfig.data.chain.id
})
if (provider.isMetaMask) {
return new Promise((resolve) => {
provider.sendAsync(data, (err, response) => {
if (err) {
console.error(err)
return resolve({jsonrpc: '2.0', result: [], id: data.id})
}
return resolve(response)
})
})
} else {
const message = await provider.request(data)
return {jsonrpc: '2.0', result: message, id: data.id}
}
}
} else {
console.error(
`Cannot make ${data.method} request. Remix client is not connected to walletconnect client`
)
return {jsonrpc: '2.0', result: [], id: data.id}
}
}
async deactivate() {

@ -1,6 +1,6 @@
{
"name": "@remix-project/ghaction-helper",
"version": "0.1.11",
"version": "0.1.12",
"description": "Solidity Tests GitHub Action Helper",
"main": "src/index.js",
"scripts": {
@ -19,17 +19,18 @@
},
"homepage": "https://github.com/ethereum/remix-project#readme",
"devDependencies": {
"@remix-project/remix-solidity": "^0.5.17",
"@remix-project/remix-solidity": "^0.5.18",
"@types/chai": "^4.3.4",
"typescript": "^4.9.3"
},
"dependencies": {
"@ethereum-waffle/chai": "^3.4.4",
"@remix-project/remix-simulator": "^0.2.31",
"@remix-project/remix-simulator": "^0.2.32",
"chai": "^4.3.7",
"ethers": "^5.7.2",
"web3": "^4.1.1"
},
"types": "./src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab"
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40"
}

@ -53,7 +53,7 @@ Details of modules are explained in [official remix-ide documentation](https://r
Please feel free to open an issue or a pull request.
In case you want to add some code, do have a look to our contribution guidelnes [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
In case you want to add some code, do have a look at our contribution guidelines [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
### License
MIT © 2018-21 Remix Team

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-analyzer",
"version": "0.5.40",
"version": "0.5.41",
"description": "Tool to perform static analysis on Solidity smart contracts",
"scripts": {
"test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts"
@ -25,8 +25,8 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-astwalker": "^0.0.61",
"@remix-project/remix-lib": "^0.5.38",
"@remix-project/remix-astwalker": "^0.0.62",
"@remix-project/remix-lib": "^0.5.39",
"async": "^2.6.2",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
@ -50,6 +50,6 @@
"typescript": "^3.7.5"
},
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab",
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40",
"main": "./src/index.js"
}

@ -35,7 +35,7 @@ astWalker.on("node", node => {
Please feel free to open an issue or a pull request.
In case you want to add some code, do have a look to our contribution guidelnes [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
In case you want to add some code, do have a look at our contribution guidelines [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
### License
MIT © 2018-21 Remix Team

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-astwalker",
"version": "0.0.61",
"version": "0.0.62",
"description": "Tool to walk through Solidity AST",
"main": "src/index.js",
"scripts": {
@ -37,7 +37,7 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.38",
"@remix-project/remix-lib": "^0.5.39",
"@types/tape": "^4.2.33",
"async": "^2.6.2",
"ethers": "^5.4.2",
@ -53,6 +53,6 @@
"tap-spec": "^5.0.0"
},
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab",
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40",
"types": "./src/index.d.ts"
}

@ -5,7 +5,7 @@
[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/ethereum/remix-project/tree/master/libs/remix-debug)
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/ethereum/remix-project/issues)
`@remix-project/remix-debug` is a tool to debug Ethereum transactions on different Remix environments (VM, testnet etc.). It works underneath Remix IDE "DEBUGGER" plugin which is used to analyse step-to-step executioon of a transaction to debug it.
`@remix-project/remix-debug` is a tool to debug Ethereum transactions on different Remix environments (VM, testnet etc.). It works underneath Remix IDE "DEBUGGER" plugin which is used to analyse step-to-step execution of a transaction to debug it.
### Installation
`@remix-project/remix-debug` is an NPM package and can be installed using NPM as:
@ -144,7 +144,7 @@ Some of the class details are as:
Please feel free to open an issue or a pull request.
In case you want to add some code, do have a look to our contribution guidelnes [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
In case you want to add some code, do have a look at our contribution guidelines [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
### License
MIT © 2018-21 Remix Team

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-debug",
"version": "0.5.31",
"version": "0.5.32",
"description": "Tool to debug Ethereum transactions",
"contributors": [
{
@ -26,10 +26,10 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-astwalker": "^0.0.61",
"@remix-project/remix-lib": "^0.5.38",
"@remix-project/remix-simulator": "^0.2.31",
"@remix-project/remix-solidity": "^0.5.17",
"@remix-project/remix-astwalker": "^0.0.62",
"@remix-project/remix-lib": "^0.5.39",
"@remix-project/remix-simulator": "^0.2.32",
"@remix-project/remix-solidity": "^0.5.18",
"ansi-gray": "^0.1.1",
"async": "^2.6.2",
"color-support": "^1.1.3",
@ -69,6 +69,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab",
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40",
"types": "./src/index.d.ts"
}

@ -19,7 +19,7 @@ export function setProvider (web3, url) {
export function web3DebugNode (network) {
const web3DebugNodes = {
Main: 'https://rpc.archivenode.io/e50zmkroshle2e2e50zm0044i7ao04ym',
Main: 'https://eth.getblock.io/68069907-1d3c-466e-a533-a943afd935c6/mainnet',
Rinkeby: 'https://remix-rinkeby.ethdevops.io',
Ropsten: 'https://remix-ropsten.ethdevops.io',
Goerli: 'https://remix-goerli.ethdevops.io',

@ -43,7 +43,7 @@
Please feel free to open an issue or a pull request.
In case you want to add some code, do have a look to our contribution guidelnes [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
In case you want to add some code, do have a look at our contribution guidelines [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
### License
MIT © 2018-21 Remix Team

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-lib",
"version": "0.5.38",
"version": "0.5.39",
"description": "Library to various Remix tools",
"contributors": [
{
@ -53,6 +53,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab",
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40",
"types": "./src/index.d.ts"
}

@ -86,7 +86,7 @@
Please feel free to open an issue or a pull request.
In case you want to add some code, do have a look to our contribution guidelnes [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
In case you want to add some code, do have a look at our contribution guidelines [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
### License
MIT © 2018-21 Remix Team

@ -26,14 +26,19 @@ program
.option('-c, --coinbase [coinbase]', 'specify coinbase', '0x0000000000000000000000000000000000000000')
.option('--rpc', 'run rpc server only', true)
.option('--details', 'display payloads for every requests and their responses', false)
.action(() => {
.action((option) => {
console.log('coinbase: ', option.coinbase)
console.log('rpc: ', option.rpc)
console.log('details: ', option.details)
console.log('host: ', option.ip)
console.log('port: ', option.port)
const Server = require('../src/server')
const server = new Server({
coinbase: program.coinbase,
rpc: program.rpc,
logDetails: program.details
coinbase: option.coinbase,
rpc: option.rpc,
logDetails: option.details
})
server.start(program.host, program.port)
server.start(option.ip, option.port)
})
program.parse(process.argv)

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-simulator",
"version": "0.2.31",
"version": "0.2.32",
"description": "Ethereum IDE and tools for the web",
"contributors": [
{
@ -22,7 +22,7 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.38",
"@remix-project/remix-lib": "^0.5.39",
"ansi-gray": "^0.1.1",
"async": "^3.1.0",
"body-parser": "^1.18.2",
@ -68,6 +68,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab",
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40",
"types": "./src/index.d.ts"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-solidity",
"version": "0.5.17",
"version": "0.5.18",
"description": "Tool to load and run Solidity compiler",
"main": "src/index.js",
"types": "src/index.d.ts",
@ -19,7 +19,7 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.38",
"@remix-project/remix-lib": "^0.5.39",
"async": "^2.6.2",
"eslint-scope": "^5.0.0",
"ethers": "^5.4.2",
@ -57,5 +57,5 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme",
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab"
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40"
}

@ -81,12 +81,15 @@ export class Compiler {
* @param missingInputs missing import file path list
*/
internalCompile(files: Source, missingInputs?: string[]): void {
internalCompile(files: Source, missingInputs?: string[], timeStamp?: number): void {
if(timeStamp != this.state.compilationStartTime && this.state.compilerRetriggerMode == CompilerRetriggerMode.retrigger ) {
return
}
this.gatherImports(files, missingInputs, (error, input) => {
if (error) {
this.state.lastCompilationResult = null
this.event.trigger('compilationFinished', [false, { error: { formattedMessage: error, severity: 'error' } }, files, input, this.state.currentVersion])
} else if (this.state.compileJSON && input) { this.state.compileJSON(input) }
} else if (this.state.compileJSON && input) { this.state.compileJSON(input, timeStamp) }
})
}
@ -100,7 +103,7 @@ export class Compiler {
this.state.target = target
this.state.compilationStartTime = new Date().getTime()
this.event.trigger('compilationStarted', [])
this.internalCompile(files)
this.internalCompile(files, null, this.state.compilationStartTime)
}
/**
@ -157,7 +160,7 @@ export class Compiler {
* @param source Source
*/
onCompilationFinished(data: CompilationResult, missingInputs?: string[], source?: SourceWithTarget, input?: string, version?: string): void {
onCompilationFinished(data: CompilationResult, missingInputs?: string[], source?: SourceWithTarget, input?: string, version?: string, timeStamp?: number): void {
let noFatalErrors = true // ie warnings are ok
const checkIfFatalError = (error: CompilationError) => {
@ -173,7 +176,7 @@ export class Compiler {
this.event.trigger('compilationFinished', [false, data, source, input, version])
} else if (missingInputs !== undefined && missingInputs.length > 0 && source && source.sources) {
// try compiling again with the new set of inputs
this.internalCompile(source.sources, missingInputs)
this.internalCompile(source.sources, missingInputs, timeStamp)
} else {
data = this.updateInterface(data)
if (source) {
@ -292,6 +295,7 @@ export class Compiler {
this.state.worker.addEventListener('message', (msg: Record<'data', MessageFromWorker>) => {
const data: MessageFromWorker = msg.data
if (this.state.compilerRetriggerMode == CompilerRetriggerMode.retrigger && data.timestamp !== this.state.compilationStartTime) {
// drop message from previous compilation
return
}
switch (data.cmd) {
@ -312,7 +316,7 @@ export class Compiler {
sources = jobs[data.job].sources
delete jobs[data.job]
}
this.onCompilationFinished(result, data.missingInputs, sources, data.input, this.state.currentVersion)
this.onCompilationFinished(result, data.missingInputs, sources, data.input, this.state.currentVersion, data.timestamp)
}
break
}
@ -325,7 +329,7 @@ export class Compiler {
this.onCompilationFinished({ error: { formattedMessage } })
})
this.state.compileJSON = (source: SourceWithTarget) => {
this.state.compileJSON = (source: SourceWithTarget, timeStamp: number) => {
if (source && source.sources) {
const { optimize, runs, evmVersion, language, useFileConfiguration, configFileContent } = this.state
jobs.push({ sources: source })
@ -342,12 +346,11 @@ export class Compiler {
return
}
this.state.worker.postMessage({
cmd: 'compile',
job: jobs.length - 1,
input: input,
timestamp: this.state.compilationStartTime
timestamp: timeStamp
})
}
}

@ -160,7 +160,7 @@ export enum CompilerRetriggerMode {
}
export interface CompilerState {
compileJSON: ((input: SourceWithTarget) => void) | null,
compileJSON: ((input: SourceWithTarget, timeStamp?: number) => void) | null,
worker: any,
currentVersion: string| null| undefined,
compilerLicense: string| null

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-tests",
"version": "0.2.31",
"version": "0.2.32",
"description": "Tool to test Solidity smart contracts",
"main": "src/index.js",
"types": "./src/index.d.ts",
@ -41,9 +41,9 @@
"@ethereumjs/tx": "^4.1.1",
"@ethereumjs/util": "^8.0.5",
"@ethereumjs/vm": "^6.4.1",
"@remix-project/remix-lib": "^0.5.38",
"@remix-project/remix-simulator": "^0.2.31",
"@remix-project/remix-solidity": "^0.5.17",
"@remix-project/remix-lib": "^0.5.39",
"@remix-project/remix-simulator": "^0.2.32",
"@remix-project/remix-solidity": "^0.5.18",
"@remix-project/remix-url-resolver": "^0.0.42",
"ansi-gray": "^0.1.1",
"async": "^2.6.0",
@ -78,5 +78,5 @@
"typescript": "^3.3.1"
},
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab"
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40"
}

@ -1,7 +1,7 @@
import { Monaco } from "@monaco-editor/react"
import monaco from "../../types/monaco"
import { EditorUIProps } from "../remix-ui-editor"
import { default as fixesList } from "./quickfixes"
import {Monaco} from '@monaco-editor/react'
import monaco from '../../types/monaco'
import {EditorUIProps} from '../remix-ui-editor'
import {default as fixesList} from './quickfixes'
export class RemixCodeActionProvider implements monaco.languages.CodeActionProvider {
props: EditorUIProps
@ -22,27 +22,80 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
let fixes: Record<string, any>[], msg: string
let isOldAST: boolean = false
const errStrings: string[] = Object.keys(fixesList)
const errStr:string = errStrings.find(es => error.message.includes(es))
const errStr: string = errStrings.find((es) => error.message.includes(es))
if (errStr) {
fixes = fixesList[errStr]
const cursorPosition: number = this.props.editorAPI.getHoverPosition({lineNumber: error.startLineNumber, column: error.startColumn})
const cursorPosition: number = this.props.editorAPI.getHoverPosition({
lineNumber: error.startLineNumber,
column: error.startColumn
})
const nodeAtPosition = await this.props.plugin.call('codeParser', 'definitionAtPosition', cursorPosition)
// Check if a function is hovered
if (nodeAtPosition && nodeAtPosition.nodeType === "FunctionDefinition") {
if (nodeAtPosition && nodeAtPosition.nodeType === 'FunctionDefinition') {
// Identify type of AST node
if (nodeAtPosition.parameters && !Array.isArray(nodeAtPosition.parameters) && Array.isArray(nodeAtPosition.parameters.parameters))
isOldAST = true
if (nodeAtPosition.parameters && !Array.isArray(nodeAtPosition.parameters) && Array.isArray(nodeAtPosition.parameters.parameters)) isOldAST = true
const paramNodes = isOldAST ? nodeAtPosition.parameters.parameters : nodeAtPosition.parameters
for (const fix of fixes) {
msg = paramNodes.length
? await this.fixForMethodWithParams(model, paramNodes, fix, error, isOldAST)
: await this.fixForMethodWithoutParams(model, nodeAtPosition, fix, error, isOldAST)
this.addQuickFix(actions, error, model.uri, {title: fix.title, range: fix.range, text: msg})
this.addQuickFix(actions, error, model.uri, {
id: fix.id,
title: fix.title,
range: fix.range,
text: msg
})
}
} else {
for (const fix of fixes) {
if (fix && nodeAtPosition && fix.nodeType !== nodeAtPosition.nodeType) continue
else this.addQuickFix(actions, error, model.uri, {title: fix.title, range: fix.range || error, text: fix.message})
if (fix && nodeAtPosition && fix.nodeType && fix.nodeType !== nodeAtPosition.nodeType) continue
switch (fix.id) {
case 2: {
// To add specific pragma based on error
const startIndex = error.message.indexOf('pragma')
const endIndex = error.message.indexOf(';')
const msg = error.message.substring(startIndex, endIndex + 1)
this.addQuickFix(actions, error, model.uri, {
title: fix.title,
range: fix.range,
text: msg
})
break
}
case 8: {
// To add `abstract` in the contract
const lineContent: string = model.getValueInRange(error)
this.addQuickFix(actions, error, model.uri, {
title: fix.title,
range: error,
text: fix.message + lineContent
})
break
}
case 9.1:
case 9.2:
case 10.1:
case 10.2:
case 10.3:
case 11.1:
case 11.2: {
// To add data location in constructor params, function params and variables
const lineContent: string = model.getValueInRange(error)
const words = lineContent.split(' ')
this.addQuickFix(actions, error, model.uri, {
title: fix.title,
range: error,
text: words[0] + fix.message + words[1]
})
break
}
default:
this.addQuickFix(actions, error, model.uri, {
title: fix.title,
range: fix.range || error,
text: fix.message
})
}
}
}
}
@ -62,16 +115,17 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
* @param fix details of quick fix to apply
*/
addQuickFix(actions: monaco.languages.CodeAction[], error: monaco.editor.IMarkerData, uri: monaco.Uri, fix: Record<string, any>) {
const {title, range, text} = fix
const {id, title, range, text} = fix
actions.push({
title,
diagnostics: [error],
kind: "quickfix",
kind: 'quickfix',
edit: {
edits: [
{
resource: uri,
edit: { range, text }
textEdit: {range, text},
versionId: undefined
}
]
},
@ -88,7 +142,13 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
* @param isOldAST true, if AST node contains legacy fields
* @returns message to be placed as quick fix
*/
async fixForMethodWithParams(model: monaco.editor.ITextModel, paramNodes: Record<string, any>[], fix: Record<string, any>, error: monaco.editor.IMarkerData, isOldAST: boolean): Promise<string> {
async fixForMethodWithParams(
model: monaco.editor.ITextModel,
paramNodes: Record<string, any>[],
fix: Record<string, any>,
error: monaco.editor.IMarkerData,
isOldAST: boolean
): Promise<string> {
let lastParamEndLoc: Record<string, any>, fixLineNumber: number, msg: string
// Get last function parameter node
const lastParamNode: Record<string, any> = paramNodes[paramNodes.length - 1]
@ -103,12 +163,13 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
fixLineNumber = lastParamEndLoc.line
}
const lineContent: string = model.getLineContent(fixLineNumber)
if (fix.id === 5 && lineContent.includes(' view '))
msg = lineContent.replace('view', 'pure')
else if (isOldAST)
msg = lineContent.substring(0, lastParamEndLoc.column + 2) + fix.message + lineContent.substring(lastParamEndLoc.column + 1, lineContent.length)
if (fix.id === 5 && lineContent.includes(' view ')) msg = lineContent.replace('view', 'pure')
else if (isOldAST) msg = lineContent.substring(0, lastParamEndLoc.column + 2) + fix.message + lineContent.substring(lastParamEndLoc.column + 1, lineContent.length)
else
msg = lineContent.substring(0, lastParamEndLoc.column + lastParamNode.name.length + 2) + fix.message + lineContent.substring(lastParamEndLoc.column + lastParamNode.name.length + 1, lineContent.length)
msg =
lineContent.substring(0, lastParamEndLoc.column + lastParamNode.name.length + 2) +
fix.message +
lineContent.substring(lastParamEndLoc.column + lastParamNode.name.length + 1, lineContent.length)
fix.range = {
startLineNumber: fixLineNumber,
@ -128,7 +189,13 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
* @param isOldAST true, if AST node contains legacy fields
* @returns message to be placed as quick fix
*/
async fixForMethodWithoutParams(model: monaco.editor.ITextModel, nodeAtPosition: Record<string, any>, fix: Record<string, any>, error: monaco.editor.IMarkerData, isOldAST: boolean): Promise<string> {
async fixForMethodWithoutParams(
model: monaco.editor.ITextModel,
nodeAtPosition: Record<string, any>,
fix: Record<string, any>,
error: monaco.editor.IMarkerData,
isOldAST: boolean
): Promise<string> {
let fixLineNumber: number, msg: string
if (isOldAST) {
const location: Record<string, any> = await this.props.plugin.call('codeParser', 'getLineColumnOfNode', nodeAtPosition)
@ -140,8 +207,7 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
if (fix.id === 5 && lineContent.includes(' view ')) {
msg = lineContent.replace('view', 'pure')
} else
msg = lineContent.substring(0, i + 3) + fix.message + lineContent.substring(i + 3, lineContent.length)
} else msg = lineContent.substring(0, i + 3) + fix.message + lineContent.substring(i + 3, lineContent.length)
fix.range = {
startLineNumber: fixLineNumber,

@ -521,6 +521,20 @@ export function GetGlobalVariable(range: monacoTypes.IRange, monaco): monacoType
export function GetGlobalFunctions(range: monacoTypes.IRange, monaco): monacoTypes.languages.CompletionItem[] {
return [
{
label: 'fallback',
kind: monaco.languages.CompletionItemKind.Function,
insertText: 'fallback() ${1:external} ${2:payable} { }',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range
},
{
label: 'receive',
kind: monaco.languages.CompletionItemKind.Function,
insertText: 'receive() ${1:external} ${2:payable} { }',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range
},
{
detail: 'assert(bool condition): throws if the condition is not met - to be used for internal errors.',
insertText: 'assert(${1:condition});',

@ -55,7 +55,6 @@ export default {
{
id: 2,
title: 'Add Solidity pragma',
message: 'pragma solidity ^0.*.*;',
nodeType: 'PragmaDirective',
range: {
startLineNumber: 2,
@ -106,5 +105,86 @@ export default {
message: 'pure ',
nodeType: 'FunctionDefinition'
}
],
'TypeError: Trying to override non-virtual function. Did you forget to add "virtual"':[
{
id: 6,
title: "Add 'virtual' specifier",
message: 'virtual',
nodeType: 'FunctionDefinition'
}
],
'TypeError: Overriding function is missing "override" specifier':[
{
id: 7,
title: "Add 'override' specifier",
message: 'override',
nodeType: 'FunctionDefinition'
}
],
'should be marked as abstract': [
{
id: 8,
title: "Add 'abstract' to contract",
message: 'abstract ',
nodeType: 'ContractDefinition'
}
],
'TypeError: Data location must be "storage" or "memory" for constructor parameter, but none was given.': [
{
id: 9.1,
title: "Add 'storage' to param",
message: ' storage '
},
{
id: 9.2,
title: "Add 'memory' to param",
message: ' memory '
}
],
'TypeError: Data location must be "storage", "memory" or "calldata" for variable, but none was given.': [
{
id: 10.1,
title: "Add 'storage' to variable",
message: ' storage '
},
{
id: 10.2,
title: "Add 'memory' to variable",
message: ' memory '
},
{
id: 10.3,
title: "Add 'calldata' to variable",
message: ' calldata '
}
],
'TypeError: Data location must be "memory" or "calldata" for parameter in function, but none was given.': [
{
id: 11.1,
title: "Add 'memory' to param",
message: ' memory '
},
{
id: 11.2,
title: "Add 'calldata' to param",
message: ' calldata '
}
],
'SyntaxError: No visibility specified. Did you intend to add "external': [
{
id: 12,
title: "Add visibility 'external'",
message: 'external ',
nodeType: 'FunctionDefinition'
}
],
'DeclarationError: Receive ether function must be payable, but is "nonpayable".': [
{
id: 13,
title: "Make function 'payable'",
message: 'payable ',
nodeType: 'FunctionDefinition'
}
]
}

@ -1,9 +1,9 @@
.hover-row {
white-space: pre;
margin-left : 10px;
background : var(--light);
font-weight : bold;
font-family : monospace;
font-size: smaller;
padding : 10px;
border-radius : 10px;
height: auto;
@ -26,3 +26,7 @@
filter: opacity(0.5);
font-weight: bolder;
}
div.monaco-list-row > span.title {
margin-top: 12px;
}

@ -18,12 +18,13 @@ import {RemixDefinitionProvider} from './providers/definitionProvider'
import { RemixCodeActionProvider } from './providers/codeActionProvider'
import './remix-ui-editor.css'
import { circomLanguageConfig, circomTokensProvider } from './syntaxes/circom'
import { IPosition } from 'monaco-editor'
enum MarkerSeverity {
Hint = 1,
Info = 2,
Warning = 4,
Error = 8
Error = 8,
}
type sourceAnnotation = {
@ -108,6 +109,7 @@ export type EditorAPIType = {
keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
addErrorMarker: (errors: errorMarker[], from: string) => void
clearErrorMarkers: (sources: string[] | { [fileName: string]: any }, from: string) => void
getPositionAt: (offset: number) => monacoTypes.IPosition
}
/* eslint-disable-next-line */
@ -151,6 +153,7 @@ export const EditorUI = (props: EditorUIProps) => {
const pasteCodeRef = useRef(false)
const editorRef = useRef(null)
const monacoRef = useRef<Monaco>(null)
const currentFunction = useRef('')
const currentFileRef = useRef('')
const currentUrlRef = useRef('')
// const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor
@ -274,7 +277,7 @@ export const EditorUI = (props: EditorUIProps) => {
// returns
{ token: 'keyword.returns', foreground: greenColor },
{token: 'keyword.return', foreground: greenColor}
{ token: 'keyword.return', foreground: greenColor },
],
colors: {
// see https://code.visualstudio.com/api/references/theme-color for more settings
@ -284,7 +287,7 @@ export const EditorUI = (props: EditorUIProps) => {
'editorSuggestWidget.selectedForeground': textColor,
'editorSuggestWidget.highlightForeground': primaryColor,
'editorSuggestWidget.focusHighlightForeground': infoColor,
'editor.lineHighlightBorder': secondaryColor,
'editor.lineHighlightBorder': textbackground,
'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor,
'editorGutter.background': lightColor,
//'editor.selectionHighlightBackground': secondaryColor,
@ -293,8 +296,8 @@ export const EditorUI = (props: EditorUIProps) => {
'menu.background': textbackground,
'menu.selectionBackground': secondaryColor,
'menu.selectionForeground': textColor,
'menu.selectionBorder': secondaryColor
}
'menu.selectionBorder': secondaryColor,
},
})
monacoRef.current.editor.setTheme(themeName)
}
@ -312,7 +315,7 @@ export const EditorUI = (props: EditorUIProps) => {
const file = editorModelsState[props.currentFile]
editorRef.current.setModel(file.model)
editorRef.current.updateOptions({
readOnly: editorModelsState[props.currentFile].readOnly
readOnly: editorModelsState[props.currentFile].readOnly,
})
if (file.language === 'sol') {
monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity')
@ -336,10 +339,10 @@ export const EditorUI = (props: EditorUIProps) => {
options: {
isWholeLine: false,
glyphMarginHoverMessage: {
value: (decoration.from ? `from ${decoration.from}:\n` : '') + decoration.text
value: (decoration.from ? `from ${decoration.from}:\n` : '') + decoration.text,
},
glyphMarginClassName: `fal fa-exclamation-square text-${decoration.type === 'error' ? 'danger' : decoration.type === 'warning' ? 'warning' : 'info'}`,
},
glyphMarginClassName: `fal fa-exclamation-square text-${decoration.type === 'error' ? 'danger' : decoration.type === 'warning' ? 'warning' : 'info'}`
}
}
}
if (typeOfDecoration === 'markerPerFile') {
@ -362,8 +365,8 @@ export const EditorUI = (props: EditorUIProps) => {
),
options: {
isWholeLine,
inlineClassName: `${isWholeLine ? 'alert-info' : 'inline-class'} border-0 highlightLine${decoration.position.start.line + 1}`
}
inlineClassName: `${isWholeLine ? 'alert-info' : 'inline-class'} border-0 highlightLine${decoration.position.start.line + 1}`,
},
}
}
if (typeOfDecoration === 'lineTextPerFile') {
@ -379,11 +382,11 @@ export const EditorUI = (props: EditorUIProps) => {
options: {
after: {
content: ` ${lineTextDecoration.content}`,
inlineClassName: `${lineTextDecoration.className}`
inlineClassName: `${lineTextDecoration.className}`,
},
afterContentClassName: `${lineTextDecoration.afterContentClassName}`,
hoverMessage: lineTextDecoration.hoverMessage
}
hoverMessage: lineTextDecoration.hoverMessage,
},
}
}
if (typeOfDecoration === 'lineTextPerFile') {
@ -399,11 +402,11 @@ export const EditorUI = (props: EditorUIProps) => {
options: {
after: {
content: ` ${lineTextDecoration.content}`,
inlineClassName: `${lineTextDecoration.className}`
inlineClassName: `${lineTextDecoration.className}`,
},
afterContentClassName: `${lineTextDecoration.afterContentClassName}`,
hoverMessage: lineTextDecoration.hoverMessage
}
hoverMessage: lineTextDecoration.hoverMessage,
},
}
}
}
@ -413,7 +416,7 @@ export const EditorUI = (props: EditorUIProps) => {
if (!model)
return {
currentDecorations: [],
registeredDecorations: []
registeredDecorations: [],
}
const decorations = []
const newRegisteredDecorations = []
@ -427,7 +430,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
return {
currentDecorations: model.deltaDecorations(currentDecorations, decorations),
registeredDecorations: newRegisteredDecorations
registeredDecorations: newRegisteredDecorations,
}
}
@ -435,7 +438,7 @@ export const EditorUI = (props: EditorUIProps) => {
const model = editorModelsState[filePath]?.model
if (!model)
return {
currentDecorations: []
currentDecorations: [],
}
const decorations = []
if (registeredDecorations) {
@ -446,7 +449,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
}
return {
currentDecorations: model.deltaDecorations(currentDecorations, decorations)
currentDecorations: model.deltaDecorations(currentDecorations, decorations),
}
}
@ -456,7 +459,7 @@ export const EditorUI = (props: EditorUIProps) => {
const monacoDecoration = convertToMonacoDecoration(decoration, typeOfDecoration)
return {
currentDecorations: model.deltaDecorations([], [monacoDecoration]),
registeredDecorations: [{value: decoration, type: typeOfDecoration}]
registeredDecorations: [{ value: decoration, type: typeOfDecoration }],
}
}
@ -477,7 +480,7 @@ export const EditorUI = (props: EditorUIProps) => {
const errorServerityMap = {
error: MarkerSeverity.Error,
warning: MarkerSeverity.Warning,
info: MarkerSeverity.Info
info: MarkerSeverity.Info,
}
if (model) {
const markerData: monacoTypes.editor.IMarkerData = {
@ -486,7 +489,7 @@ export const EditorUI = (props: EditorUIProps) => {
startColumn: (error.position.start && error.position.start.column) || 0,
endLineNumber: (error.position.end && error.position.end.line) || 0,
endColumn: (error.position.end && error.position.end.column) || 0,
message: error.message
message: error.message,
}
if (!allMarkersPerfile[filePath]) {
allMarkersPerfile[filePath] = []
@ -548,7 +551,7 @@ export const EditorUI = (props: EditorUIProps) => {
props.editorAPI.getFontSize = () => {
if (!editorRef.current) return
return editorRef.current.getOption(43).fontSize
return editorRef.current.getOption(51)
}
;(window as any).addRemixBreakpoint = (position) => {
// make it available from e2e testing...
@ -571,9 +574,9 @@ export const EditorUI = (props: EditorUIProps) => {
range: new monacoRef.current.Range(position.lineNumber, 1, position.lineNumber, 1),
options: {
isWholeLine: false,
glyphMarginClassName: 'fas fa-circle text-info'
}
}
glyphMarginClassName: 'fas fa-circle text-info',
},
},
]
)
prevState[currentFile][position.lineNumber] = decorationIds[0]
@ -625,7 +628,7 @@ export const EditorUI = (props: EditorUIProps) => {
</div>
</div>
</div>
)
),
}
props.plugin.call('notification', 'alert', modalContent)
pasteCodeRef.current = true
@ -634,10 +637,10 @@ export const EditorUI = (props: EditorUIProps) => {
// zoomin zoomout
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize + 1})
editor.updateOptions({fontSize: editor.getOption(51) + 1})
})
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize - 1})
editor.updateOptions({fontSize: editor.getOption(51) - 1})
})
// add context menu items
@ -648,10 +651,10 @@ export const EditorUI = (props: EditorUIProps) => {
contextMenuGroupId: 'zooming', // create a new grouping
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal,
],
run: () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize + 1})
editor.updateOptions({fontSize: editor.getOption(51) + 1})
}
}
const zoomOutAction = {
@ -661,10 +664,10 @@ export const EditorUI = (props: EditorUIProps) => {
contextMenuGroupId: 'zooming', // create a new grouping
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus,
],
run: () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize - 1})
editor.updateOptions({fontSize: editor.getOption(51) - 1})
}
}
const formatAction = {
@ -674,12 +677,48 @@ export const EditorUI = (props: EditorUIProps) => {
contextMenuGroupId: 'formatting', // create a new grouping
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyF
monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyF,
],
run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
await props.plugin.call('codeFormatter', 'format', file)
},
}
let gptGenerateDocumentationAction
const executeGptGenerateDocumentationAction = {
id: 'generateDocumentation',
label: 'Generate documentation for this function',
contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [],
run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file)
const message = `
solidity code: ${content}
Generate the documentation for the function ${currentFunction.current} using the Doxygen style syntax
`
await props.plugin.call('openaigpt', 'message', message)
},
}
let gptExplainFunctionAction
const executegptExplainFunctionAction = {
id: 'explainFunction',
label: 'Explain this function',
contextMenuOrder: 1, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [],
run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file)
const message = `
solidity code: ${content}
Explain the function ${currentFunction.current}
`
await props.plugin.call('openaigpt', 'message', message)
},
}
const freeFunctionCondition = editor.createContextKey('freeFunctionCondition', false)
@ -692,7 +731,7 @@ export const EditorUI = (props: EditorUIProps) => {
precondition: 'freeFunctionCondition',
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR
monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR,
],
run: async () => {
const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin)
@ -708,12 +747,14 @@ export const EditorUI = (props: EditorUIProps) => {
} else {
props.plugin.call('notification', 'toast', 'Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.')
}
}
},
}
editor.addAction(formatAction)
editor.addAction(zoomOutAction)
editor.addAction(zoominAction)
freeFunctionAction = editor.addAction(executeFreeFunctionAction)
gptGenerateDocumentationAction = editor.addAction(executeGptGenerateDocumentationAction)
gptExplainFunctionAction = editor.addAction(executegptExplainFunctionAction)
// we have to add the command because the menu action isn't always available (see onContextMenuHandlerForFreeFunction)
editor.addCommand(monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, () => executeFreeFunctionAction.run())
@ -725,6 +766,15 @@ export const EditorUI = (props: EditorUIProps) => {
freeFunctionAction.dispose()
freeFunctionAction = null
}
if (gptGenerateDocumentationAction) {
gptGenerateDocumentationAction.dispose()
gptGenerateDocumentationAction = null
}
if (gptExplainFunctionAction) {
gptExplainFunctionAction.dispose()
gptExplainFunctionAction = null
}
const file = await props.plugin.call('fileManager', 'getCurrentFile')
if (!file.endsWith('.sol')) {
freeFunctionCondition.set(false)
@ -736,6 +786,14 @@ export const EditorUI = (props: EditorUIProps) => {
executeFreeFunctionAction.label = `Run the free function "${freeFunctionNode.name}" in the Remix VM`
freeFunctionAction = editor.addAction(executeFreeFunctionAction)
}
const functionImpl = nodesAtPosition.find((node) => node.kind === 'function')
if (functionImpl) {
currentFunction.current = functionImpl.name
executeGptGenerateDocumentationAction.label = `Generate documentation for the function "${functionImpl.name}"`
gptGenerateDocumentationAction = editor.addAction(executeGptGenerateDocumentationAction)
executegptExplainFunctionAction.label = `Explain the function "${functionImpl.name}"`
gptExplainFunctionAction = editor.addAction(executegptExplainFunctionAction)
}
freeFunctionCondition.set(!!freeFunctionNode)
}
contextmenu._onContextMenu = (...args) => {
@ -756,7 +814,7 @@ export const EditorUI = (props: EditorUIProps) => {
editor.revealRange(input.options.selection)
editor.setPosition({
column: input.options.selection.startColumn,
lineNumber: input.options.selection.startLineNumber
lineNumber: input.options.selection.startLineNumber,
})
}
} catch (e) {
@ -816,7 +874,7 @@ export const EditorUI = (props: EditorUIProps) => {
beforeMount={handleEditorWillMount}
options={{
glyphMargin: true,
readOnly: (!editorRef.current || !props.currentFile) && editorModelsState[props.currentFile]?.readOnly
readOnly: (!editorRef.current || !props.currentFile) && editorModelsState[props.currentFile]?.readOnly,
}}
defaultValue={defaultEditorValue}
/>

File diff suppressed because it is too large Load Diff

@ -134,3 +134,11 @@ export const upgradeReportMsg = (report: LayoutCompatibilityReport) => (
<div className="pl-4 text-danger">{report.explain()}</div>
</div>
)
export function RenderIf({ condition, children }: { condition: boolean, children: JSX.Element }) {
return condition ? children : null
}
export function RenderIfNot({ condition, children }: { condition: boolean, children: JSX.Element }) {
return condition ? null : children
}

@ -73,7 +73,7 @@ function HomeTabFeaturedPlugins({plugin}: HomeTabFeaturedPluginsProps) {
_paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'sourcify'])
}
const startCookbook = async () => {
await plugin.appManager.activatePlugin(['cookbook.dev'])
await plugin.appManager.activatePlugin(['cookbookdev'])
plugin.verticalIcons.select('cookbook.dev')
_paq.push(['trackEvent', 'hometabActivate', 'userActivate', 'cookbook.dev'])
}

@ -67,6 +67,21 @@ export const Renderer = ({message, opt = {}, plugin}: RendererProps) => {
}
}
const askGtp = async () => {
try {
const content = await plugin.call('fileManager', 'readFile', editorOptions.errFile)
const message = `
solidity code: ${content}
error message: ${messageText}
explain why the error occurred and how to fix it.
`
await plugin.call('openaigpt', 'message', message)
} catch (err) {
console.error('unable to askGtp')
console.error(err)
}
}
return (
<>
{messageText && !close && (
@ -82,6 +97,7 @@ export const Renderer = ({message, opt = {}, plugin}: RendererProps) => {
<i className="fas fa-times"></i>
</div>
<CopyToClipboard content={messageText} className={` p-0 m-0 far fa-copy ${classList}`} direction={'top'} />
<span onClick={() => { askGtp() }}>ASK GPT</span>
</div>
)}
</>

@ -16,14 +16,14 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
content: string
}>({
display: '',
content: ''
content: '',
})
const [atAddressOptions, setAtAddressOptions] = useState<{
title: string | JSX.Element
disabled: boolean
}>({
title: <FormattedMessage id="udapp.atAddressOptionsTitle1" />,
disabled: true
disabled: true,
})
const [loadedAddress, setLoadedAddress] = useState<string>('')
const [contractOptions, setContractOptions] = useState<{
@ -31,7 +31,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
disabled: boolean
}>({
title: <FormattedMessage id="udapp.contractOptionsTitle1" />,
disabled: true
disabled: true,
})
const [loadedContractData, setLoadedContractData] = useState<ContractData>(null)
const [constructorInterface, setConstructorInterface] = useState<FuncABI>(null)
@ -50,7 +50,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
enableAtAddress(false)
setAbiLabel({
display: 'none',
content: 'ABI file selected'
content: 'ABI file selected',
})
}, [])
@ -71,19 +71,19 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
if (/.(.abi)$/.exec(currentFile) && '' !== atAddressValue.current.value) {
setAbiLabel({
display: 'block',
content: currentFile
content: currentFile,
})
enableAtAddress(true)
} else if (isContractFile(currentFile)) {
setAbiLabel({
display: 'none',
content: ''
content: '',
})
if (!currentContract) enableAtAddress(false)
} else {
setAbiLabel({
display: 'none',
content: ''
content: '',
})
if (!currentContract) enableAtAddress(false)
}
@ -150,7 +150,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<span className="text-start">
<FormattedMessage id="udapp.atAddressOptionsTitle2" values={{ br: <br /> }} />
</span>
)
),
})
} else {
setAtAddressOptions({
@ -161,7 +161,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<span className="text-start">
<FormattedMessage id="udapp.atAddressOptionsTitle4" values={{ br: <br /> }} />
</span>
)
),
})
}
}
@ -170,7 +170,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
if (enable) {
setContractOptions({
disabled: false,
title: <FormattedMessage id="udapp.contractOptionsTitle2" />
title: <FormattedMessage id="udapp.contractOptionsTitle2" />,
})
} else {
setContractOptions({
@ -182,7 +182,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<span className="text-start">
<FormattedMessage id="udapp.contractOptionsTitle4" values={{ br: <br /> }} />
</span>
)
),
})
}
}
@ -289,7 +289,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
setCompilerName(contract.compilerName)
setContractOptions({
disabled: false,
title: <FormattedMessage id="udapp.contractOptionsTitle2" />
title: <FormattedMessage id="udapp.contractOptionsTitle2" />,
})
}
})
@ -297,7 +297,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
setCompilerName('')
setContractOptions({
title: <FormattedMessage id="udapp.contractOptionsTitle1" />,
disabled: true
disabled: true,
})
}
}
@ -323,7 +323,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<a href="https://eips.ethereum.org/EIPS/eip-55" target="_blank" rel="noreferrer">
EIP-55
</a>
)
),
}}
/>
</span>
@ -343,7 +343,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<a href="https://eips.ethereum.org/EIPS/eip-170" target="_blank" rel="noreferrer">
eip-170
</a>
)
),
}}
/>
</div>
@ -358,7 +358,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<a href="https://eips.ethereum.org/EIPS/eip-3860" target="_blank" rel="noreferrer">
eip-3860
</a>
)
),
}}
/>
</div>
@ -383,7 +383,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<FormattedMessage
id="udapp.compiledBy"
values={{
compilerName: <span className="text-capitalize">{compilerName}</span>
compilerName: <span className="text-capitalize">{compilerName}</span>,
}}
/>
</label>
@ -423,7 +423,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
disabled={contractOptions.disabled}
style={{
display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block',
pointerEvents: contractOptions.disabled ? 'none' : 'auto'
pointerEvents: contractOptions.disabled ? 'none' : 'auto',
}}
>
<option value="" disabled hidden>
@ -509,12 +509,12 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<div className="pt-2 d-flex flex-column sudapp_button udapp_atAddressSect">
<div className="d-flex flex-row">
<CustomTooltip placement={'top-end'} tooltipClasses="text-wrap text-left" tooltipId="runAndDeployAddresstooltip" tooltipText={atAddressOptions.title}>
<div id="runAndDeployAtAdressButtonContainer" onClick={loadFromAddress} data-title={atAddressOptions.title}>
<div id="runAndDeployAtAdressButtonContainer" data-title={atAddressOptions.title}>
<button
className="udapp_atAddress btn-sm py-2 btn-primary"
id="runAndDeployAtAdressButton"
disabled={atAddressOptions.disabled}
style={{pointerEvents: 'none', border: 'none'}}
style={{ border: 'none' }}
onClick={loadFromAddress}
data-title={atAddressOptions.title}
>
@ -532,7 +532,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
ref={atAddressValue}
className={(!addressIsValid ? 'border border-danger' : 'border-0') + ' h-100 udapp_input udapp_ataddressinput ataddressinput form-control'}
placeholder={intl.formatMessage({
id: 'udapp.loadContractFromAddress'
id: 'udapp.loadContractFromAddress',
})}
onChange={atAddressChanged}
/>

@ -34,7 +34,7 @@ export function InstanceContainerUI(props: InstanceContainerProps) {
tooltipId="deployAndRunClearInstancesTooltip"
tooltipText={<FormattedMessage id="udapp.deployAndRunClearInstances" />}
>
<i className="mr-2 udapp_icon far fa-trash-alt" data-id="deployAndRunClearInstances" onClick={clearInstance} aria-hidden="true"></i>
<i className="mr-1 udapp_icon far fa-trash-alt" data-id="deployAndRunClearInstances" onClick={clearInstance} aria-hidden="true"></i>
</CustomTooltip>
) : null}
</div>

@ -111,6 +111,10 @@
border-bottom-right-radius: 0;
border-right: 0;
}
.udapp_atAddress:focus {
outline: none;
box-shadow: none;
}
.udapp_atAddressSect {
margin-top: 8px;
height: 32px;

@ -515,7 +515,6 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const currentFile = api.currentFile
if (!isSolFileSelected()) return
_setCompilerVersionFromPragma(currentFile)
let externalCompType
if (hhCompilation) externalCompType = 'hardhat'
@ -527,7 +526,6 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
const currentFile = api.currentFile
if (!isSolFileSelected()) return
_setCompilerVersionFromPragma(currentFile)
let externalCompType
if (hhCompilation) externalCompType = 'hardhat'

@ -285,7 +285,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => {
const file = split[2]
const parsedLocation = {
start: parseInt(split[0]),
length: parseInt(split[1])
length: parseInt(split[1]),
}
const locationToHighlight = testTab.offsetToLineColumnConverter.offsetToLineColumnWithContent(parsedLocation, parseInt(file), filesContent[fileName].content)
await testTab.call('editor', 'discardHighlight')
@ -549,7 +549,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => {
passed: result.totalPassing,
failed: result.totalFailing,
timeTaken: totalTime,
rendered: false
rendered: false,
}
testsResultByFilename[filename]['summary'] = testsSummary
showTestsResult()
@ -597,7 +597,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => {
evmVersion,
optimize,
usingWorker: canUseWorker(currentVersion),
runs
runs,
}
const deployCb = async (file: string, contractAddress: string) => {
const compilerData = await testTab.call('compilerArtefacts', 'getCompilerAbstract', file)
@ -838,35 +838,45 @@ export const SolidityUnitTesting = (props: Record<string, any>) => {
</button>
</CustomTooltip>
</div>
<div className="d-flex align-items-center mx-3 pb-2 mt-2 border-bottom">
<div className="d-flex align-items-center ml-1 mr-3 pl-1 pb-2 mt-2 border-bottom custom-control custom-checkbox">
<input
id="checkAllTests"
className="custom-control-input"
type="checkbox"
data-id="testTabCheckAllTests"
onClick={checkAll}
checked={checkSelectAll}
onChange={() => {}} // eslint-disable-line
/>
<label className="text-nowrap pl-2 mb-0" htmlFor="checkAllTests">
<label
data-id="testTabCheckAllTests"
htmlFor="checkAllTests"
className="form-check-label mb-0 ml-4 custom-control-label text-nowrap"
style={{ paddingTop: '0.125rem' }}
>
{' '}
<FormattedMessage id="solidityUnitTesting.selectAll" />{' '}
</label>
</div>
<div className="testList py-2 mt-0 border-bottom">
<div className="testList ml-1 pr-2 mt-0 border-bottom py-2">
{testFiles.length
? testFiles.map((testFileObj: TestObject, index) => {
const elemId = `singleTest${testFileObj.fileName}`
return (
<div className="d-flex align-items-center py-1" key={index}>
<div className="d-flex align-items-center pl-1 custom-control custom-checkbox" key={index}>
<input
data-id="singleTest"
className="singleTest"
className="singleTest custom-control-input"
id={elemId}
onChange={(e) => toggleCheckbox(e.target.checked, index)}
type="checkbox"
checked={testFileObj.checked}
/>
<label className="singleTestLabel text-nowrap pl-2 mb-0" htmlFor={elemId}>
<label
data-id="singleTest"
id={"id" + elemId}
className="singleTestLabel text-nowrap mb-0 form-check-label ml-4 custom-control-label text-nowrap"
htmlFor={elemId}
style={{ paddingTop: '0.125rem' }}
>
{testFileObj.fileName}
</label>
</div>

@ -167,7 +167,7 @@ export const TabsUI = (props: TabsUIProps) => {
<button
data-id="play-editor"
className="btn text-success py-0"
disabled={!(tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' || tabsState.currentExt === 'sol')}
disabled={!(tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' || tabsState.currentExt === 'sol' || tabsState.currentExt === 'circom')}
onClick={async () => {
const path = active().substr(active().indexOf('/') + 1, active().length)
const content = await props.plugin.call('fileManager', 'readFile', path)

@ -1,4 +1,4 @@
import { CLEAR_CONSOLE, CMD_HISTORY, EMPTY_BLOCK, ERROR, HTML, INFO, KNOWN_TRANSACTION, LISTEN_ON_NETWORK, LOG, NEW_TRANSACTION, SCRIPT, UNKNOWN_TRANSACTION, WARN } from '../types/terminalTypes'
import { CLEAR_CONSOLE, CMD_HISTORY, EMPTY_BLOCK, ERROR, HTML, INFO, KNOWN_TRANSACTION, LISTEN_ON_NETWORK, LOG, TYPEWRITERLOG, TYPEWRITERWARNING, TYPEWRITERSUCCESS, NEW_TRANSACTION, SCRIPT, UNKNOWN_TRANSACTION, WARN } from '../types/terminalTypes'
export const initialState = {
journalBlocks: [
@ -151,6 +151,21 @@ export const registerScriptRunnerReducer = (state, action) => {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log', provider: action.payload.provider })
}
case TYPEWRITERLOG:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-log', provider: action.payload.provider })
}
case TYPEWRITERWARNING:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-warning', provider: action.payload.provider })
}
case TYPEWRITERSUCCESS:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-success', provider: action.payload.provider })
}
case INFO:
return {
...state,

@ -8,7 +8,7 @@ import {
registerErrorScriptRunnerAction,
registerWarnScriptRunnerAction,
listenOnNetworkAction,
initListeningOnNetwork
initListeningOnNetwork,
} from './actions/terminalAction'
import { initialState, registerCommandReducer, addCommandHistoryReducer, registerScriptRunnerReducer } from './reducers/terminalReducer'
import { getKeyOf, getValueOf, Objectfilter, matched } from './utils/utils'
@ -45,7 +45,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const [toaster, setToaster] = useState(false)
const [toastProvider, setToastProvider] = useState({
show: false,
fileName: ''
fileName: '',
})
const [modalState, setModalState] = useState({
message: '',
@ -54,7 +54,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
cancelLabel: '',
hide: true,
cancelFn: () => {},
handleHide: () => {}
handleHide: () => {},
})
const [clearConsole, setClearConsole] = useState(false)
@ -63,7 +63,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const [autoCompletState, setAutoCompleteState] = useState({
activeSuggestion: 0,
data: {
_options: []
_options: [],
},
_startingElement: 0,
autoCompleteSelectedItem: {},
@ -75,7 +75,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
text: '',
userInput: '',
extraCommands: [],
commandHistoryIndex: 0
commandHistoryIndex: 0,
})
const [searchInput, setSearchInput] = useState('')
@ -84,6 +84,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
// terminal inputRef
const inputEl = useRef(null)
const messagesEndRef = useRef(null)
const typeWriterIndexes = useRef([])
// terminal dragable
const panelRef = useRef(null)
@ -101,8 +102,8 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
scriptRunnerDispatch({
type: 'html',
payload: {
message: [html ? (html.innerText ? html.innerText : html) : null]
}
message: [html ? (html.innerText ? html.innerText : html) : null],
},
})
},
@ -110,14 +111,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
if (typeof message === 'string') {
message = {
value: message,
type: 'log'
type: 'log',
}
}
scriptRunnerDispatch({
type: message.type ? message.type : 'log',
payload: {message: [message.value]}
payload: { message: [message.value] },
})
}
},
})
}, [])
@ -213,8 +214,8 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
},
execute: (fileName, callback) => {
return execute(fileName, callback)
}
}
},
},
}
try {
const cmds = vm.createContext(context)
@ -247,7 +248,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
// backspace or any key that should remove the autocompletion
setAutoCompleteState((prevState) => ({
...prevState,
showSuggestions: false
showSuggestions: false,
}))
}
if (autoCompletState.showSuggestions && (event.which === 13 || event.which === 9)) {
@ -256,7 +257,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
...prevState,
activeSuggestion: 0,
showSuggestions: false,
userInput: Object.keys(autoCompletState.data._options[0]).toString()
userInput: Object.keys(autoCompletState.data._options[0]).toString(),
}))
} else {
if (autoCompletState.showSuggestions && (event.which === 13 || event.which === 9)) {
@ -266,14 +267,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
showSuggestions: false,
userInput: autoCompletState.data._options[autoCompletState.activeSuggestion]
? Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion]).toString()
: inputEl.current.value
: inputEl.current.value,
}))
} else {
setAutoCompleteState((prevState) => ({
...prevState,
activeSuggestion: 0,
showSuggestions: false,
userInput: autoCompletState.data._options.length === 1 ? Object.keys(autoCompletState.data._options[0]).toString() : inputEl.current.value
userInput: autoCompletState.data._options.length === 1 ? Object.keys(autoCompletState.data._options[0]).toString() : inputEl.current.value,
}))
}
}
@ -298,14 +299,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
inputEl.current.focus()
setAutoCompleteState((prevState) => ({
...prevState,
showSuggestions: false
showSuggestions: false,
}))
}
} else if (newstate._commandHistory.length && event.which === 38 && !autoCompletState.showSuggestions && autoCompletState.userInput === '') {
event.preventDefault()
setAutoCompleteState((prevState) => ({
...prevState,
userInput: newstate._commandHistory[0]
userInput: newstate._commandHistory[0],
}))
} else if (event.which === 38 && autoCompletState.showSuggestions) {
event.preventDefault()
@ -315,7 +316,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setAutoCompleteState((prevState) => ({
...prevState,
activeSuggestion: suggestionCount - 1,
userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion]).toString()
userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion]).toString(),
}))
} else if (event.which === 38 && !autoCompletState.showSuggestions) {
// <arrowUp>
@ -332,7 +333,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setAutoCompleteState((prevState) => ({
...prevState,
activeSuggestion: suggestionCount + 1,
userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion + 1]).toString()
userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion + 1]).toString(),
}))
} else if (event.which === 40 && !autoCompletState.showSuggestions) {
if (_cmdIndex > -1) {
@ -359,7 +360,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
log: 'text-log',
info: 'text-log',
warn: 'text-warning',
error: 'text-danger'
error: 'text-danger',
}[mode] // defaults
if (mode) {
@ -389,6 +390,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const handleClearConsole = () => {
setClearConsole(true)
typeWriterIndexes.current = []
dispatch({ type: 'clearconsole', payload: [] })
inputEl.current.focus()
}
@ -408,42 +410,42 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setAutoCompleteState((prevState) => ({
...prevState,
showSuggestions: false,
userInput: inputString
userInput: inputString,
}))
} else {
setAutoCompleteState((prevState) => ({
...prevState,
showSuggestions: true,
userInput: inputString
userInput: inputString,
}))
}
const textList = inputString.split('.')
if (textList.length === 1) {
setAutoCompleteState((prevState) => ({
...prevState,
data: {_options: []}
data: { _options: [] },
}))
const result = Objectfilter(allPrograms, autoCompletState.userInput)
setAutoCompleteState((prevState) => ({
...prevState,
data: {_options: result}
data: { _options: result },
}))
} else {
setAutoCompleteState((prevState) => ({
...prevState,
data: {_options: []}
data: { _options: [] },
}))
const result = Objectfilter(allCommands, autoCompletState.userInput)
setAutoCompleteState((prevState) => ({
...prevState,
data: {_options: result}
data: { _options: result },
}))
}
} else {
setAutoCompleteState((prevState) => ({
...prevState,
showSuggestions: false,
userInput: inputString
userInput: inputString,
}))
}
}
@ -453,7 +455,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setAutoCompleteState((prevState) => ({
...prevState,
showSuggestions: false,
userInput: result
userInput: result,
}))
inputEl.current.focus()
}
@ -466,7 +468,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
}
setAutoCompleteState((prevState) => ({
...prevState,
activeSuggestion: suggestionCount - 1
activeSuggestion: suggestionCount - 1,
}))
} else if (event.keyCode === 40) {
if (autoCompletState.activeSuggestion - 1 === autoCompletState.data._options.length) {
@ -474,7 +476,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
}
setAutoCompleteState((prevState) => ({
...prevState,
activeSuggestion: suggestionCount + 1
activeSuggestion: suggestionCount + 1,
}))
}
}
@ -488,7 +490,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
okFn,
cancelLabel,
cancelFn,
hide
hide,
}))
}
@ -515,7 +517,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
display:
autoCompletState.showSuggestions && autoCompletState.userInput !== '' && autoCompletState.userInput.length > 0 && autoCompletState.data._options.length > 0
? 'block'
: 'none'
: 'none',
}}
>
<div>
@ -545,7 +547,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setAutoCompleteState((prevState) => ({
...prevState,
activeSuggestion: 0,
showSuggestions: false
showSuggestions: false,
}))
}
@ -590,12 +592,17 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText="Pending Transactions">
<div className="mx-2">0</div>
</CustomTooltip>
<div className="pt-1 h-80 mx-3 align-items-center remix_ui_terminal_listenOnNetwork custom-control custom-checkbox">
<div className="h-80 mx-3 align-items-center remix_ui_terminal_listenOnNetwork custom-control custom-checkbox">
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={intl.formatMessage({ id: 'terminal.listenTitle' })}>
<input className="custom-control-input" id="listenNetworkCheck" onChange={listenOnNetwork} type="checkbox" />
</CustomTooltip>
<CustomTooltip placement="top" tooltipId="terminalClear" tooltipClasses="text-nowrap" tooltipText={intl.formatMessage({ id: 'terminal.listenTitle' })}>
<label className="pt-1 form-check-label custom-control-label text-nowrap" htmlFor="listenNetworkCheck" data-id="listenNetworkCheckInput">
<label
className="form-check-label custom-control-label text-nowrap"
style={{ paddingTop: '0.125rem' }}
htmlFor="listenNetworkCheck"
data-id="listenNetworkCheckInput"
>
<FormattedMessage id="terminal.listen" />
</label>
</CustomTooltip>
@ -686,7 +693,11 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
})
} else if (Array.isArray(x.message)) {
return x.message.map((msg, i) => {
if (!msg) msg = 'null'
// strictly check condition on 0, false, except undefined, NaN.
// if you type `undefined`, terminal automatically throws error, it's error message: "undefined" is not valid JSON
// if you type `NaN`, terminal would give `null`
if (msg === false || msg === 0) msg = msg.toString()
else if (!msg) msg = 'null'
if (React.isValidElement(msg)) {
return (
<div className="px-4 block" data-id="block" key={i}>
@ -714,23 +725,38 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
</div>
)
} else {
// typeWriterIndexes: we don't want to rerender using typewriter when the react component updates
if (x.typewriter && !typeWriterIndexes.current.includes(index)) {
typeWriterIndexes.current.push(index)
return (
<div className={classNameBlock} data-id="block" key={i}>
<span className={x.style}>{msg ? msg.toString() : null}</span>
</div>
<div className={classNameBlock} data-id="block" key={index}> <span ref={(element) => {
typewrite(element, msg ? msg.toString() : null)
}} className={x.style}></span></div>
)
} else {
return (
<div className={classNameBlock} data-id="block" key={i}><span className={x.style}>{msg ? msg.toString() : null}</span></div>
)
}
}
})
} else {
// typeWriterIndexes: we don't want to rerender using typewriter when the react component updates
if (x.typewriter && !typeWriterIndexes.current.includes(index)) {
typeWriterIndexes.current.push(index)
return (
<div className={classNameBlock} data-id="block" key={index}> <span ref={(element) => {
typewrite(element, x.message)
}} className={x.style}></span></div>
)
} else {
if (typeof x.message !== 'function') {
return (
<div className={classNameBlock} data-id="block" key={index}>
{' '}
<span className={x.style}> {x.message}</span>
</div>
<div className={classNameBlock} data-id="block" key={index}> <span className={x.style}> {x.message}</span></div>
)
}
}
}
})}
<div ref={messagesEndRef} />
</div>
@ -769,6 +795,17 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
)
}
const typewrite = (elementsRef, message) => {
(() => {
let count = 0
const id = setInterval(() => {
count++
elementsRef.innerText = message.substr(0, count)
if (message === count) clearInterval(id)
}, 5)
})()
}
function isHtml (value) {
if (!value.indexOf) return false
return value.indexOf('<div') !== -1 || value.indexOf('<span') !== -1 || value.indexOf('<p') !== -1 || value.indexOf('<label') !== -1 || value.indexOf('<b') !== -1

@ -15,6 +15,9 @@ export const NEW_CALL = 'newCall'
export const HTML = 'html'
export const LOG = 'log'
export const TYPEWRITERLOG = 'typewriterlog'
export const TYPEWRITERWARNING = 'typewriterwarning'
export const TYPEWRITERSUCCESS = 'typewritersuccess'
export const INFO = 'info'
export const WARN = 'warn'
export const ERROR = 'error'

@ -81,7 +81,7 @@ urlResolver.resolve(fileName, urlHandler)
Please feel free to open an issue or a pull request.
In case you want to add some code, do have a look to our contribution guidelnes [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
In case you want to add some code, do have a look at our contribution guidelines [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
### License
MIT © 2018-21 Remix Team

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-url-resolver",
"version": "0.0.60",
"version": "0.0.61",
"description": "Solidity import url resolver engine",
"main": "src/index.js",
"types": "src/index.d.ts",
@ -40,5 +40,5 @@
"typescript": "^3.1.6"
},
"typings": "src/index.d.ts",
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab"
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40"
}

@ -14,7 +14,7 @@
Please feel free to open an issue or a pull request.
In case you want to add some code, do have a look to our contribution guidelnes [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
In case you want to add some code, do have a look at our contribution guidelines [here](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md). Reach us on [Gitter](https://gitter.im/ethereum/remix) in case of any queries.
### License
MIT © 2022 Remix Team

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-ws-templates",
"version": "1.0.25",
"version": "1.0.26",
"description": "Create a Remix IDE workspace using different templates",
"main": "src/index.js",
"types": "src/index.d.ts",
@ -24,5 +24,5 @@
"ethers": "^5.4.2",
"web3": "^4.1.1"
},
"gitHead": "b11b11623729f741a5ccd387bc60dc5db41f28ab"
"gitHead": "335beb044cbd9d58c255bd8441a310757c99bf40"
}

@ -1,8 +1,7 @@
import { ethers } from 'ethers'
// https://etherscan.io/address/0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2#code
export const CREATE2_DEPLOYER_ADDRESS =
"0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2";
export const CREATE2_DEPLOYER_ADDRESS = '0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2'
/**
* Deploy the given contract using a factory
@ -14,13 +13,12 @@ export const CREATE2_DEPLOYER_ADDRESS =
* @return {string} deployed contract address
*/
export const deploy = async (contractName: string, args: Array<any>, salt: string, accountIndex?: number): Promise<string> => {
console.log(`deploying ${contractName}`)
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.Contract(CREATE2_DEPLOYER_ADDRESS, contractDeployerAbi, signer);
const signer = new ethers.providers.Web3Provider(web3Provider).getSigner(accountIndex)
const factory = new ethers.Contract(CREATE2_DEPLOYER_ADDRESS, contractDeployerAbi, signer)
//@ts-ignore
const contract = await ethers.getContractFactory(contractName)
const initCode = contract.getDeployTransaction(args)
@ -40,219 +38,219 @@ export const deploy = async (contractName: string, args: Array<any>, salt: strin
export const contractDeployerAbi = [
{
"anonymous": false,
"inputs": [
anonymous: false,
inputs: [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
indexed: true,
internalType: 'address',
name: 'previousOwner',
type: 'address',
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
indexed: true,
internalType: 'address',
name: 'newOwner',
type: 'address',
},
],
"name": "OwnershipTransferred",
"type": "event"
name: 'OwnershipTransferred',
type: 'event',
},
{
"anonymous": false,
"inputs": [
anonymous: false,
inputs: [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
indexed: false,
internalType: 'address',
name: 'account',
type: 'address',
},
],
"name": "Paused",
"type": "event"
name: 'Paused',
type: 'event',
},
{
"anonymous": false,
"inputs": [
anonymous: false,
inputs: [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
indexed: false,
internalType: 'address',
name: 'account',
type: 'address',
},
],
"name": "Unpaused",
"type": "event"
name: 'Unpaused',
type: 'event',
},
{
"inputs": [
inputs: [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
internalType: 'bytes32',
name: 'salt',
type: 'bytes32',
},
{
"internalType": "bytes32",
"name": "codeHash",
"type": "bytes32"
}
internalType: 'bytes32',
name: 'codeHash',
type: 'bytes32',
},
],
"name": "computeAddress",
"outputs": [
name: 'computeAddress',
outputs: [
{
"internalType": "address",
"name": "",
"type": "address"
}
internalType: 'address',
name: '',
type: 'address',
},
],
"stateMutability": "view",
"type": "function"
stateMutability: 'view',
type: 'function',
},
{
"inputs": [
inputs: [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
internalType: 'bytes32',
name: 'salt',
type: 'bytes32',
},
{
"internalType": "bytes32",
"name": "codeHash",
"type": "bytes32"
internalType: 'bytes32',
name: 'codeHash',
type: 'bytes32',
},
{
"internalType": "address",
"name": "deployer",
"type": "address"
}
internalType: 'address',
name: 'deployer',
type: 'address',
},
],
"name": "computeAddressWithDeployer",
"outputs": [
name: 'computeAddressWithDeployer',
outputs: [
{
"internalType": "address",
"name": "",
"type": "address"
}
internalType: 'address',
name: '',
type: 'address',
},
],
"stateMutability": "pure",
"type": "function"
stateMutability: 'pure',
type: 'function',
},
{
"inputs": [
inputs: [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
internalType: 'uint256',
name: 'value',
type: 'uint256',
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
internalType: 'bytes32',
name: 'salt',
type: 'bytes32',
},
{
"internalType": "bytes",
"name": "code",
"type": "bytes"
}
internalType: 'bytes',
name: 'code',
type: 'bytes',
},
],
"name": "deploy",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
name: 'deploy',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
"inputs": [
inputs: [
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
internalType: 'uint256',
name: 'value',
type: 'uint256',
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
internalType: 'bytes32',
name: 'salt',
type: 'bytes32',
},
],
"name": "deployERC1820Implementer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
name: 'deployERC1820Implementer',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
"inputs": [
inputs: [
{
"internalType": "address payable",
"name": "payoutAddress",
"type": "address"
}
internalType: 'address payable',
name: 'payoutAddress',
type: 'address',
},
],
"name": "killCreate2Deployer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
name: 'killCreate2Deployer',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
"inputs": [],
"name": "owner",
"outputs": [
inputs: [],
name: 'owner',
outputs: [
{
"internalType": "address",
"name": "",
"type": "address"
}
internalType: 'address',
name: '',
type: 'address',
},
],
"stateMutability": "view",
"type": "function"
stateMutability: 'view',
type: 'function',
},
{
"inputs": [],
"name": "pause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
inputs: [],
name: 'pause',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
"inputs": [],
"name": "paused",
"outputs": [
inputs: [],
name: 'paused',
outputs: [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
internalType: 'bool',
name: '',
type: 'bool',
},
],
"stateMutability": "view",
"type": "function"
stateMutability: 'view',
type: 'function',
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
inputs: [],
name: 'renounceOwnership',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
"inputs": [
inputs: [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
internalType: 'address',
name: 'newOwner',
type: 'address',
},
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
name: 'transferOwnership',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
"inputs": [],
"name": "unpause",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
inputs: [],
name: 'unpause',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
"stateMutability": "payable",
"type": "receive"
}
stateMutability: 'payable',
type: 'receive',
},
]

@ -17,7 +17,7 @@ More details are explained in the [documentation](https://remix-ide.readthedocs.
NOTE: When the remixd NPM module is installed, it also installs [Slither](https://github.com/crytic/slither), [solc-select](https://github.com/crytic/solc-select#quickstart) and sets [solc](https://docs.soliditylang.org/en/latest/installing-solidity.html) to latest version i.e. 0.8.15 currently.
ALSO NOTE: Python3.6+ (pip3) needs to already be installed on the System. In case of any discrepany, Slither can also installed along with other dependencies using command:
ALSO NOTE: Python3.6+ (pip3) needs to already be installed on the System. In case of any discrepancy, Slither can also be installed along with other dependencies using the command:
```
> remixd -i slither
```

@ -113,8 +113,7 @@
"watch": "watchify apps/remix-ide/src/index.js -dv -p browserify-reload -o apps/remix-ide/build/app.js --exclude solc",
"reinstall": "rm ./node-modules/ -rf && rm yarn.lock && rm ./build/ -rf && yarn install & yarn run build",
"ganache-cli": "npx ganache-cli",
"build-contracts": "find ./node_modules/@openzeppelin/contracts | grep -i '.sol' > libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt && find ./node_modules/@uniswap/v3-core/contracts | grep -i '.sol' >> libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt",
"prepare": "husky install"
"build-contracts": "find ./node_modules/@openzeppelin/contracts | grep -i '.sol' > libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt && find ./node_modules/@uniswap/v3-core/contracts | grep -i '.sol' >> libs/remix-ui/editor/src/lib/providers/completion/contracts/contracts.txt"
},
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.16.0",
@ -140,7 +139,7 @@
"@remixproject/plugin-webview": "0.3.33",
"@remixproject/plugin-ws": "0.3.33",
"@types/nightwatch": "^2.3.1",
"@web3modal/ethereum": "^2.6.2",
"@web3modal/ethereum": "^2.7.1",
"@web3modal/react": "^2.6.2",
"ansi-gray": "^0.1.1",
"async": "^2.6.2",
@ -150,6 +149,7 @@
"brace": "^0.8.0",
"change-case": "^4.1.1",
"chokidar": "^2.1.8",
"circom_wasm": "^0.0.2",
"color-support": "^1.1.3",
"commander": "^9.4.1",
"core-js": "^3.6.5",
@ -179,6 +179,7 @@
"latest-version": "^5.1.0",
"merge": "^2.1.1",
"npm-install-version": "^6.0.2",
"openai": "^3.3.0",
"path-browserify": "^1.0.1",
"prettier": "^2.8.4",
"prettier-plugin-solidity": "^1.0.0-beta.24",
@ -208,8 +209,8 @@
"tree-kill": "^1.2.2",
"ts-loader": "^9.2.6",
"tslib": "^2.3.0",
"viem": "^1.2.12",
"wagmi": "^1.3.8",
"viem": "^1.6.0",
"wagmi": "^1.3.10",
"web3": "^4.1.0",
"winston": "^3.3.3",
"ws": "^7.3.0"
@ -229,7 +230,7 @@
"@babel/preset-typescript": "^7.18.6",
"@babel/register": "^7.4.4",
"@fortawesome/fontawesome-free": "^5.8.1",
"@monaco-editor/react": "4.4.5",
"@monaco-editor/react": "4.5.1",
"@nrwl/cli": "^15.7.1",
"@nrwl/eslint-plugin-nx": "^15.7.1",
"@nrwl/js": "15.7.1",
@ -328,7 +329,7 @@
"minixhr": "^4.0.0",
"mkdirp": "^0.5.1",
"mocha": "^8.0.1",
"monaco-editor": "^0.30.1",
"monaco-editor": "0.41.0",
"nanohtml": "^1.6.3",
"nightwatch": "^2.3",
"nodemon": "^2.0.4",
@ -368,10 +369,5 @@
},
"resolutions": {
"@types/react": "^17.0.24"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged --pattern \"**/*.{js,jsx,ts,tsx}\""
}
}
}

@ -0,0 +1,3 @@
{
"**/*.{tsx,ts,js}": ["prettier --write","eslint --fix"]
}

@ -3643,20 +3643,19 @@
semver "^7.3.8"
superstruct "^1.0.3"
"@monaco-editor/loader@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.2.tgz#04effbb87052d19cd7d3c9d81c0635490f9bb6d8"
integrity sha512-BTDbpHl3e47r3AAtpfVFTlAi7WXv4UQ/xZmz8atKl4q7epQV5e7+JbigFDViWF71VBi4IIBdcWP57Hj+OWuc9g==
"@monaco-editor/loader@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca"
integrity sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==
dependencies:
state-local "^1.0.6"
"@monaco-editor/react@4.4.5":
version "4.4.5"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.4.5.tgz#beabe491efeb2457441a00d1c7651c653697f65b"
integrity sha512-IImtzU7sRc66OOaQVCG+5PFHkSWnnhrUWGBuH6zNmH2h0YgmAhcjHZQc/6MY9JWEbUtVF1WPBMJ9u1XuFbRrVA==
"@monaco-editor/react@4.5.1":
version "4.5.1"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.5.1.tgz#fbc76c692aee9a33b9ab24ae0c5f219b8f002fdb"
integrity sha512-NNDFdP+2HojtNhCkRfE6/D6ro6pBNihaOzMbGK84lNWzRu+CfBjwzGt4jmnqimLuqp5yE5viHS2vi+QOAnD5FQ==
dependencies:
"@monaco-editor/loader" "^1.3.2"
prop-types "^15.7.2"
"@monaco-editor/loader" "^1.3.3"
"@motionone/animation@^10.15.1":
version "10.15.1"
@ -3764,6 +3763,13 @@
dependencies:
"@noble/hashes" "1.3.0"
"@noble/curves@1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d"
integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==
dependencies:
"@noble/hashes" "1.3.1"
"@noble/ed25519@^1.7.0":
version "1.7.3"
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.3.tgz#57e1677bf6885354b466c38e2b620c62f45a7123"
@ -3779,6 +3785,11 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1"
integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==
"@noble/hashes@1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9"
integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==
"@noble/secp256k1@1.7.1", "@noble/secp256k1@^1.6.3", "@noble/secp256k1@~1.7.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c"
@ -5869,7 +5880,7 @@
dependencies:
"@types/node" "*"
"@types/ws@^8.5.3":
"@types/ws@^8.5.4":
version "8.5.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==
@ -5997,56 +6008,61 @@
resolved "https://registry.yarnpkg.com/@wagmi/chains/-/chains-1.2.0.tgz#d59eaa70ec51a5fdcd113975926992acfb17ab12"
integrity sha512-dmDRipsE54JfyudOBkuhEexqQWcrZqxn/qiujG8SBzMh/az/AH5xlJSA+j1CPWTx9+QofSMF3B7A4gb6XRmSaQ==
"@wagmi/chains@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@wagmi/chains/-/chains-1.5.0.tgz#0b23a1505704b4b07a46f41e1ec65a486aceb16b"
integrity sha512-JO5iqh7Km4GW/6XKKDYMq3YQ9wlOSGzzaTUQhALQ58KANxEZ70tZWhfTZAPD3fdgv4wheai7kyHDNgTW6X7fnw==
"@wagmi/chains@1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@wagmi/chains/-/chains-1.6.0.tgz#eb992ad28dbaaab729b5bcab3e5b461e8a035656"
integrity sha512-5FRlVxse5P4ZaHG3GTvxwVANSmYJas1eQrTBHhjxVtqXoorm0aLmCHbhmN8Xo1yu09PaWKlleEvfE98yH4AgIw==
"@wagmi/connectors@2.6.6":
version "2.6.6"
resolved "https://registry.yarnpkg.com/@wagmi/connectors/-/connectors-2.6.6.tgz#98f0f90831ed8d76295f44b1b85439ce0118bf70"
integrity sha512-/o1c/TCivQs8DOAUOcQvY2UIt3p2mWOAHi39D0LC74+ncpXzLC5/gyaWU38qnTxPM8s/PmTmaWDgz+VhICXrag==
"@wagmi/chains@1.7.0":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@wagmi/chains/-/chains-1.7.0.tgz#8f6ad81cf867e1788417f7c978ca92bc083ecaf6"
integrity sha512-TKVeHv0GqP5sV1yQ8BDGYToAFezPnCexbbBpeH14x7ywi5a1dDStPffpt9x+ytE6LJWkZ6pAMs/HNWXBQ5Nqmw==
"@wagmi/connectors@2.7.0":
version "2.7.0"
resolved "https://registry.yarnpkg.com/@wagmi/connectors/-/connectors-2.7.0.tgz#547972502cbe6719217043fe5b610ac48534dc93"
integrity sha512-1KOL0HTJl5kzSC/YdKwFwiokr6poUQn1V/tcT0TpG3iH2x0lSM7FTkvCjVVY/6lKzTXrLlo9y2aE7AsOPnkvqg==
dependencies:
"@coinbase/wallet-sdk" "^3.6.6"
"@ledgerhq/connect-kit-loader" "^1.1.0"
"@safe-global/safe-apps-provider" "^0.17.1"
"@safe-global/safe-apps-sdk" "^8.0.0"
"@walletconnect/ethereum-provider" "2.9.0"
"@walletconnect/ethereum-provider" "2.9.2"
"@walletconnect/legacy-provider" "^2.0.0"
"@walletconnect/modal" "2.5.9"
"@walletconnect/utils" "2.9.0"
"@walletconnect/modal" "2.6.1"
"@walletconnect/utils" "2.9.2"
abitype "0.8.7"
eventemitter3 "^4.0.7"
"@wagmi/core@1.3.7":
version "1.3.7"
resolved "https://registry.yarnpkg.com/@wagmi/core/-/core-1.3.7.tgz#7bc00d172d206ffa171df0d5a813c3559d8824e3"
integrity sha512-ens31RwICdrbRanNYwlJs0DAw/LOqUPQm6qXsmEciOENT4w+7pC959LXU9xfaOADWVMekeLDmRqAGCszTNIXAg==
"@wagmi/core@1.3.9":
version "1.3.9"
resolved "https://registry.yarnpkg.com/@wagmi/core/-/core-1.3.9.tgz#16bac164fe74203fde68abe7991b947d3a26e6ab"
integrity sha512-SrnABCrsDvhiMCLLLyzyHnZbEumsFT/XWlJJQZeyEDcixL95R7XQwOaaoRI4MpNilCtMtu3jzN57tA5Z2iA+kw==
dependencies:
"@wagmi/chains" "1.5.0"
"@wagmi/connectors" "2.6.6"
"@wagmi/chains" "1.7.0"
"@wagmi/connectors" "2.7.0"
abitype "0.8.7"
eventemitter3 "^4.0.7"
zustand "^4.3.1"
"@walletconnect/core@2.9.0":
version "2.9.0"
resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.9.0.tgz#7837a5d015a22b48d35b987bcde2aa9ccdf300d8"
integrity sha512-MZYJghS9YCvGe32UOgDj0mCasaOoGHQaYXWeQblXE/xb8HuaM6kAWhjIQN9P+MNp5QP134BHP5olQostcCotXQ==
"@walletconnect/core@2.9.2":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.9.2.tgz#c46734ca63771b28fd77606fd521930b7ecfc5e1"
integrity sha512-VARMPAx8sIgodeyngDHbealP3B621PQqjqKsByFUTOep8ZI1/R/20zU+cmq6j9RCrL+kLKZcrZqeVzs8Z7OlqQ==
dependencies:
"@walletconnect/heartbeat" "1.2.1"
"@walletconnect/jsonrpc-provider" "1.0.13"
"@walletconnect/jsonrpc-types" "1.0.3"
"@walletconnect/jsonrpc-utils" "1.0.8"
"@walletconnect/jsonrpc-ws-connection" "1.0.12"
"@walletconnect/jsonrpc-ws-connection" "1.0.13"
"@walletconnect/keyvaluestorage" "^1.0.2"
"@walletconnect/logger" "^2.0.1"
"@walletconnect/relay-api" "^1.0.9"
"@walletconnect/relay-auth" "^1.0.4"
"@walletconnect/safe-json" "^1.0.2"
"@walletconnect/time" "^1.0.2"
"@walletconnect/types" "2.9.0"
"@walletconnect/utils" "2.9.0"
"@walletconnect/types" "2.9.2"
"@walletconnect/utils" "2.9.2"
events "^3.3.0"
lodash.isequal "4.5.0"
uint8arrays "^3.1.0"
@ -6079,19 +6095,19 @@
dependencies:
tslib "1.14.1"
"@walletconnect/ethereum-provider@2.9.0":
version "2.9.0"
resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.9.0.tgz#aa6e9e441678c824af8f744c50dafd604f19d69e"
integrity sha512-rSXkC0SXMigJRdIi/M2RMuEuATY1AwtlTWQBnqyxoht7xbO2bQNPCXn0XL4s/GRNrSUtoKSY4aPMHXV4W4yLBA==
"@walletconnect/ethereum-provider@2.9.2":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.9.2.tgz#fb3a6fca279bb4e98e75baa2fb9730545d41bb99"
integrity sha512-eO1dkhZffV1g7vpG19XUJTw09M/bwGUwwhy1mJ3AOPbOSbMPvwiCuRz2Kbtm1g9B0Jv15Dl+TvJ9vTgYF8zoZg==
dependencies:
"@walletconnect/jsonrpc-http-connection" "^1.0.7"
"@walletconnect/jsonrpc-provider" "^1.0.13"
"@walletconnect/jsonrpc-types" "^1.0.3"
"@walletconnect/jsonrpc-utils" "^1.0.8"
"@walletconnect/sign-client" "2.9.0"
"@walletconnect/types" "2.9.0"
"@walletconnect/universal-provider" "2.9.0"
"@walletconnect/utils" "2.9.0"
"@walletconnect/sign-client" "2.9.2"
"@walletconnect/types" "2.9.2"
"@walletconnect/universal-provider" "2.9.2"
"@walletconnect/utils" "2.9.2"
events "^3.3.0"
"@walletconnect/events@^1.0.1":
@ -6192,10 +6208,10 @@
"@walletconnect/jsonrpc-types" "^1.0.2"
tslib "1.14.1"
"@walletconnect/jsonrpc-ws-connection@1.0.12":
version "1.0.12"
resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.12.tgz#2192314884fabdda6d0a9d22e157e5b352025ed8"
integrity sha512-HAcadga3Qjt1Cqy+qXEW6zjaCs8uJGdGQrqltzl3OjiK4epGZRdvSzTe63P+t/3z+D2wG+ffEPn0GVcDozmN1w==
"@walletconnect/jsonrpc-ws-connection@1.0.13":
version "1.0.13"
resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.13.tgz#23b0cdd899801bfbb44a6556936ec2b93ef2adf4"
integrity sha512-mfOM7uFH4lGtQxG+XklYuFBj6dwVvseTt5/ahOkkmpcAEgz2umuzu7fTR+h5EmjQBdrmYyEBOWADbeaFNxdySg==
dependencies:
"@walletconnect/jsonrpc-utils" "^1.0.6"
"@walletconnect/safe-json" "^1.0.2"
@ -6279,31 +6295,30 @@
pino "7.11.0"
tslib "1.14.1"
"@walletconnect/modal-core@2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@walletconnect/modal-core/-/modal-core-2.5.9.tgz#45e0c25320d42855aaac39e6ba256a84f972b871"
integrity sha512-isIebwF9hOknGouhS/Ob4YJ9Sa/tqNYG2v6Ua9EkCqIoLimepkG5eC53tslUWW29SLSfQ9qqBNG2+iE7yQXqgw==
"@walletconnect/modal-core@2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@walletconnect/modal-core/-/modal-core-2.6.1.tgz#bc76055d0b644a2d4b98024324825c108a700905"
integrity sha512-f2hYlJ5pwzGvjyaZ6BoGR5uiMgXzWXt6w6ktt1N8lmY6PiYp8whZgqx2hTxVWwVlsGnaIfh6UHp1hGnANx0eTQ==
dependencies:
buffer "6.0.3"
valtio "1.10.6"
valtio "1.11.0"
"@walletconnect/modal-ui@2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@walletconnect/modal-ui/-/modal-ui-2.5.9.tgz#4d07f1697147ec9f75d85d93f564cadae05a5e59"
integrity sha512-nfBaAT9Ls7RZTBBgAq+Nt/3AoUcinIJ9bcq5UHXTV3lOPu/qCKmUC/0HY3GvUK8ykabUAsjr0OAGmcqkB91qug==
"@walletconnect/modal-ui@2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@walletconnect/modal-ui/-/modal-ui-2.6.1.tgz#200c54c8dfe3c71321abb2724e18bb357dfd6371"
integrity sha512-RFUOwDAMijSK8B7W3+KoLKaa1l+KEUG0LCrtHqaB0H0cLnhEGdLR+kdTdygw+W8+yYZbkM5tXBm7MlFbcuyitA==
dependencies:
"@walletconnect/modal-core" "2.5.9"
lit "2.7.5"
"@walletconnect/modal-core" "2.6.1"
lit "2.7.6"
motion "10.16.2"
qrcode "1.5.3"
"@walletconnect/modal@2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@walletconnect/modal/-/modal-2.5.9.tgz#28840f2a46bcd0a47c5fda60d18a5f1607a92a72"
integrity sha512-Zs2RvPwbBNRdBhb50FuJCxi3FJltt1KSpI7odjU/x9GTpTOcSOkmR66PBCy2JvNA0+ztnS1Xs0LVEr3lu7/Jzw==
"@walletconnect/modal@2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@walletconnect/modal/-/modal-2.6.1.tgz#066fdbfcff83b58c8a9da66ab4af0eb93e3626de"
integrity sha512-G84tSzdPKAFk1zimgV7JzIUFT5olZUVtI3GcOk77OeLYjlMfnDT23RVRHm5EyCrjkptnvpD0wQScXePOFd2Xcw==
dependencies:
"@walletconnect/modal-core" "2.5.9"
"@walletconnect/modal-ui" "2.5.9"
"@walletconnect/modal-core" "2.6.1"
"@walletconnect/modal-ui" "2.6.1"
"@walletconnect/randombytes@^1.0.3":
version "1.0.3"
@ -6349,19 +6364,19 @@
dependencies:
tslib "1.14.1"
"@walletconnect/sign-client@2.9.0":
version "2.9.0"
resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.9.0.tgz#fd3b0acb68bc8d56350f01ed70f8c6326e6e89fa"
integrity sha512-mEKc4LlLMebCe45qzqh+MX4ilQK4kOEBzLY6YJpG8EhyT45eX4JMNA7qQoYa9MRMaaVb/7USJcc4e3ZrjZvQmA==
"@walletconnect/sign-client@2.9.2":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.9.2.tgz#ff4c81c082c2078878367d07f24bcb20b1f7ab9e"
integrity sha512-anRwnXKlR08lYllFMEarS01hp1gr6Q9XUgvacr749hoaC/AwGVlxYFdM8+MyYr3ozlA+2i599kjbK/mAebqdXg==
dependencies:
"@walletconnect/core" "2.9.0"
"@walletconnect/core" "2.9.2"
"@walletconnect/events" "^1.0.1"
"@walletconnect/heartbeat" "1.2.1"
"@walletconnect/jsonrpc-utils" "1.0.8"
"@walletconnect/logger" "^2.0.1"
"@walletconnect/time" "^1.0.2"
"@walletconnect/types" "2.9.0"
"@walletconnect/utils" "2.9.0"
"@walletconnect/types" "2.9.2"
"@walletconnect/utils" "2.9.2"
events "^3.3.0"
"@walletconnect/time@^1.0.2":
@ -6371,10 +6386,10 @@
dependencies:
tslib "1.14.1"
"@walletconnect/types@2.9.0":
version "2.9.0"
resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.9.0.tgz#6e5dfdc7212c1ec4ab49a1ec409c743e16093f72"
integrity sha512-ORopsMfSRvUYqtjKKd6scfg8o4/aGebipLxx92AuuUgMTERSU6cGmIrK6rdLu7W6FBJkmngPLEGc9mRqAb9Lug==
"@walletconnect/types@2.9.2":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.9.2.tgz#d5fd5a61dc0f41cbdca59d1885b85207ac7bf8c5"
integrity sha512-7Rdn30amnJEEal4hk83cdwHUuxI1SWQ+K7fFFHBMqkuHLGi3tpMY6kpyfDxnUScYEZXqgRps4Jo5qQgnRqVM7A==
dependencies:
"@walletconnect/events" "^1.0.1"
"@walletconnect/heartbeat" "1.2.1"
@ -6383,25 +6398,25 @@
"@walletconnect/logger" "^2.0.1"
events "^3.3.0"
"@walletconnect/universal-provider@2.9.0":
version "2.9.0"
resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.9.0.tgz#a6b4a1f099262536e17b5c25bf7b3c89db9945a8"
integrity sha512-k3nkSBkF69sJJVoe17IVoPtnhp/sgaa2t+x7BvA/BKeMxE0DGdtRJdEXotTc8DBmI7o2tkq6l8+HyFBGjQ/CjQ==
"@walletconnect/universal-provider@2.9.2":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.9.2.tgz#40e54e98bc48b1f2f5f77eb5b7f05462093a8506"
integrity sha512-JmaolkO8D31UdRaQCHwlr8uIFUI5BYhBzqYFt54Mc6gbIa1tijGOmdyr6YhhFO70LPmS6gHIjljwOuEllmlrxw==
dependencies:
"@walletconnect/jsonrpc-http-connection" "^1.0.7"
"@walletconnect/jsonrpc-provider" "1.0.13"
"@walletconnect/jsonrpc-types" "^1.0.2"
"@walletconnect/jsonrpc-utils" "^1.0.7"
"@walletconnect/logger" "^2.0.1"
"@walletconnect/sign-client" "2.9.0"
"@walletconnect/types" "2.9.0"
"@walletconnect/utils" "2.9.0"
"@walletconnect/sign-client" "2.9.2"
"@walletconnect/types" "2.9.2"
"@walletconnect/utils" "2.9.2"
events "^3.3.0"
"@walletconnect/utils@2.9.0":
version "2.9.0"
resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.9.0.tgz#c73925edb9fefe79021bcf028e957028f986b728"
integrity sha512-7Tu3m6dZL84KofrNBcblsgpSqU2vdo9ImLD7zWimLXERVGNQ8smXG+gmhQYblebIBhsPzjy9N38YMC3nPlfQNw==
"@walletconnect/utils@2.9.2":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.9.2.tgz#035bdb859ee81a4bcc6420f56114cc5ec3e30afb"
integrity sha512-D44hwXET/8JhhIjqljY6qxSu7xXnlPrf63UN/Qfl98vDjWlYVcDl2+JIQRxD9GPastw0S8XZXdRq59XDXLuZBg==
dependencies:
"@stablelib/chacha20poly1305" "1.0.1"
"@stablelib/hkdf" "1.0.1"
@ -6411,7 +6426,7 @@
"@walletconnect/relay-api" "^1.0.9"
"@walletconnect/safe-json" "^1.0.2"
"@walletconnect/time" "^1.0.2"
"@walletconnect/types" "2.9.0"
"@walletconnect/types" "2.9.2"
"@walletconnect/window-getters" "^1.0.1"
"@walletconnect/window-metadata" "^1.0.1"
detect-browser "5.3.0"
@ -6441,10 +6456,10 @@
buffer "6.0.3"
valtio "1.10.6"
"@web3modal/ethereum@^2.6.2":
version "2.6.2"
resolved "https://registry.yarnpkg.com/@web3modal/ethereum/-/ethereum-2.6.2.tgz#e6522ddf00444cb3e6e54d390a74864c329cee5e"
integrity sha512-8QCzJj0+x6y/7V++DQnXdPjdmMvq+zm/Fl8CEKhNlni9p4H406q7ramjdJEu1Bk4xtCklYh/aU3ZGR5wUIDysA==
"@web3modal/ethereum@^2.7.1":
version "2.7.1"
resolved "https://registry.yarnpkg.com/@web3modal/ethereum/-/ethereum-2.7.1.tgz#464dbc1d00d075c16961b77e9a353b1966538653"
integrity sha512-1x3qhYh9qgtvw1MDQD4VeDf2ZOsVANKRPtUty4lF+N4L8xnAIwvNKUAMA4j6T5xSsjqUfq5Tdy5mYsNxLmsWMA==
"@web3modal/react@^2.6.2":
version "2.6.2"
@ -6687,6 +6702,11 @@ abitype@0.8.7:
resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.8.7.tgz#e4b3f051febd08111f486c0cc6a98fa72d033622"
integrity sha512-wQ7hV8Yg/yKmGyFpqrNZufCxbszDe5es4AZGYPBitocfSqXtjrTG9JMWFcc4N30ukl2ve48aBTwt7NJxVQdU3w==
abitype@0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.3.tgz#294d25288ee683d72baf4e1fed757034e3c8c277"
integrity sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==
abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
@ -7570,6 +7590,13 @@ axios@^0.21.1:
dependencies:
follow-redirects "^1.14.0"
axios@^0.26.0:
version "0.26.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9"
integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==
dependencies:
follow-redirects "^1.14.8"
axios@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
@ -9612,6 +9639,11 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
inherits "^2.0.1"
safe-buffer "^5.0.1"
circom_wasm@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/circom_wasm/-/circom_wasm-0.0.2.tgz#9d24866b8289a5778999270823a4cb06e64145b5"
integrity sha512-SCMP6cxHHL7MLedDrTl+nGYyE6+kE5GepbxtZm65GlR0wUMD9eNOD1shwScWaDnmBOZTrImmNeTYZA5DWCmIww==
circular-json@^0.3.0:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
@ -17487,6 +17519,15 @@ lit@2.7.5:
lit-element "^3.3.0"
lit-html "^2.7.0"
lit@2.7.6:
version "2.7.6"
resolved "https://registry.yarnpkg.com/lit/-/lit-2.7.6.tgz#810007b876ed43e0c70124de91831921598b1665"
integrity sha512-1amFHA7t4VaaDe+vdQejSVBklwtH9svGoG6/dZi9JhxtJBBlqY5D1RV7iLUYY0trCqQc4NfhYYZilZiVHt7Hxg==
dependencies:
"@lit/reactive-element" "^1.6.0"
lit-element "^3.3.0"
lit-html "^2.7.0"
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@ -19196,10 +19237,10 @@ mold-source-map@~0.4.0:
convert-source-map "^1.1.0"
through "~2.2.7"
monaco-editor@^0.30.1:
version "0.30.1"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.30.1.tgz#47f8d18a0aa2264fc5654581741ab8d7bec01689"
integrity sha512-B/y4+b2O5G2gjuxIFtCE2EkM17R2NM7/3F8x0qcPsqy4V83bitJTIO4TIeZpYlzu/xy6INiY/+84BEm6+7Cmzg==
monaco-editor@0.41.0:
version "0.41.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.41.0.tgz#2ba31e5af7e3ae93ac5d7467ec2772ef9b3d967f"
integrity sha512-1o4olnZJsiLmv5pwLEAmzHTE/5geLKQ07BrGxlF4Ri/AXAc2yyDGZwHjiTqD8D/ROKUZmwMA28A+yEowLNOEcA==
motion@10.16.2:
version "10.16.2"
@ -20567,6 +20608,14 @@ open@^8.0.9, open@^8.4.0:
is-docker "^2.1.1"
is-wsl "^2.2.0"
openai@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/openai/-/openai-3.3.0.tgz#a6408016ad0945738e1febf43f2fccca83a3f532"
integrity sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==
dependencies:
axios "^0.26.0"
form-data "^4.0.0"
opener@^1.5.1, opener@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
@ -26632,6 +26681,14 @@ valtio@1.10.6:
proxy-compare "2.5.1"
use-sync-external-store "1.2.0"
valtio@1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/valtio/-/valtio-1.11.0.tgz#c029dcd17a0f99d2fbec933721fe64cfd32a31ed"
integrity sha512-65Yd0yU5qs86b5lN1eu/nzcTgQ9/6YnD6iO+DDaDbQLn1Zv2w12Gwk43WkPlUBxk5wL/6cD5YMFf7kj6HZ1Kpg==
dependencies:
proxy-compare "2.5.1"
use-sync-external-store "1.2.0"
value-or-function@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813"
@ -26677,7 +26734,7 @@ vfile@^5.0.0:
unist-util-stringify-position "^3.0.0"
vfile-message "^3.0.0"
viem@^1.0.0, viem@^1.2.12:
viem@^1.0.0:
version "1.2.12"
resolved "https://registry.yarnpkg.com/viem/-/viem-1.2.12.tgz#0342f52d05968bd1c2af5db0b9bc569926ae9151"
integrity sha512-TMhvqT2VaCaJyBfuNDyL1h8xPFyPDHeX6Qab66TjWscnNcTwkW0gojO4Uh+A4RuPzFxIlWSW+b5SjS8SJHlHpg==
@ -26692,6 +26749,22 @@ viem@^1.0.0, viem@^1.2.12:
isomorphic-ws "5.0.0"
ws "8.12.0"
viem@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/viem/-/viem-1.6.0.tgz#8befa678c3ac79b9558dfd1708130b2ecb1994f4"
integrity sha512-ae9Twkd0q2Qlj4yYpWjb4DzYAhKY0ibEpRH8FJaTywZXNpTjFidSdBaT0CVn1BaH7O7cnX4/O47zvDUMGJD1AA==
dependencies:
"@adraffy/ens-normalize" "1.9.0"
"@noble/curves" "1.1.0"
"@noble/hashes" "1.3.0"
"@scure/bip32" "1.3.0"
"@scure/bip39" "1.2.0"
"@types/ws" "^8.5.4"
"@wagmi/chains" "1.6.0"
abitype "0.9.3"
isomorphic-ws "5.0.0"
ws "8.12.0"
vinyl-fs@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7"
@ -26755,15 +26828,15 @@ w3c-blob@0.0.1:
resolved "https://registry.yarnpkg.com/w3c-blob/-/w3c-blob-0.0.1.tgz#b0cd352a1a50f515563420ffd5861f950f1d85b8"
integrity sha1-sM01KhpQ9RVWNCD/1YYflQ8dhbg=
wagmi@^1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/wagmi/-/wagmi-1.3.8.tgz#544554fdab35ee32d93f107eb75b9d1924e22880"
integrity sha512-RCfcE+Q+yhyfq7j5u7KQRz2KXUMAMexu0Y2kr8z81t4fmDlU5C+OEVQ6NFoUzAzEsJpznpQjZgsfpHKeFm17hQ==
wagmi@^1.3.10:
version "1.3.10"
resolved "https://registry.yarnpkg.com/wagmi/-/wagmi-1.3.10.tgz#100aeaecf7a030e9e91118d366a734ec30c56551"
integrity sha512-MMGJcnxOmeUZWDmzUxgRGcB1cqxbJoSFSa+pNY4vBCWMz0n4ptpE5F8FKISLCx+BGoDwsaz2ldcMALcdJZ+29w==
dependencies:
"@tanstack/query-sync-storage-persister" "^4.27.1"
"@tanstack/react-query" "^4.28.0"
"@tanstack/react-query-persist-client" "^4.28.0"
"@wagmi/core" "1.3.7"
"@wagmi/core" "1.3.9"
abitype "0.8.7"
use-sync-external-store "^1.2.0"

Loading…
Cancel
Save