From 11ff044057d5dbb7f84bb475a0efce19c7c24d30 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 24 Oct 2024 07:39:46 +0200 Subject: [PATCH] merge from master --- .circleci/config.yml | 373 +- .github/workflows/pr-reminder.yml | 2 +- .gitignore | 4 +- .nvmrc | 1 + .prettierrc.json | 4 +- CONTRIBUTING.md | 6 +- README.md | 4 +- .../src/app/components/container.tsx | 6 +- .../.babelrc | 10 +- .../.browserslistrc | 0 apps/contract-verification/.eslintrc | 3 + .../.eslintrc.json | 0 apps/contract-verification/project.json | 69 + apps/contract-verification/src/app/App.css | 14 + .../src/app/AppContext.tsx | 34 + .../app/ContractVerificationPluginClient.ts | 18 + .../src/app/Verifiers/AbstractVerifier.ts | 16 + .../src/app/Verifiers/BlockscoutVerifier.ts | 50 + .../src/app/Verifiers/EtherscanVerifier.ts | 289 + .../src/app/Verifiers/SourcifyVerifier.ts | 169 + .../src/app/Verifiers/index.ts | 30 + .../src/app/VerifyFormContext.tsx | 46 + apps/contract-verification/src/app/app.tsx | 154 + .../src/app/components/AccordionReceipt.tsx | 105 + .../src/app/components/ConfigInput.tsx | 69 + .../app/components/ConstructorArguments.tsx | 135 + .../app/components/ContractAddressInput.tsx | 33 + .../src/app/components/ContractDropdown.css | 3 + .../src/app/components/ContractDropdown.tsx | 73 + .../src/app/components/NavMenu.tsx | 33 + .../components/SearchableChainDropdown.tsx | 113 + .../src/app/components/index.tsx | 5 + .../src/app/hooks/useLocalStorage.tsx | 9 +- .../src/app/hooks/useSourcifySupported.tsx | 33 + .../src/app/layouts/Default.tsx | 25 + .../src/app/layouts/index.ts | 1 + apps/contract-verification/src/app/routes.tsx | 49 + .../src/app/types/SettingsTypes.ts | 19 + .../src/app/types/ThemeType.ts | 1 + .../src/app/types/VerificationTypes.ts | 80 + .../src/app/types/index.ts | 3 + .../src/app/utils/default-apis.json | 576 + .../src/app/utils/default-settings.ts | 28 + .../src/app/utils/index.ts | 1 + .../src/app/views/LookupView.tsx | 167 + .../src/app/views/ReceiptsView.tsx | 16 + .../src/app/views/SettingsView.tsx | 54 + .../src/app/views/VerifyView.tsx | 279 + .../src/app/views/index.ts | 4 + .../src/assets/.gitkeep | 0 .../src/environments/environment.prod.ts | 4 +- .../src/environments/environment.ts | 4 +- .../src/favicon.ico | Bin .../src/index.html | 5 +- apps/contract-verification/src/main.tsx | 10 + .../src/polyfills.ts | 4 +- apps/contract-verification/src/profile.json | 16 + .../src/styles.css | 0 .../tsconfig.app.json | 0 apps/contract-verification/tsconfig.json | 18 + .../webpack.config.js | 26 +- apps/etherscan/project.json | 69 - apps/etherscan/src/app/App.css | 7 - apps/etherscan/src/app/AppContext.tsx | 25 - .../src/app/EtherscanPluginClient.ts | 70 - apps/etherscan/src/app/app.tsx | 136 - .../src/app/components/HeaderWithSettings.tsx | 81 - .../src/app/components/SubmitButton.tsx | 34 - apps/etherscan/src/app/components/index.ts | 2 - apps/etherscan/src/app/layouts/Default.tsx | 17 - apps/etherscan/src/app/layouts/index.ts | 1 - apps/etherscan/src/app/routes.tsx | 37 - apps/etherscan/src/app/types/Receipt.ts | 9 - apps/etherscan/src/app/types/ThemeType.ts | 1 - apps/etherscan/src/app/types/index.ts | 2 - apps/etherscan/src/app/utils/index.ts | 1 - apps/etherscan/src/app/utils/networks.ts | 46 - apps/etherscan/src/app/utils/scripts.ts | 30 - apps/etherscan/src/app/utils/utilities.ts | 69 - apps/etherscan/src/app/utils/verify.ts | 206 - .../src/app/views/CaptureKeyView.tsx | 63 - apps/etherscan/src/app/views/ErrorView.tsx | 16 - apps/etherscan/src/app/views/HomeView.tsx | 31 - apps/etherscan/src/app/views/ReceiptsView.tsx | 170 - apps/etherscan/src/app/views/VerifyView.tsx | 235 - apps/etherscan/src/app/views/index.ts | 4 - apps/etherscan/src/main.tsx | 14 - apps/etherscan/src/profile.json | 16 - apps/learneth/src/App.tsx | 38 +- .../src/components/LoadingScreen/index.tsx | 2 +- .../src/components/RepoImporter/index.tsx | 21 +- apps/learneth/src/pages/Home/index.tsx | 15 +- apps/learneth/src/pages/Logo/index.tsx | 9 +- apps/learneth/src/redux/models/remixide.ts | 1 + apps/learneth/src/redux/models/workshop.ts | 51 +- apps/learneth/src/redux/store.ts | 13 +- apps/quick-dapp/src/actions/index.ts | 25 +- apps/{etherscan => remix-dapp}/.eslintrc | 0 apps/remix-dapp/README.md | 1 + apps/remix-dapp/package.json | 9 + apps/remix-dapp/project.json | 69 + apps/remix-dapp/src/App.css | 25 + apps/remix-dapp/src/App.tsx | 65 + apps/remix-dapp/src/actions/index.ts | 234 + apps/remix-dapp/src/assets/instance.json | 507 + apps/remix-dapp/src/assets/logo.png | Bin 0 -> 15967 bytes .../src/components/ContractGUI/index.tsx | 361 + .../src/components/DappTop/index.tsx | 74 + .../src/components/DragBar/index.tsx | 62 + .../remix-dapp/src/components/Home/mobile.tsx | 88 + apps/remix-dapp/src/components/Home/pc.tsx | 47 + .../src/components/SettingsUI/account.tsx | 58 + .../src/components/SettingsUI/gasPrice.tsx | 35 + .../src/components/SettingsUI/index.tsx | 94 + .../src/components/SettingsUI/locale.tsx | 53 + .../src/components/SettingsUI/network.tsx | 47 + .../src/components/SettingsUI/theme.tsx | 88 + .../src/components/SettingsUI/value.tsx | 108 + .../components/UiTerminal/ChechTxStatus.tsx | 29 + .../src/components/UiTerminal/Context.tsx | 83 + .../src/components/UiTerminal/RenderCall.tsx | 84 + .../UiTerminal/RenderKnownTransactions.tsx | 76 + .../src/components/UiTerminal/Table.tsx | 327 + .../src/components/UiTerminal/TxList.tsx | 209 + .../src/components/UiTerminal/index.css | 92 + .../src/components/UiTerminal/index.tsx | 174 + .../src/components/UiTerminal/types.ts | 32 + .../src/components/UiTerminal/utils.ts | 68 + .../src/components/UniversalDappUI/index.css | 41 + .../src/components/UniversalDappUI/index.tsx | 247 + .../UniversalDappUI/lowLevelInteractions.tsx | 211 + apps/remix-dapp/src/contexts/index.ts | 3 + apps/remix-dapp/src/index.css | 4 + apps/remix-dapp/src/index.html | 17 + apps/remix-dapp/src/locales/en/index.ts | 14 + apps/remix-dapp/src/locales/en/terminal.json | 44 + apps/remix-dapp/src/locales/en/udapp.json | 155 + apps/remix-dapp/src/locales/es/index.ts | 18 + apps/remix-dapp/src/locales/es/terminal.json | 43 + apps/remix-dapp/src/locales/es/udapp.json | 141 + apps/remix-dapp/src/locales/fr/index.ts | 18 + apps/remix-dapp/src/locales/fr/terminal.json | 43 + apps/remix-dapp/src/locales/fr/udapp.json | 141 + apps/remix-dapp/src/locales/it/index.ts | 18 + apps/remix-dapp/src/locales/it/terminal.json | 43 + apps/remix-dapp/src/locales/it/udapp.json | 141 + apps/remix-dapp/src/locales/zh/index.ts | 18 + apps/remix-dapp/src/locales/zh/terminal.json | 43 + apps/remix-dapp/src/locales/zh/udapp.json | 141 + apps/remix-dapp/src/main.tsx | 9 + apps/remix-dapp/src/reducers/state.ts | 51 + apps/remix-dapp/src/types.ts | 13 + apps/remix-dapp/src/utils/buildData.ts | 41 + apps/remix-dapp/src/utils/chains.ts | 151 + apps/remix-dapp/src/utils/constants.ts | 53 + apps/remix-dapp/src/utils/metamask.ts | 96 + apps/remix-dapp/src/utils/tools.ts | 17 + apps/remix-dapp/src/utils/txRunner.ts | 277 + apps/remix-dapp/src/utils/walletConnect.ts | 129 + apps/remix-dapp/src/vite-env.d.ts | 1 + apps/remix-dapp/tsconfig.app.json | 23 + apps/{etherscan => remix-dapp}/tsconfig.json | 0 apps/remix-dapp/webpack.config.js | 108 + apps/remix-dapp/yarn.lock | 34 + .../src/commands/addLocalPlugin.ts | 2 +- .../src/githttpbackend/package.json | 4 +- .../src/githttpbackend/yarn.lock | 105 +- .../src/tests/dgit_github.test.ts | 7 + .../src/tests/dgit_local.test.ts | 1 + apps/remix-ide-e2e/src/tests/editor.test.ts | 10 +- .../src/tests/etherscan_api.test.ts | 2 +- apps/remix-ide-e2e/src/tests/matomo.test.ts | 492 + .../src/tests/pinned_contracts.test.ts | 59 +- .../remix-ide-e2e/src/tests/quickDapp.test.ts | 4 +- .../src/tests/runAndDeploy_injected.test.ts | 30 + apps/remix-ide-e2e/src/tests/search.test.ts | 6 +- .../src/tests/signingMessage.test.ts | 19 +- apps/remix-ide-e2e/src/tests/terminal.test.ts | 22 +- .../remix-ide-e2e/src/tests/vyper_api.test.ts | 161 +- .../remix-ide-e2e/src/tests/workspace.test.ts | 20 +- .../src/tests/workspace_git.test.ts | 5 +- apps/remix-ide/ci/downloadsoljson.sh | 32 +- .../ci/update_desktop_release_assets.ts | 242 + apps/remix-ide/contracts/ballot.sol | 55 +- apps/remix-ide/project.json | 2 +- apps/remix-ide/research-project.md | 2 +- apps/remix-ide/src/app.js | 148 +- .../components/plugin-manager-component.js | 3 +- apps/remix-ide/src/app/components/preload.tsx | 2 + .../src/app/components/vertical-icons.tsx | 7 - apps/remix-ide/src/app/files/dgitProvider.ts | 621 +- apps/remix-ide/src/app/files/fileManager.ts | 2 +- apps/remix-ide/src/app/panels/file-panel.js | 6 - apps/remix-ide/src/app/panels/layout.ts | 37 +- .../app/plugins/electron/appUpdaterPlugin.ts | 54 + .../src/app/plugins/electron/foundryPlugin.ts | 13 + .../src/app/plugins/electron/hardhatPlugin.ts | 13 + .../plugins/electron/remixAIDesktopPlugin.tsx | 33 + .../src/app/plugins/electron/slitherPlugin.ts | 13 + apps/remix-ide/src/app/plugins/matomo.ts | 2 +- .../app/plugins/permission-handler-plugin.tsx | 2 +- .../src/app/plugins/remix-templates.ts | 23 +- .../src/app/plugins/remixAIPlugin.tsx | 169 + apps/remix-ide/src/app/plugins/remixGuide.css | 6 + apps/remix-ide/src/app/plugins/remixGuide.tsx | 38 +- .../src/app/plugins/remixGuideData.json | 253 +- apps/remix-ide/src/app/plugins/solcoderAI.tsx | 283 - .../templates-selection-plugin.tsx | 24 +- .../plugins/templates-selection/templates.ts | 293 +- .../src/app/providers/abstract-provider.tsx | 2 +- .../injected-arbitrum-one-provider.tsx | 17 - .../providers/injected-custom-provider.tsx | 10 +- .../injected-ephemery-testnet-provider.tsx | 40 - .../providers/injected-optimism-provider.tsx | 17 - .../injected-provider-trustwallet.tsx | 26 - .../injected-skale-chaos-testnet-provider.tsx | 27 - apps/remix-ide/src/app/tabs/compile-tab.js | 6 +- .../src/app/tabs/locales/en/editor.json | 2 +- .../src/app/tabs/locales/en/electron.json | 4 +- .../src/app/tabs/locales/en/filePanel.json | 16 +- .../src/app/tabs/locales/en/gitui.json | 3 + .../src/app/tabs/locales/en/home.json | 7 +- .../src/app/tabs/locales/en/settings.json | 18 +- .../src/app/tabs/locales/en/udapp.json | 21 +- .../src/app/tabs/locales/en/vyper.json | 3 + .../src/app/tabs/runTab/model/recorder.js | 4 +- .../src/app/tabs/script-runner-ui.tsx | 14 +- apps/remix-ide/src/app/tabs/settings-tab.tsx | 2 + .../src/app/udapp/{run-tab.js => run-tab.tsx} | 93 +- .../css/themes/bootstrap-cerulean.min.css | 4 +- .../css/themes/bootstrap-cyborg.min.css | 3 +- .../css/themes/bootstrap-flatly.min.css | 2 +- .../css/themes/bootstrap-spacelab.min.css | 3 +- .../assets/css/themes/remix-black_undtds.css | 1 + .../assets/css/themes/remix-candy_ikhg4m.css | 1 + .../assets/css/themes/remix-dark_tvx1s2.css | 1 + .../assets/css/themes/remix-hacker_owl.css | 1 + .../assets/css/themes/remix-light_powaqg.css | 1 + .../css/themes/remix-midcentury_hrzph3.css | 1 + .../src/assets/css/themes/remix-unicorn.css | 1 + .../src/assets/css/themes/remix-violet.css | 1 + .../remix-ide/src/assets/img/gnosis_chain.png | Bin 0 -> 7928 bytes apps/remix-ide/src/assets/img/remi-prof.webp | Bin 0 -> 63410 bytes apps/remix-ide/src/assets/js/loader.js | 67 +- apps/remix-ide/src/assets/list.json | 2 +- apps/remix-ide/src/blockchain/blockchain.tsx | 2 + .../src/blockchain/providers/injected.ts | 6 +- apps/remix-ide/src/blockchain/providers/vm.ts | 21 +- .../src/blockchain/providers/worker-vm.ts | 31 + apps/remix-ide/src/remixAppManager.js | 50 +- apps/remix-ide/src/remixEngine.js | 4 +- apps/remixdesktop/README.md | 152 +- apps/remixdesktop/TEST.md | 124 + apps/remixdesktop/after-pack.js | 14 + apps/remixdesktop/afterbuild.js | 30 + apps/remixdesktop/aftersign.js | 104 + apps/remixdesktop/alpha.json | 61 + apps/remixdesktop/beta.json | 61 + .../circom-download/latest/circom-linux-amd64 | Bin 0 -> 11786424 bytes apps/remixdesktop/entitlements.mac.plist | 13 + apps/remixdesktop/esbuild.js | 12 + apps/remixdesktop/insiders.json | 62 + apps/remixdesktop/latest.json | 61 + apps/remixdesktop/nightwatch.conf.js | 344 + apps/remixdesktop/notarizedmg.sh | 39 + apps/remixdesktop/package-lock.json | 14684 ++++ apps/remixdesktop/package.json | 116 +- apps/remixdesktop/run_ci_test.sh | 19 + apps/remixdesktop/run_git_ui_isogit_tests.sh | 14 + apps/remixdesktop/rundist.bash | 24 + apps/remixdesktop/rundist_esbuild.bash | 24 + apps/remixdesktop/rundist_tsc.bash | 24 + apps/remixdesktop/rundist_webpack.bash | 24 + apps/remixdesktop/splice_tests.js | 35 + apps/remixdesktop/src/engine.ts | 34 +- .../src/lib/InferenceServerManager.ts | 525 + apps/remixdesktop/src/lib/databatcher.ts | 2 +- apps/remixdesktop/src/lib/remixd.ts | 37 + apps/remixdesktop/src/lib/utils.ts | 24 + apps/remixdesktop/src/main.ts | 73 +- apps/remixdesktop/src/menus/file.ts | 31 - apps/remixdesktop/src/plugins/appUpdater.ts | 122 + .../src/plugins/compilerLoader.ts | 49 +- .../remixdesktop/src/plugins/foundryPlugin.ts | 248 + apps/remixdesktop/src/plugins/fsPlugin.ts | 130 +- .../remixdesktop/src/plugins/hardhatPlugin.ts | 220 + apps/remixdesktop/src/plugins/isoGitPlugin.ts | 285 +- .../remixdesktop/src/plugins/remixAIDektop.ts | 115 + .../remixdesktop/src/plugins/ripgrepPlugin.ts | 29 +- .../remixdesktop/src/plugins/slitherPlugin.ts | 197 + apps/remixdesktop/src/plugins/templates.ts | 24 +- apps/remixdesktop/src/plugins/xtermPlugin.ts | 92 +- apps/remixdesktop/src/preload.ts | 18 +- apps/remixdesktop/src/tools/git.ts | 84 +- apps/remixdesktop/src/types/index.ts | 9 - apps/remixdesktop/src/utils/config.ts | 8 +- apps/remixdesktop/src/utils/matamo.ts | 51 + .../test/cache_dir/remixdesktop.json | 2 + apps/remixdesktop/test/lib/git.ts | 195 + apps/remixdesktop/test/nighwatch.app.ts | 101 + .../test/tests/app/compiler.test.ts | 37 + .../test/tests/app/externaleditor.test.ts | 122 + .../test/tests/app/foundry.test.ts | 157 + apps/remixdesktop/test/tests/app/gist.test.ts | 36 + .../test/tests/app/git-ui.test.ts | 203 + .../test/tests/app/git-ui_2.test.ts | 181 + .../test/tests/app/git-ui_3.test.ts | 153 + .../test/tests/app/git-ui_4.test.ts | 200 + apps/remixdesktop/test/tests/app/git.test.ts | 28 + .../test/tests/app/github.test.ts | 255 + .../test/tests/app/github_2.test.ts | 190 + .../test/tests/app/github_3.test.ts | 180 + .../test/tests/app/hardhat.test.ts | 90 + .../test/tests/app/offline.test.ts | 48 + .../test/tests/app/search.test.ts | 269 + .../test/tests/app/slitherlinux.test.ts | 107 + .../test/tests/app/templates.test.ts | 35 + .../remixdesktop/test/tests/app/xterm.test.ts | 246 + .../test/tests/app/xtermwin.test.ts | 249 + apps/remixdesktop/test/types/index.d.ts | 108 + apps/remixdesktop/tsconfig.e2e.json | 8 + apps/remixdesktop/tsconfig.json | 43 +- apps/remixdesktop/webpack.config.js | 44 + apps/remixdesktop/yarn.lock | 3523 +- apps/solidity-compiler/src/app/compiler.ts | 4 + apps/vyper/README.md | 2 +- apps/vyper/src/app/app.css | 6 + apps/vyper/src/app/app.tsx | 67 +- .../src/app/components/CompileErrorCard.tsx | 3 +- .../src/app/components/CompilerButton.tsx | 39 +- apps/vyper/src/app/utils/compiler.tsx | 147 +- apps/vyper/src/app/utils/index.ts | 17 + apps/vyper/src/app/utils/remix-client.tsx | 27 +- apps/vyper/src/app/utils/types.ts | 39 +- libs/ghaction-helper/package.json | 8 +- libs/remix-ai-core/.eslintrc | 1 + libs/remix-ai-core/README.md | 7 + libs/remix-ai-core/package-lock.json | 5344 ++ libs/remix-ai-core/project.json | 32 + .../src/agents/codeExplainAgent.ts | 29 + .../src/agents/completionAgent.ts | 23 + .../remix-ai-core/src/agents/securityAgent.ts | 29 + .../src/helpers/dowload_model.ts | 0 .../src/helpers/inferenceServerReleases.ts | 55 + libs/remix-ai-core/src/index.ts | 20 + .../src/inferencers/remote/remoteInference.ts | 141 + libs/remix-ai-core/src/prompts/chat.ts | 21 + .../src/prompts/completionPrompts.ts | 18 + .../src/prompts/promptBuilder.ts | 28 + libs/remix-ai-core/src/types/constants.ts | 9 + libs/remix-ai-core/src/types/models.ts | 81 + libs/remix-ai-core/src/types/types.ts | 87 + libs/remix-ai-core/tsconfig.json | 10 + libs/remix-ai-core/tsconfig.lib.json | 15 + libs/remix-analyzer/package.json | 8 +- libs/remix-api/src/index.ts | 3 +- .../src/lib/plugins/filePanel-api.ts | 2 +- .../src/lib/plugins/fileSystem-api.ts | 5 +- libs/remix-api/src/lib/plugins/fs-api.ts | 13 + libs/remix-api/src/lib/plugins/git-api.ts | 43 + .../src/lib/plugins/remixAIDesktop-api.ts | 23 + libs/remix-api/src/lib/plugins/remixai-api.ts | 21 + .../remix-api/src/lib/plugins/terminal-api.ts | 11 + libs/remix-api/src/lib/remix-api.ts | 13 +- libs/remix-api/src/lib/types/git.ts | 206 + libs/remix-astwalker/package.json | 6 +- .../src/lib/compiler-artefacts.ts | 102 +- .../remix-core-plugin/src/lib/gist-handler.ts | 61 +- libs/remix-debug/package.json | 12 +- libs/remix-git/index.ts | 1 + libs/remix-git/src/isogit.ts | 345 + libs/remix-lib/package.json | 4 +- libs/remix-lib/src/execution/txExecution.ts | 2 +- libs/remix-lib/src/execution/txRunnerWeb3.ts | 3 +- libs/remix-lib/src/types/ICompilerApi.ts | 1 + libs/remix-simulator/package.json | 6 +- libs/remix-solidity/package.json | 6 +- libs/remix-tests/package.json | 10 +- libs/remix-tests/src/runTestFiles.ts | 6 +- libs/remix-tests/src/runTestSources.ts | 6 +- libs/remix-ui/app/src/index.ts | 2 +- .../app/src/lib/remix-app/actions/app.ts | 2 +- .../remix-app/components/dragbar/dragbar.tsx | 43 +- .../remix-app/components/modals/matomo.tsx | 19 +- .../components/modals/modal-wrapper.tsx | 8 +- .../app/src/lib/remix-app/context/context.tsx | 2 +- .../src/lib/remix-app/context/provider.tsx | 5 +- .../app/src/lib/remix-app/interface/index.ts | 9 +- .../app/src/lib/remix-app/reducer/modals.ts | 3 +- .../app/src/lib/remix-app/remix-app.tsx | 35 +- .../app/src/lib/remix-app/state/app.ts | 2 +- .../app/src/lib/remix-app/types/index.ts | 17 +- .../src/lib/providers/completionTimer.ts | 31 - .../lib/providers/inlineCompletionProvider.ts | 95 +- .../editor/src/lib/remix-plugin-types.ts | 1 - .../editor/src/lib/remix-ui-editor.tsx | 20 +- .../components/buttons/sourceControlBase.tsx | 2 +- .../buttons/sourcecontrolbuttons.tsx | 108 +- libs/remix-ui/git/src/components/disabled.tsx | 32 +- .../components/github/repositoryselect.tsx | 3 +- .../github/selectandclonerepositories.tsx | 2 +- libs/remix-ui/git/src/components/gitui.tsx | 18 +- .../components/navigation/branchedetails.tsx | 2 +- .../src/components/navigation/branches.tsx | 4 + .../components/navigation/commitdetails.tsx | 2 +- .../git/src/components/navigation/commits.tsx | 5 +- .../components/navigation/remotesdetails.tsx | 3 +- .../components/navigation/sourcecontrol.tsx | 3 +- .../git/src/components/panels/branches.tsx | 4 +- .../branches/branchdifferencedetails.tsx | 2 +- .../panels/branches/branchdifferences.tsx | 2 +- .../panels/branches/localbranchdetails.tsx | 3 +- .../panels/branches/remotebranchedetails.tsx | 4 +- .../components/panels/commands/pushpull.tsx | 3 +- .../git/src/components/panels/commits.tsx | 2 - .../panels/commits/commitdetailsitem.tsx | 2 +- .../src/components/panels/remoteselect.tsx | 3 +- .../src/components/panels/remotesimport.tsx | 10 +- .../sourcecontrol/sourcecontrolitem.tsx | 3 +- .../sourcecontrol/sourcontrolitembuttons.tsx | 2 +- .../git/src/components/panels/version.tsx | 10 + libs/remix-ui/git/src/index.ts | 2 +- libs/remix-ui/git/src/lib/gitactions.ts | 32 +- libs/remix-ui/git/src/lib/listeners.ts | 42 +- libs/remix-ui/git/src/lib/pluginActions.ts | 12 +- libs/remix-ui/git/src/state/actions.ts | 6 +- libs/remix-ui/git/src/state/context.tsx | 10 +- libs/remix-ui/git/src/state/gitpayload.ts | 17 +- libs/remix-ui/git/src/state/gitreducer.tsx | 17 +- libs/remix-ui/git/src/types/index.ts | 238 +- .../grid-view/src/lib/remix-ui-grid-cell.tsx | 14 +- .../src/lib/remix-ui-grid-section.css | 4 +- .../src/lib/remix-ui-grid-section.tsx | 3 - .../src/lib/components/homeTabFeatured.tsx | 74 +- .../lib/components/homeTabFeaturedPlugins.tsx | 27 +- .../src/lib/components/homeTabFile.tsx | 2 +- .../lib/components/homeTabFileElectron.tsx | 6 +- .../src/lib/components/homeTabGetStarted.tsx | 26 +- .../src/lib/components/homeTabScamAlert.tsx | 11 +- .../src/lib/components/homeTablangOptions.tsx | 2 + .../home-tab/src/lib/remix-ui-home-tab.css | 6 - .../home-tab/src/lib/remix-ui-home-tab.tsx | 2 +- .../src/lib/remix-ui-modal-dialog.tsx | 23 +- .../modal-dialog/src/lib/types/index.ts | 7 +- .../panel/src/lib/plugins/panel-header.tsx | 3 + .../src/lib/permission-dialog.tsx | 4 +- .../InactivePluginCardContainer.tsx | 7 +- libs/remix-ui/plugin-manager/src/types.d.ts | 22 +- libs/remix-ui/remix-ai/src/index.ts | 1 + .../remix-ai/src/lib/components/Default.tsx | 84 + .../src/lib/components/ModelSelection.tsx | 78 + .../remix-ai/src/lib/components/RemixAI.tsx | 15 + libs/remix-ui/remix-ai/src/lib/remix-ai.css | 167 + libs/remix-ui/renderer/src/lib/renderer.tsx | 39 +- .../run-tab/src/lib/actions/account.ts | 5 + .../run-tab/src/lib/actions/actions.ts | 22 +- .../run-tab/src/lib/actions/deploy.ts | 8 +- .../run-tab/src/lib/actions/events.ts | 28 +- .../remix-ui/run-tab/src/lib/actions/index.ts | 11 +- .../run-tab/src/lib/actions/payload.ts | 38 +- .../run-tab/src/lib/components/account.tsx | 55 +- .../src/lib/components/contractDropdownUI.tsx | 2 +- .../src/lib/components/environment.tsx | 2 +- .../lib/components/instanceContainerUI.tsx | 68 +- .../src/lib/components/recorderCardUI.tsx | 3 +- .../run-tab/src/lib/components/settingsUI.tsx | 1 + .../src/lib/components/universalDappUI.tsx | 41 +- .../run-tab/src/lib/constants/index.ts | 4 +- libs/remix-ui/run-tab/src/lib/css/run-tab.css | 5 - .../run-tab/src/lib/reducers/runTab.ts | 99 +- libs/remix-ui/run-tab/src/lib/run-tab.tsx | 9 +- .../run-tab/src/lib/types/blockchain.d.ts | 8 +- .../src/lib/types/execution-context.d.ts | 1 - libs/remix-ui/run-tab/src/lib/types/index.ts | 43 +- .../run-tab/src/lib/types/run-tab.d.ts | 21 +- .../settings/src/lib/remix-ui-settings.tsx | 31 +- .../settings/src/lib/settingsAction.ts | 1 + .../src/lib/components/solidityCompile.tsx | 2 +- .../src/lib/api/compiler-api.ts | 17 +- .../src/lib/compiler-container.tsx | 6 +- .../src/lib/logic/compileTabLogic.ts | 8 +- .../src/lib/solidity-compiler.tsx | 18 +- .../src/lib/components/UmlDownload.tsx | 98 +- .../src/lib/solidity-uml-gen.tsx | 37 +- .../src/lib/remix-ui-static-analyser.tsx | 8 +- libs/remix-ui/tabs/src/lib/remix-ui-tabs.css | 4 +- libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx | 6 +- .../remix-ui-terminal-menu-buttons.tsx | 19 +- .../terminal/src/lib/remix-ui-terminal.tsx | 8 +- .../src/lib/actions/{index.ts => index.tsx} | 29 +- .../workspace/src/lib/actions/workspace.ts | 16 +- .../src/lib/components/createModal.tsx | 51 + .../src/lib/components/electron-menu.tsx | 2 +- .../components/electron-workspace-name.tsx | 2 +- .../components/file-explorer-context-menu.tsx | 5 + .../src/lib/components/file-explorer-menu.tsx | 30 + .../src/lib/components/file-explorer.tsx | 7 + .../components/workspace-hamburger-item.tsx | 2 +- .../lib/components/workspace-hamburger.tsx | 23 + .../workspace/src/lib/contexts/index.ts | 3 + .../src/lib/providers/FileSystemProvider.tsx | 6 + .../workspace/src/lib/remix-ui-workspace.tsx | 213 +- .../remix-ui/workspace/src/lib/types/index.ts | 7 +- .../workspace/src/lib/utils/constants.ts | 30 + .../remix-ui/workspace/src/lib/utils/index.ts | 7 + libs/remix-ui/xterm/src/lib/actions/index.ts | 2 - .../remix-ui-terminal-menu-xterm.tsx | 10 +- .../lib/components/remix-ui-xterminals.tsx | 22 +- libs/remix-url-resolver/package.json | 4 +- libs/remix-ws-templates/package.json | 4 +- .../src/templates/hashchecker/README.md | 8 +- .../remixDefault/contracts/3_Ballot.sol | 54 +- .../src/templates/rln/README.md | 6 +- .../src/templates/semaphore/README.md | 6 +- libs/remixd/package.json | 2 +- libs/remixd/src/bin/remixd.ts | 2 +- package-lock.json | 68610 ++++++++++++++++ package.json | 8 +- release-management.md | 12 +- release-process.md | 4 +- releaseDetails.json | 14 +- team-best-practices.md | 8 +- tsconfig.paths.json | 10 + yarn.lock | 309 +- 524 files changed, 112994 insertions(+), 5766 deletions(-) create mode 100644 .nvmrc rename apps/{etherscan => contract-verification}/.babelrc (51%) rename apps/{etherscan => contract-verification}/.browserslistrc (100%) create mode 100644 apps/contract-verification/.eslintrc rename apps/{etherscan => contract-verification}/.eslintrc.json (100%) create mode 100644 apps/contract-verification/project.json create mode 100644 apps/contract-verification/src/app/App.css create mode 100644 apps/contract-verification/src/app/AppContext.tsx create mode 100644 apps/contract-verification/src/app/ContractVerificationPluginClient.ts create mode 100644 apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts create mode 100644 apps/contract-verification/src/app/Verifiers/BlockscoutVerifier.ts create mode 100644 apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts create mode 100644 apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts create mode 100644 apps/contract-verification/src/app/Verifiers/index.ts create mode 100644 apps/contract-verification/src/app/VerifyFormContext.tsx create mode 100644 apps/contract-verification/src/app/app.tsx create mode 100644 apps/contract-verification/src/app/components/AccordionReceipt.tsx create mode 100644 apps/contract-verification/src/app/components/ConfigInput.tsx create mode 100644 apps/contract-verification/src/app/components/ConstructorArguments.tsx create mode 100644 apps/contract-verification/src/app/components/ContractAddressInput.tsx create mode 100644 apps/contract-verification/src/app/components/ContractDropdown.css create mode 100644 apps/contract-verification/src/app/components/ContractDropdown.tsx create mode 100644 apps/contract-verification/src/app/components/NavMenu.tsx create mode 100644 apps/contract-verification/src/app/components/SearchableChainDropdown.tsx create mode 100644 apps/contract-verification/src/app/components/index.tsx rename apps/{etherscan => contract-verification}/src/app/hooks/useLocalStorage.tsx (78%) create mode 100644 apps/contract-verification/src/app/hooks/useSourcifySupported.tsx create mode 100644 apps/contract-verification/src/app/layouts/Default.tsx create mode 100644 apps/contract-verification/src/app/layouts/index.ts create mode 100644 apps/contract-verification/src/app/routes.tsx create mode 100644 apps/contract-verification/src/app/types/SettingsTypes.ts create mode 100644 apps/contract-verification/src/app/types/ThemeType.ts create mode 100644 apps/contract-verification/src/app/types/VerificationTypes.ts create mode 100644 apps/contract-verification/src/app/types/index.ts create mode 100644 apps/contract-verification/src/app/utils/default-apis.json create mode 100644 apps/contract-verification/src/app/utils/default-settings.ts create mode 100644 apps/contract-verification/src/app/utils/index.ts create mode 100644 apps/contract-verification/src/app/views/LookupView.tsx create mode 100644 apps/contract-verification/src/app/views/ReceiptsView.tsx create mode 100644 apps/contract-verification/src/app/views/SettingsView.tsx create mode 100644 apps/contract-verification/src/app/views/VerifyView.tsx create mode 100644 apps/contract-verification/src/app/views/index.ts rename apps/{etherscan => contract-verification}/src/assets/.gitkeep (100%) rename apps/{etherscan => contract-verification}/src/environments/environment.prod.ts (56%) rename apps/{etherscan => contract-verification}/src/environments/environment.ts (89%) rename apps/{etherscan => contract-verification}/src/favicon.ico (100%) rename apps/{etherscan => contract-verification}/src/index.html (71%) create mode 100644 apps/contract-verification/src/main.tsx rename apps/{etherscan => contract-verification}/src/polyfills.ts (71%) create mode 100644 apps/contract-verification/src/profile.json rename apps/{etherscan => contract-verification}/src/styles.css (100%) rename apps/{etherscan => contract-verification}/tsconfig.app.json (100%) create mode 100644 apps/contract-verification/tsconfig.json rename apps/{etherscan => contract-verification}/webpack.config.js (79%) delete mode 100644 apps/etherscan/project.json delete mode 100644 apps/etherscan/src/app/App.css delete mode 100644 apps/etherscan/src/app/AppContext.tsx delete mode 100644 apps/etherscan/src/app/EtherscanPluginClient.ts delete mode 100644 apps/etherscan/src/app/app.tsx delete mode 100644 apps/etherscan/src/app/components/HeaderWithSettings.tsx delete mode 100644 apps/etherscan/src/app/components/SubmitButton.tsx delete mode 100644 apps/etherscan/src/app/components/index.ts delete mode 100644 apps/etherscan/src/app/layouts/Default.tsx delete mode 100644 apps/etherscan/src/app/layouts/index.ts delete mode 100644 apps/etherscan/src/app/routes.tsx delete mode 100644 apps/etherscan/src/app/types/Receipt.ts delete mode 100644 apps/etherscan/src/app/types/ThemeType.ts delete mode 100644 apps/etherscan/src/app/types/index.ts delete mode 100644 apps/etherscan/src/app/utils/index.ts delete mode 100644 apps/etherscan/src/app/utils/networks.ts delete mode 100644 apps/etherscan/src/app/utils/scripts.ts delete mode 100644 apps/etherscan/src/app/utils/utilities.ts delete mode 100644 apps/etherscan/src/app/utils/verify.ts delete mode 100644 apps/etherscan/src/app/views/CaptureKeyView.tsx delete mode 100644 apps/etherscan/src/app/views/ErrorView.tsx delete mode 100644 apps/etherscan/src/app/views/HomeView.tsx delete mode 100644 apps/etherscan/src/app/views/ReceiptsView.tsx delete mode 100644 apps/etherscan/src/app/views/VerifyView.tsx delete mode 100644 apps/etherscan/src/app/views/index.ts delete mode 100644 apps/etherscan/src/main.tsx delete mode 100644 apps/etherscan/src/profile.json rename apps/{etherscan => remix-dapp}/.eslintrc (100%) create mode 100644 apps/remix-dapp/README.md create mode 100644 apps/remix-dapp/package.json create mode 100644 apps/remix-dapp/project.json create mode 100644 apps/remix-dapp/src/App.css create mode 100644 apps/remix-dapp/src/App.tsx create mode 100644 apps/remix-dapp/src/actions/index.ts create mode 100644 apps/remix-dapp/src/assets/instance.json create mode 100644 apps/remix-dapp/src/assets/logo.png create mode 100644 apps/remix-dapp/src/components/ContractGUI/index.tsx create mode 100644 apps/remix-dapp/src/components/DappTop/index.tsx create mode 100644 apps/remix-dapp/src/components/DragBar/index.tsx create mode 100644 apps/remix-dapp/src/components/Home/mobile.tsx create mode 100644 apps/remix-dapp/src/components/Home/pc.tsx create mode 100644 apps/remix-dapp/src/components/SettingsUI/account.tsx create mode 100644 apps/remix-dapp/src/components/SettingsUI/gasPrice.tsx create mode 100644 apps/remix-dapp/src/components/SettingsUI/index.tsx create mode 100644 apps/remix-dapp/src/components/SettingsUI/locale.tsx create mode 100644 apps/remix-dapp/src/components/SettingsUI/network.tsx create mode 100644 apps/remix-dapp/src/components/SettingsUI/theme.tsx create mode 100644 apps/remix-dapp/src/components/SettingsUI/value.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/ChechTxStatus.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/Context.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/RenderCall.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/Table.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/TxList.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/index.css create mode 100644 apps/remix-dapp/src/components/UiTerminal/index.tsx create mode 100644 apps/remix-dapp/src/components/UiTerminal/types.ts create mode 100644 apps/remix-dapp/src/components/UiTerminal/utils.ts create mode 100644 apps/remix-dapp/src/components/UniversalDappUI/index.css create mode 100644 apps/remix-dapp/src/components/UniversalDappUI/index.tsx create mode 100644 apps/remix-dapp/src/components/UniversalDappUI/lowLevelInteractions.tsx create mode 100644 apps/remix-dapp/src/contexts/index.ts create mode 100644 apps/remix-dapp/src/index.css create mode 100644 apps/remix-dapp/src/index.html create mode 100644 apps/remix-dapp/src/locales/en/index.ts create mode 100644 apps/remix-dapp/src/locales/en/terminal.json create mode 100644 apps/remix-dapp/src/locales/en/udapp.json create mode 100644 apps/remix-dapp/src/locales/es/index.ts create mode 100644 apps/remix-dapp/src/locales/es/terminal.json create mode 100644 apps/remix-dapp/src/locales/es/udapp.json create mode 100644 apps/remix-dapp/src/locales/fr/index.ts create mode 100644 apps/remix-dapp/src/locales/fr/terminal.json create mode 100644 apps/remix-dapp/src/locales/fr/udapp.json create mode 100644 apps/remix-dapp/src/locales/it/index.ts create mode 100644 apps/remix-dapp/src/locales/it/terminal.json create mode 100644 apps/remix-dapp/src/locales/it/udapp.json create mode 100644 apps/remix-dapp/src/locales/zh/index.ts create mode 100644 apps/remix-dapp/src/locales/zh/terminal.json create mode 100644 apps/remix-dapp/src/locales/zh/udapp.json create mode 100644 apps/remix-dapp/src/main.tsx create mode 100644 apps/remix-dapp/src/reducers/state.ts create mode 100644 apps/remix-dapp/src/types.ts create mode 100644 apps/remix-dapp/src/utils/buildData.ts create mode 100644 apps/remix-dapp/src/utils/chains.ts create mode 100644 apps/remix-dapp/src/utils/constants.ts create mode 100644 apps/remix-dapp/src/utils/metamask.ts create mode 100644 apps/remix-dapp/src/utils/tools.ts create mode 100644 apps/remix-dapp/src/utils/txRunner.ts create mode 100644 apps/remix-dapp/src/utils/walletConnect.ts create mode 100644 apps/remix-dapp/src/vite-env.d.ts create mode 100644 apps/remix-dapp/tsconfig.app.json rename apps/{etherscan => remix-dapp}/tsconfig.json (100%) create mode 100644 apps/remix-dapp/webpack.config.js create mode 100644 apps/remix-dapp/yarn.lock create mode 100644 apps/remix-ide-e2e/src/tests/matomo.test.ts create mode 100644 apps/remix-ide/ci/update_desktop_release_assets.ts create mode 100644 apps/remix-ide/src/app/plugins/electron/appUpdaterPlugin.ts create mode 100644 apps/remix-ide/src/app/plugins/electron/foundryPlugin.ts create mode 100644 apps/remix-ide/src/app/plugins/electron/hardhatPlugin.ts create mode 100644 apps/remix-ide/src/app/plugins/electron/remixAIDesktopPlugin.tsx create mode 100644 apps/remix-ide/src/app/plugins/electron/slitherPlugin.ts create mode 100644 apps/remix-ide/src/app/plugins/remixAIPlugin.tsx create mode 100644 apps/remix-ide/src/app/plugins/remixGuide.css delete mode 100644 apps/remix-ide/src/app/plugins/solcoderAI.tsx delete mode 100644 apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx delete mode 100644 apps/remix-ide/src/app/providers/injected-ephemery-testnet-provider.tsx delete mode 100644 apps/remix-ide/src/app/providers/injected-optimism-provider.tsx delete mode 100644 apps/remix-ide/src/app/providers/injected-provider-trustwallet.tsx delete mode 100644 apps/remix-ide/src/app/providers/injected-skale-chaos-testnet-provider.tsx create mode 100644 apps/remix-ide/src/app/tabs/locales/en/gitui.json create mode 100644 apps/remix-ide/src/app/tabs/locales/en/vyper.json rename apps/remix-ide/src/app/udapp/{run-tab.js => run-tab.tsx} (77%) create mode 100644 apps/remix-ide/src/assets/img/gnosis_chain.png create mode 100644 apps/remix-ide/src/assets/img/remi-prof.webp create mode 100644 apps/remixdesktop/TEST.md create mode 100644 apps/remixdesktop/after-pack.js create mode 100644 apps/remixdesktop/afterbuild.js create mode 100644 apps/remixdesktop/aftersign.js create mode 100644 apps/remixdesktop/alpha.json create mode 100644 apps/remixdesktop/beta.json create mode 100755 apps/remixdesktop/circom-download/latest/circom-linux-amd64 create mode 100644 apps/remixdesktop/entitlements.mac.plist create mode 100644 apps/remixdesktop/esbuild.js create mode 100644 apps/remixdesktop/insiders.json create mode 100644 apps/remixdesktop/latest.json create mode 100644 apps/remixdesktop/nightwatch.conf.js create mode 100644 apps/remixdesktop/notarizedmg.sh create mode 100644 apps/remixdesktop/package-lock.json create mode 100755 apps/remixdesktop/run_ci_test.sh create mode 100755 apps/remixdesktop/run_git_ui_isogit_tests.sh create mode 100755 apps/remixdesktop/rundist.bash create mode 100755 apps/remixdesktop/rundist_esbuild.bash create mode 100755 apps/remixdesktop/rundist_tsc.bash create mode 100755 apps/remixdesktop/rundist_webpack.bash create mode 100755 apps/remixdesktop/splice_tests.js create mode 100644 apps/remixdesktop/src/lib/InferenceServerManager.ts create mode 100644 apps/remixdesktop/src/lib/remixd.ts create mode 100644 apps/remixdesktop/src/lib/utils.ts create mode 100644 apps/remixdesktop/src/plugins/appUpdater.ts create mode 100644 apps/remixdesktop/src/plugins/foundryPlugin.ts create mode 100644 apps/remixdesktop/src/plugins/hardhatPlugin.ts create mode 100644 apps/remixdesktop/src/plugins/remixAIDektop.ts create mode 100644 apps/remixdesktop/src/plugins/slitherPlugin.ts create mode 100644 apps/remixdesktop/src/utils/matamo.ts create mode 100644 apps/remixdesktop/test/cache_dir/remixdesktop.json create mode 100644 apps/remixdesktop/test/lib/git.ts create mode 100644 apps/remixdesktop/test/nighwatch.app.ts create mode 100644 apps/remixdesktop/test/tests/app/compiler.test.ts create mode 100644 apps/remixdesktop/test/tests/app/externaleditor.test.ts create mode 100644 apps/remixdesktop/test/tests/app/foundry.test.ts create mode 100644 apps/remixdesktop/test/tests/app/gist.test.ts create mode 100644 apps/remixdesktop/test/tests/app/git-ui.test.ts create mode 100644 apps/remixdesktop/test/tests/app/git-ui_2.test.ts create mode 100644 apps/remixdesktop/test/tests/app/git-ui_3.test.ts create mode 100644 apps/remixdesktop/test/tests/app/git-ui_4.test.ts create mode 100644 apps/remixdesktop/test/tests/app/git.test.ts create mode 100644 apps/remixdesktop/test/tests/app/github.test.ts create mode 100644 apps/remixdesktop/test/tests/app/github_2.test.ts create mode 100644 apps/remixdesktop/test/tests/app/github_3.test.ts create mode 100644 apps/remixdesktop/test/tests/app/hardhat.test.ts create mode 100644 apps/remixdesktop/test/tests/app/offline.test.ts create mode 100644 apps/remixdesktop/test/tests/app/search.test.ts create mode 100644 apps/remixdesktop/test/tests/app/slitherlinux.test.ts create mode 100644 apps/remixdesktop/test/tests/app/templates.test.ts create mode 100644 apps/remixdesktop/test/tests/app/xterm.test.ts create mode 100644 apps/remixdesktop/test/tests/app/xtermwin.test.ts create mode 100644 apps/remixdesktop/test/types/index.d.ts create mode 100644 apps/remixdesktop/tsconfig.e2e.json create mode 100644 apps/remixdesktop/webpack.config.js create mode 100644 libs/remix-ai-core/.eslintrc create mode 100644 libs/remix-ai-core/README.md create mode 100644 libs/remix-ai-core/package-lock.json create mode 100644 libs/remix-ai-core/project.json create mode 100644 libs/remix-ai-core/src/agents/codeExplainAgent.ts create mode 100644 libs/remix-ai-core/src/agents/completionAgent.ts create mode 100644 libs/remix-ai-core/src/agents/securityAgent.ts create mode 100644 libs/remix-ai-core/src/helpers/dowload_model.ts create mode 100644 libs/remix-ai-core/src/helpers/inferenceServerReleases.ts create mode 100644 libs/remix-ai-core/src/index.ts create mode 100644 libs/remix-ai-core/src/inferencers/remote/remoteInference.ts create mode 100644 libs/remix-ai-core/src/prompts/chat.ts create mode 100644 libs/remix-ai-core/src/prompts/completionPrompts.ts create mode 100644 libs/remix-ai-core/src/prompts/promptBuilder.ts create mode 100644 libs/remix-ai-core/src/types/constants.ts create mode 100644 libs/remix-ai-core/src/types/models.ts create mode 100644 libs/remix-ai-core/src/types/types.ts create mode 100644 libs/remix-ai-core/tsconfig.json create mode 100644 libs/remix-ai-core/tsconfig.lib.json create mode 100644 libs/remix-api/src/lib/plugins/fs-api.ts create mode 100644 libs/remix-api/src/lib/plugins/git-api.ts create mode 100644 libs/remix-api/src/lib/plugins/remixAIDesktop-api.ts create mode 100644 libs/remix-api/src/lib/plugins/remixai-api.ts create mode 100644 libs/remix-api/src/lib/plugins/terminal-api.ts create mode 100644 libs/remix-api/src/lib/types/git.ts create mode 100644 libs/remix-git/index.ts create mode 100644 libs/remix-git/src/isogit.ts delete mode 100644 libs/remix-ui/editor/src/lib/providers/completionTimer.ts create mode 100644 libs/remix-ui/git/src/components/panels/version.tsx create mode 100644 libs/remix-ui/remix-ai/src/index.ts create mode 100644 libs/remix-ui/remix-ai/src/lib/components/Default.tsx create mode 100644 libs/remix-ui/remix-ai/src/lib/components/ModelSelection.tsx create mode 100644 libs/remix-ui/remix-ai/src/lib/components/RemixAI.tsx create mode 100644 libs/remix-ui/remix-ai/src/lib/remix-ai.css rename libs/remix-ui/workspace/src/lib/actions/{index.ts => index.tsx} (96%) create mode 100644 libs/remix-ui/workspace/src/lib/components/createModal.tsx create mode 100644 package-lock.json diff --git a/.circleci/config.yml b/.circleci/config.yml index afe5c5576b..26bb163e27 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -105,6 +105,51 @@ jobs: paths: - "persist" + test-remixdesktop-linux: + machine: + image: ubuntu-2004:current + resource_class: + xlarge + working_directory: ~/remix-project + parallelism: 10 + steps: + - run: ldd --version + - checkout + - attach_workspace: + at: . + - run: unzip ./persist/desktopbuild.zip + - run: + command: | + nvm install 20.2 + nvm use 20.2 + node -v + npm install --global yarn node-gyp + python -m pip install --upgrade pip + pip install setuptools + mkdir apps/remixdesktop/build + cp -r dist/apps/remix-ide apps/remixdesktop/build + cd apps/remixdesktop/ + yarn add node-pty + yarn --ignore-optional + yarn add @remix-project/remix-ws-templates + ./rundist.bash + - run: + name: "Run tests" + command: | + nvm use 20.2 + cd apps/remixdesktop/ + ./run_ci_test.sh + - run: + name: "Run isogit tests" + command: | + nvm use 20.2 + cd apps/remixdesktop/ + ./run_git_ui_isogit_tests.sh + - store_test_results: + path: ./apps/remixdesktop/reports/tests + - store_artifacts: + path: ./apps/remixdesktop/reports/screenshots + build-remixdesktop-linux: machine: image: ubuntu-2004:current @@ -119,22 +164,37 @@ jobs: - run: unzip ./persist/desktopbuild.zip - run: command: | + nvm install 20.2 + nvm use 20.2 node -v + npm install --global yarn node-gyp + python -m pip install --upgrade pip + pip install setuptools mkdir apps/remixdesktop/build cp -r dist/apps/remix-ide apps/remixdesktop/build cd apps/remixdesktop/ yarn add node-pty yarn --ignore-optional yarn add @remix-project/remix-ws-templates - PUBLISH_FOR_PULL_REQUEST='true' yarn dist + ./rundist.bash rm -rf release/*-unpacked - save_cache: key: remixdesktop-linux-deps-{{ checksum "apps/remixdesktop/yarn.lock" }} paths: - apps/remixdesktop/node_modules + - run: + name: "remove unnecessary files" + command: | + rm -rf ~/remix-project/apps/remixdesktop/release/.icon* + rm -rf ~/remix-project/apps/remixdesktop/release/builder* - store_artifacts: path: apps/remixdesktop/release/ destination: remixdesktop-linux + - persist_to_workspace: + root: apps/remixdesktop + paths: + - "release" + build-remixdesktop-windows: executor: @@ -147,39 +207,107 @@ jobs: - attach_workspace: at: . - run: unzip ./persist/desktopbuild.zip - - restore_cache: - key: node-20-windows-v3 + - run: command: | - nvm install 20.0.0 - nvm use 20.0.0 + nvm install 20.2 + nvm use 20.2 node -v npx -v npm install --global yarn + npm install --global node-gyp yarn -v - - save_cache: - key: node-20-windows-v3 - paths: - - /ProgramData/nvm/v20.0.0 - - restore_cache: - keys: - - remixdesktop-windows-deps-{{ checksum "apps/remixdesktop/yarn.lock" }} + - run: command: | mkdir apps/remixdesktop/build cp -r dist/apps/remix-ide apps/remixdesktop/build cd apps/remixdesktop/ + python -m pip install --upgrade pip + pip install setuptools yarn - PUBLISH_FOR_PULL_REQUEST='true' yarn dist + ./rundist.bash rm -rf release/*-unpacked - - save_cache: - key: remixdesktop-windows-deps-{{ checksum "apps/remixdesktop/yarn.lock" }} - paths: - - apps/remixdesktop/node_modules + - persist_to_workspace: root: apps/remixdesktop paths: - "release" + + test-remixdesktop-windows: + executor: + name: win/default # executor type + size: xlarge # can be medium, large, xlarge, 2xlarge + shell: bash.exe + parallelism: 10 + working_directory: ~/remix-project + steps: + - run: + name: Restart local mstsc + command: psexec64.exe -accepteula -nobanner -i 0 mstsc /v:localhost /w:2560 /h:1140 + background: true + shell: powershell.exe + - run: + name: Naive impl to wait until the screen stretches + command: Start-Sleep 5 + shell: powershell.exe + - run: + name: Get screen info + command: | + Add-Type -AssemblyName System.Windows.Forms + [System.Windows.Forms.Screen]::AllScreens | fl * + shell: powershell.exe + - checkout + - attach_workspace: + at: . + - run: unzip ./persist/desktopbuild.zip + + - run: + command: | + nvm install 20.2 + nvm use 20.2 + node -v + npx -v + npm install --global yarn + npm install --global node-gyp + yarn -v + - run: + name: start selenium + command: | + cd "apps/remixdesktop/" + yarn -v + shell: powershell.exe + + - run: + command: | + mkdir apps/remixdesktop/build + cp -r dist/apps/remix-ide apps/remixdesktop/build + cd apps/remixdesktop/ + nvm use 20.2 + node -v + python -m pip install --upgrade pip + pip install setuptools + yarn + ./rundist.bash + - run: + name: run tests + command: | + cd "apps/remixdesktop/" + yarn -v + sleep 15 + ./run_ci_test.sh + - run: + name: "Run isogit tests" + command: | + cd apps/remixdesktop/ + yarn -v + sleep 15 + ./run_git_ui_isogit_tests.sh + - store_test_results: + path: ./apps/remixdesktop/reports/tests + - store_artifacts: + path: ./apps/remixdesktop/reports/screenshots + # see https://docs.digicert.com/en/software-trust-manager/ci-cd-integrations/script-integrations/github-integration-ksp.html sign-remixdesktop-windows: executor: win/default # executor type @@ -230,28 +358,66 @@ jobs: command: | Get-ChildItem -Path 'C:\Program Files (x86)\Windows Kits\10\App Certification Kit' -Filter signtool.exe -Recurse - run: - name: "Signtool-Signing" + name: read env shell: powershell.exe command: | - & $env:Signtool sign /sha1 $env:SM_CODE_SIGNING_CERT_SHA1_HASH /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 $env:RemixSetupExe + # Specify the path to your package.json file + $packageJsonPath = "C:\Users\circleci\remix-project\apps\remixdesktop\package.json" + + # Check if the file exists + if (Test-Path $packageJsonPath) { + # Read the content of the package.json file + $packageJsonContent = Get-Content $packageJsonPath -Raw | ConvertFrom-Json + + # Check if the 'version' field exists in the package.json + if ($packageJsonContent.'version' -ne $null) { + # Store the version value in an environment variable + $version = $packageJsonContent.version + $file = "C:\Users\circleci\remix-project\release\Remix-Desktop-Setup-$($version).exe" + Write-Host "Version $(file) stored in PACKAGE_VERSION environment variable." + "Set-Variable -Name 'PACKAGE_VERSION' -Value '$file' -Scope Global" > SetEnvVars.ps1 + dir Env: + } else { + Write-Host "Error: 'version' field not found in package.json." + } + } else { + Write-Host "Error: package.json file not found at $packageJsonPath." + } + - run: + name: "Signtool-Signing" + shell: powershell.exe + command: | + . .\SetEnvVars.ps1 + dir Env: + & $env:Signtool sign /sha1 $env:SM_CODE_SIGNING_CERT_SHA1_HASH /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 $PACKAGE_VERSION - run: name: "Signtool-Verification" shell: powershell.exe command: | - $verify_output = $(& $env:Signtool verify /v /pa $env:RemixSetupExe) + . .\SetEnvVars.ps1 + $verify_output = $(& $env:Signtool verify /v /pa $PACKAGE_VERSION) echo ${verify_output} if (!$verify_output.Contains("Number of files successfully Verified: 1")) { echo 'Verification failed' exit 1 } + - run: + name: "remove unnecessary files" + shell: bash.exe + command: | + rm -rf ~/remix-project/release/.icon* + rm -rf ~/remix-project/release/builder* - store_artifacts: path: ~/remix-project/release/ destination: remixdesktop-windows + - persist_to_workspace: + root: ~/remix-project/ + paths: + - "release" environment: SM_CLIENT_CERT_FILE: 'C:\Certificate_pkcs12.p12' Signtool: 'C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe' SSM: 'C:\Program Files\DigiCert\DigiCert One Signing Manager Tools' - RemixSetupExe: 'C:\Users\circleci\remix-project\release\Remix IDE.exe' build-remixdesktop-mac: macos: @@ -259,23 +425,41 @@ jobs: resource_class: macos.m1.large.gen1 working_directory: ~/remix-project + parameters: + arch: + type: string steps: - checkout - attach_workspace: at: . + - run: + name: Install Apple Certificate + command: | + echo $APPLE_CERTIFICATE_BASE64 | base64 --decode > /tmp/certificate.p12 + security create-keychain -p ci-password build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p ci-password build.keychain + curl -o DeveloperIDG2CA.cer "https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer" + sudo security import DeveloperIDG2CA.cer -k /Library/Keychains/System.keychain -T /usr/bin/codesign + sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain DeveloperIDG2CA.cer + security import /tmp/certificate.p12 -k build.keychain -P $APPLE_CERTIFICATE_PASSWORD -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple: -s -k ci-password build.keychain + security find-identity -v -p codesigning - run: unzip ./persist/desktopbuild.zip - run: command: | ls -la dist/apps/remix-ide - nvm install 20.0.0 - nvm use 20.0.0 + nvm install 20.2 + nvm use 20.2 - restore_cache: keys: - remixdesktop-deps-mac-{{ checksum "apps/remixdesktop/yarn.lock" }} - run: command: | - nvm use 20.0.0 - cd apps/remixdesktop && yarn + nvm use 20.2 + cd apps/remixdesktop + yarn || yarn + find ./node_modules yarn add @remix-project/remix-ws-templates - save_cache: key: remixdesktop-deps-mac-{{ checksum "apps/remixdesktop/yarn.lock" }} @@ -284,20 +468,120 @@ jobs: # use USE_HARD_LINK=false https://github.com/electron-userland/electron-builder/issues/3179 - run: command: | - nvm use 20.0.0 + nvm use 20.2 mkdir apps/remixdesktop/build cp -r dist/apps/remix-ide apps/remixdesktop/build cd apps/remixdesktop yarn - yarn installRipGrepMacOXarm64 - PUBLISH_FOR_PULL_REQUEST='true' USE_HARD_LINKS=false yarn dist --mac --arm64 - yarn installRipGrepMacOXx64 - PUBLISH_FOR_PULL_REQUEST='true' USE_HARD_LINKS=false yarn dist --mac --x64 - rm -rf release/mac* + - run: + command: | + nvm use 20.2 + cd apps/remixdesktop + yarn installRipGrepMacOX<< parameters.arch >> + PUBLISH_FOR_PULL_REQUEST='false' USE_HARD_LINKS=false ./rundist.bash --<< parameters.arch >> + if [ -f release/latest-mac.yml ]; then + cat release/latest-mac.yml + mv release/latest-mac.yml release/latest-mac-<< parameters.arch >>.yml + fi + find build + - run: + name: Notarize the app + command: | + brew install jq + cd apps/remixdesktop + zsh notarizedmg.sh + - run: + name: "remove unnecessary files" + command: | + rm -rf ~/remix-project/apps/remixdesktop/release/.icon* + rm -rf ~/remix-project/apps/remixdesktop/release/builder* + rm -rf ~/remix-project/apps/remixdesktop/release/*.blockmap + rm -rf ~/remix-project/apps/remixdesktop/release/_.* - store_artifacts: path: apps/remixdesktop/release/ destination: remixdesktop-mac - + - persist_to_workspace: + root: apps/remixdesktop + paths: + - "release" + test-remixdesktop-mac: + macos: + xcode: 14.2.0 + resource_class: + macos.m1.large.gen1 + working_directory: ~/remix-project + parallelism: 10 + steps: + - checkout + - attach_workspace: + at: . + - run: unzip ./persist/desktopbuild.zip + - run: + command: | + ls -la dist/apps/remix-ide + nvm install 20.2 + nvm use 20.2 + - restore_cache: + keys: + - remixdesktop-deps-mac-{{ checksum "apps/remixdesktop/yarn.lock" }} + - run: + command: | + nvm use 20.2 + cd apps/remixdesktop + yarn || yarn + yarn add @remix-project/remix-ws-templates + - save_cache: + key: remixdesktop-deps-mac-{{ checksum "apps/remixdesktop/yarn.lock" }} + paths: + - apps/remixdesktop/node_modules + - run: + command: | + nvm use 20.2 + mkdir apps/remixdesktop/build + cp -r dist/apps/remix-ide apps/remixdesktop/build + cd apps/remixdesktop + yarn + - run: + command: | + nvm use 20.2 + cd apps/remixdesktop + yarn installRipGrepMacOXarm64 + PUBLISH_FOR_PULL_REQUEST='false' DO_NOT_NOTARIZE='true' USE_HARD_LINKS=false ./rundist.bash --arm64 + find build + - run: + name: "Run tests" + command: | + nvm use 20.2 + cd apps/remixdesktop + ./run_ci_test.sh + - run: + name: "Run isogit tests" + command: | + nvm use 20.2 + cd apps/remixdesktop + ./run_git_ui_isogit_tests.sh + - store_test_results: + path: ./apps/remixdesktop/reports/tests + - store_artifacts: + path: ./apps/remixdesktop/reports/screenshots + + uploadartifacts: + docker: + - image: cimg/node:20.0.0-browsers + resource_class: + xlarge + working_directory: ~/remix-project + steps: + - checkout + - attach_workspace: + at: . + - restore_cache: + keys: + - v1-deps-{{ checksum "yarn.lock" }} + - run: yarn + - run: + name: "Upload Artifacts" + command: npx ts-node apps/remix-ide/ci/update_desktop_release_assets.ts lint: docker: - image: cimg/node:20.0.0-browsers @@ -504,10 +788,16 @@ workflows: - build-desktop: filters: branches: - only: [/.*desktop.*/] + only: [/.*desktop.*/, 'remix_beta'] - build-remixdesktop-mac: requires: - build-desktop + matrix: + parameters: + arch: ["arm64", "x64"] + - test-remixdesktop-mac: + requires: + - build-desktop - build-remixdesktop-windows: requires: - build-desktop @@ -517,6 +807,23 @@ workflows: - build-remixdesktop-linux: requires: - build-desktop + - test-remixdesktop-linux: + requires: + - build-desktop + - test-remixdesktop-windows: + requires: + - build-desktop + - uploadartifacts: + requires: + - build-remixdesktop-mac + - build-remixdesktop-linux + - sign-remixdesktop-windows + - test-remixdesktop-windows + - test-remixdesktop-linux + - test-remixdesktop-mac + filters: + branches: + only: [/.*desktop.*/] - build-plugin: matrix: parameters: diff --git a/.github/workflows/pr-reminder.yml b/.github/workflows/pr-reminder.yml index eaedd51125..172ffc6011 100644 --- a/.github/workflows/pr-reminder.yml +++ b/.github/workflows/pr-reminder.yml @@ -14,4 +14,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} - freeze-date: '2024-09-09T18:00:00Z' + freeze-date: '2024-10-21T18:00:00Z' diff --git a/.gitignore b/.gitignore index ef8b36916c..9e40832324 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ soljson.js *_group*.*.ts *_group*.ts stats.json +release # compiled output @@ -61,9 +62,10 @@ testem.log apps/remixdesktop/.webpack apps/remixdesktop/out apps/remixdesktop/release/ +apps/remixdesktop/build*/ apps/remix-ide/src/assets/list.json apps/remix-ide/src/assets/esbuild.wasm apps/remixdesktop/build* -apps/remixdesktop/reports/ +apps/remixdesktop/reports apps/remixdesktop/logs/ logs diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..9a2a0e219c --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20 diff --git a/.prettierrc.json b/.prettierrc.json index 07681f5b0a..967e2ecc37 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,9 +1,7 @@ { "tabWidth": 2, "printWidth": 500, - "bracketSpacing": false, "useTabs": false, "semi": false, - "singleQuote": true, - "bracketSpacing": false + "singleQuote": true } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a603101311..e23efaf847 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,7 +27,7 @@ Then you can replace the string with a intl component. The `id` prop will be the + ``` -In some cases, jsx maybe not acceptable, you can use `intl.formatMessage` . +In some cases, jsx maybe not be acceptable, you can use `intl.formatMessage` . ```jsx ``` -You can't be sure there is a match key in locale file or not. So it will be better to provide a `defaultMessage` prop. +You can't be sure whether there is a match key in locale file or not. So it will be better to provide a `defaultMessage` prop. ### Should I update the non-english locale json files? You probably will have this question when you are updating the english locale json files. diff --git a/README.md b/README.md index 9059e6318c..113c4b897c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat&logo=github)](https://github.com/ethereum/remix-project/blob/master/CONTRIBUTING.md) [![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) +[![GitHub](https://img.shields.io/github/license/ethereum/remix-project)](https://github.com/ethereum/remix-project/blob/master/LICENSE) [![Discord](https://img.shields.io/badge/join-discord-brightgreen.svg?style=flat&logo=discord)](https://discord.gg/mh9hFCKkEq) [![X Follow](https://img.shields.io/twitter/follow/ethereumremix?style=flat&logo=x&color=green)](https://x.com/ethereumremix) @@ -68,7 +68,7 @@ git clone https://github.com/ethereum/remix-project.git 2. Install dependencies: `yarn install` or simply run `yarn` 3. Build Remix libraries: `yarn run build:libs` 4. Build Remix project: `yarn build` -5. Build and run project server: `yarn serve`. Optionally, run `yarn serve:hot` to enable hot module reload for frontend updates. +5. Build and run project server: `yarn serve`. Optionally, run `yarn serve:hot` to enable hot module to reload for frontend updates. Open `http://127.0.0.1:8080` in your browser to load Remix IDE locally. diff --git a/apps/circuit-compiler/src/app/components/container.tsx b/apps/circuit-compiler/src/app/components/container.tsx index 3a18465fa4..2c7cc41e16 100644 --- a/apps/circuit-compiler/src/app/components/container.tsx +++ b/apps/circuit-compiler/src/app/components/container.tsx @@ -74,7 +74,7 @@ export function Container () { explain why the error occurred and how to fix it. ` // @ts-ignore - await circuitApp.plugin.call('solcoder', 'error_explaining', message) + await circuitApp.plugin.call('remixAI', 'error_explaining', message) } else { const message = ` error message: ${error} @@ -82,7 +82,7 @@ export function Container () { explain why the error occurred and how to fix it. ` // @ts-ignore - await circuitApp.plugin.call('solcoder', 'error_explaining', message) + await circuitApp.plugin.call('remixAI', 'error_explaining', message) } } else { const error = report.message @@ -92,7 +92,7 @@ export function Container () { explain why the error occurred and how to fix it. ` // @ts-ignore - await circuitApp.plugin.call('solcoder', 'error_explaining', message) + await circuitApp.plugin.call('remixAI', 'error_explaining', message) } } diff --git a/apps/etherscan/.babelrc b/apps/contract-verification/.babelrc similarity index 51% rename from apps/etherscan/.babelrc rename to apps/contract-verification/.babelrc index e37036ce66..6df3e5be52 100644 --- a/apps/etherscan/.babelrc +++ b/apps/contract-verification/.babelrc @@ -1,9 +1,5 @@ { - "presets": ["@babel/preset-env", ["@babel/preset-react", - {"runtime": "automatic"} - ]], + "presets": ["@babel/preset-env", ["@babel/preset-react", { "runtime": "automatic" }]], "plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-transform-runtime", "@babel/plugin-proposal-nullish-coalescing-operator"], - "ignore": [ - "**/node_modules/**" - ] -} \ No newline at end of file + "ignore": ["**/node_modules/**"] +} diff --git a/apps/etherscan/.browserslistrc b/apps/contract-verification/.browserslistrc similarity index 100% rename from apps/etherscan/.browserslistrc rename to apps/contract-verification/.browserslistrc diff --git a/apps/contract-verification/.eslintrc b/apps/contract-verification/.eslintrc new file mode 100644 index 0000000000..be97c53fbb --- /dev/null +++ b/apps/contract-verification/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.json" +} diff --git a/apps/etherscan/.eslintrc.json b/apps/contract-verification/.eslintrc.json similarity index 100% rename from apps/etherscan/.eslintrc.json rename to apps/contract-verification/.eslintrc.json diff --git a/apps/contract-verification/project.json b/apps/contract-verification/project.json new file mode 100644 index 0000000000..dee28fe326 --- /dev/null +++ b/apps/contract-verification/project.json @@ -0,0 +1,69 @@ +{ + "name": "contract-verification", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/contract-verification/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "development", + "options": { + "compiler": "babel", + "outputPath": "dist/apps/contract-verification", + "index": "apps/contract-verification/src/index.html", + "baseHref": "./", + "main": "apps/contract-verification/src/main.tsx", + "polyfills": "apps/contract-verification/src/polyfills.ts", + "tsConfig": "apps/contract-verification/tsconfig.app.json", + "assets": [ + "apps/contract-verification/src/favicon.ico", + "apps/contract-verification/src/assets", + "apps/contract-verification/src/profile.json" + ], + "styles": ["apps/contract-verification/src/styles.css"], + "scripts": [], + "webpackConfig": "apps/contract-verification/webpack.config.js" + }, + "configurations": { + "development": { + }, + "production": { + "fileReplacements": [ + { + "replace": "apps/contract-verification/src/environments/environment.ts", + "with": "apps/contract-verification/src/environments/environment.prod.ts" + } + ] + } + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/contract-verification/**/*.ts"], + "eslintConfig": "apps/contract-verification/.eslintrc" + } + }, + "serve": { + "executor": "@nrwl/webpack:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "contract-verification:build", + "hmr": true, + "baseHref": "/" + }, + "configurations": { + "development": { + "buildTarget": "contract-verification:build:development", + "port": 5003 + }, + "production": { + "buildTarget": "contract-verification:build:production" + } + } + } + }, + "tags": [] +} diff --git a/apps/contract-verification/src/app/App.css b/apps/contract-verification/src/app/App.css new file mode 100644 index 0000000000..e036149047 --- /dev/null +++ b/apps/contract-verification/src/app/App.css @@ -0,0 +1,14 @@ +html, body, #root { + height: 100%; +} + +body { + margin: 0; +} + +a:focus { + background-color: var(bg-light) !important; +} + +.fa-arrow-up-right-from-square::before { content: "\f08e"; } +.fa-xmark::before { content: "\f00d"; } diff --git a/apps/contract-verification/src/app/AppContext.tsx b/apps/contract-verification/src/app/AppContext.tsx new file mode 100644 index 0000000000..e4ffda0cee --- /dev/null +++ b/apps/contract-verification/src/app/AppContext.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import type { ThemeType, Chain, SubmittedContracts, ContractVerificationSettings } from './types' +import { CompilerAbstract } from '@remix-project/remix-solidity' +import { ContractVerificationPluginClient } from './ContractVerificationPluginClient' +import { ContractDropdownSelection } from './components/ContractDropdown' + +// Define the type for the context +type AppContextType = { + themeType: ThemeType + setThemeType: (themeType: ThemeType) => void + clientInstance: ContractVerificationPluginClient + settings: ContractVerificationSettings + setSettings: React.Dispatch> + chains: Chain[] + compilationOutput: { [key: string]: CompilerAbstract } | undefined + submittedContracts: SubmittedContracts + setSubmittedContracts: React.Dispatch> +} + +// Provide a default value with the appropriate types +const defaultContextValue: AppContextType = { + themeType: 'dark', + setThemeType: (themeType: ThemeType) => {}, + clientInstance: {} as ContractVerificationPluginClient, + settings: { chains: {} }, + setSettings: () => {}, + chains: [], + compilationOutput: undefined, + submittedContracts: {}, + setSubmittedContracts: (submittedContracts: SubmittedContracts) => {}, +} + +// Create the context with the type +export const AppContext = React.createContext(defaultContextValue) diff --git a/apps/contract-verification/src/app/ContractVerificationPluginClient.ts b/apps/contract-verification/src/app/ContractVerificationPluginClient.ts new file mode 100644 index 0000000000..8554cb29f6 --- /dev/null +++ b/apps/contract-verification/src/app/ContractVerificationPluginClient.ts @@ -0,0 +1,18 @@ +import { PluginClient } from '@remixproject/plugin' +import { createClient } from '@remixproject/plugin-webview' +import EventManager from 'events' + +export class ContractVerificationPluginClient extends PluginClient { + public internalEvents: EventManager + + constructor() { + super() + this.internalEvents = new EventManager() + createClient(this) + this.onload() + } + + onActivation(): void { + this.internalEvents.emit('verification_activated') + } +} diff --git a/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts b/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts new file mode 100644 index 0000000000..480fc5bd9b --- /dev/null +++ b/apps/contract-verification/src/app/Verifiers/AbstractVerifier.ts @@ -0,0 +1,16 @@ +import { CompilerAbstract } from '@remix-project/remix-solidity' +import type { LookupResponse, SubmittedContract, VerificationResponse } from '../types' + +// Optional function definitions +export interface AbstractVerifier { + verifyProxy(submittedContract: SubmittedContract): Promise + checkVerificationStatus?(receiptId: string): Promise + checkProxyVerificationStatus?(receiptId: string): Promise +} + +export abstract class AbstractVerifier { + constructor(public apiUrl: string, public explorerUrl: string) {} + + abstract verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract): Promise + abstract lookup(contractAddress: string, chainId: string): Promise +} diff --git a/apps/contract-verification/src/app/Verifiers/BlockscoutVerifier.ts b/apps/contract-verification/src/app/Verifiers/BlockscoutVerifier.ts new file mode 100644 index 0000000000..fd7b95563f --- /dev/null +++ b/apps/contract-verification/src/app/Verifiers/BlockscoutVerifier.ts @@ -0,0 +1,50 @@ +import { SourceFile } from '../types' +import { EtherscanVerifier } from './EtherscanVerifier' + +// Etherscan and Blockscout return different objects from the getsourcecode method +interface BlockscoutSource { + AdditionalSources: Array<{ SourceCode: string; Filename: string }> + ConstructorArguments: string + OptimizationRuns: number + IsProxy: string + SourceCode: string + ABI: string + ContractName: string + CompilerVersion: string + OptimizationUsed: string + Runs: string + EVMVersion: string + FileName: string + Address: string +} + +export class BlockscoutVerifier extends EtherscanVerifier { + LOOKUP_STORE_DIR = 'blockscout-verified' + + constructor(apiUrl: string) { + // apiUrl and explorerUrl are the same for Blockscout + super(apiUrl, apiUrl, undefined) + } + + getContractCodeUrl(address: string): string { + const url = new URL(this.explorerUrl + `/address/${address}`) + url.searchParams.append('tab', 'contract') + return url.href + } + + processReceivedFiles(source: unknown, contractAddress: string, chainId: string): { sourceFiles: SourceFile[]; targetFilePath?: string } { + const blockscoutSource = source as BlockscoutSource + + const result: SourceFile[] = [] + const filePrefix = `/${this.LOOKUP_STORE_DIR}/${chainId}/${contractAddress}` + + const targetFilePath = `${filePrefix}/${blockscoutSource.FileName}` + result.push({ content: blockscoutSource.SourceCode, path: targetFilePath }) + + for (const additional of blockscoutSource.AdditionalSources ?? []) { + result.push({ content: additional.SourceCode, path: `${filePrefix}/${additional.Filename}` }) + } + + return { sourceFiles: result, targetFilePath } + } +} diff --git a/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts new file mode 100644 index 0000000000..8d5a6198a2 --- /dev/null +++ b/apps/contract-verification/src/app/Verifiers/EtherscanVerifier.ts @@ -0,0 +1,289 @@ +import { CompilerAbstract } from '@remix-project/remix-solidity' +import { AbstractVerifier } from './AbstractVerifier' +import type { LookupResponse, SourceFile, SubmittedContract, VerificationResponse, VerificationStatus } from '../types' + +interface EtherscanRpcResponse { + status: '0' | '1' + message: string + result: string +} + +interface EtherscanCheckStatusResponse { + status: '0' | '1' + message: string + result: 'Pending in queue' | 'Pass - Verified' | 'Fail - Unable to verify' | 'Already Verified' | 'Unknown UID' +} + +interface EtherscanSource { + SourceCode: string + ABI: string + ContractName: string + CompilerVersion: string + OptimizationUsed: string + Runs: string + ConstructorArguments: string + EVMVersion: string + Library: string + LicenseType: string + Proxy: string + Implementation: string + SwarmSource: string +} + +interface EtherscanGetSourceCodeResponse { + status: '0' | '1' + message: string + result: EtherscanSource[] +} + +export class EtherscanVerifier extends AbstractVerifier { + LOOKUP_STORE_DIR = 'etherscan-verified' + + constructor(apiUrl: string, explorerUrl: string, protected apiKey?: string) { + super(apiUrl, explorerUrl) + } + + async verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract): Promise { + // TODO: Handle version Vyper contracts. This relies on Solidity metadata. + const metadata = JSON.parse(compilerAbstract.data.contracts[submittedContract.filePath][submittedContract.contractName].metadata) + const formData = new FormData() + formData.append('chainId', submittedContract.chainId) + formData.append('codeformat', 'solidity-standard-json-input') + formData.append('sourceCode', compilerAbstract.input.toString()) + formData.append('contractaddress', submittedContract.address) + formData.append('contractname', submittedContract.filePath + ':' + submittedContract.contractName) + formData.append('compilerversion', `v${metadata.compiler.version}`) + formData.append('constructorArguements', submittedContract.abiEncodedConstructorArgs?.replace('0x', '') ?? '') + + const url = new URL(this.apiUrl + '/api') + url.searchParams.append('module', 'contract') + url.searchParams.append('action', 'verifysourcecode') + if (this.apiKey) { + url.searchParams.append('apikey', this.apiKey) + } + + const response = await fetch(url.href, { + method: 'POST', + body: formData, + }) + + if (!response.ok) { + const responseText = await response.text() + console.error('Error on Etherscan API verification at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + responseText) + throw new Error(responseText) + } + + const verificationResponse: EtherscanRpcResponse = await response.json() + + if (verificationResponse.result.includes('already verified')) { + return { status: 'already verified', receiptId: null, lookupUrl: this.getContractCodeUrl(submittedContract.address) } + } + + if (verificationResponse.status !== '1' || verificationResponse.message !== 'OK') { + console.error('Error on Etherscan API verification at ' + this.apiUrl + '\nStatus: ' + verificationResponse.status + '\nMessage: ' + verificationResponse.message + '\nResult: ' + verificationResponse.result) + throw new Error(verificationResponse.result) + } + + const lookupUrl = this.getContractCodeUrl(submittedContract.address) + return { status: 'pending', receiptId: verificationResponse.result, lookupUrl } + } + + async verifyProxy(submittedContract: SubmittedContract): Promise { + if (!submittedContract.proxyAddress) { + throw new Error('SubmittedContract does not have a proxyAddress') + } + + const formData = new FormData() + formData.append('address', submittedContract.proxyAddress) + formData.append('expectedimplementation', submittedContract.address) + + const url = new URL(this.apiUrl + '/api') + url.searchParams.append('module', 'contract') + url.searchParams.append('action', 'verifyproxycontract') + if (this.apiKey) { + url.searchParams.append('apikey', this.apiKey) + } + + const response = await fetch(url.href, { + method: 'POST', + body: formData, + }) + + if (!response.ok) { + const responseText = await response.text() + console.error('Error on Etherscan API proxy verification at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + responseText) + throw new Error(responseText) + } + + const verificationResponse: EtherscanRpcResponse = await response.json() + + if (verificationResponse.status !== '1' || verificationResponse.message !== 'OK') { + console.error('Error on Etherscan API proxy verification at ' + this.apiUrl + '\nStatus: ' + verificationResponse.status + '\nMessage: ' + verificationResponse.message + '\nResult: ' + verificationResponse.result) + throw new Error(verificationResponse.result) + } + + return { status: 'pending', receiptId: verificationResponse.result } + } + + async checkVerificationStatus(receiptId: string): Promise { + const url = new URL(this.apiUrl + '/api') + url.searchParams.append('module', 'contract') + url.searchParams.append('action', 'checkverifystatus') + url.searchParams.append('guid', receiptId) + if (this.apiKey) { + url.searchParams.append('apikey', this.apiKey) + } + + const response = await fetch(url.href, { method: 'GET' }) + + if (!response.ok) { + const responseText = await response.text() + console.error('Error on Etherscan API check verification status at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + responseText) + throw new Error(responseText) + } + + const checkStatusResponse: EtherscanCheckStatusResponse = await response.json() + + if (checkStatusResponse.result.startsWith('Fail - Unable to verify')) { + return { status: 'failed', receiptId, message: checkStatusResponse.result } + } + if (checkStatusResponse.result === 'Pending in queue') { + return { status: 'pending', receiptId } + } + if (checkStatusResponse.result === 'Pass - Verified') { + return { status: 'verified', receiptId } + } + if (checkStatusResponse.result === 'Already Verified') { + return { status: 'already verified', receiptId } + } + if (checkStatusResponse.result === 'Unknown UID') { + console.error('Error on Etherscan API check verification status at ' + this.apiUrl + '\nStatus: ' + checkStatusResponse.status + '\nMessage: ' + checkStatusResponse.message + '\nResult: ' + checkStatusResponse.result) + return { status: 'failed', receiptId, message: checkStatusResponse.result } + } + + if (checkStatusResponse.status !== '1' || !checkStatusResponse.message.startsWith('OK')) { + console.error('Error on Etherscan API check verification status at ' + this.apiUrl + '\nStatus: ' + checkStatusResponse.status + '\nMessage: ' + checkStatusResponse.message + '\nResult: ' + checkStatusResponse.result) + throw new Error(checkStatusResponse.result) + } + + return { status: 'unknown', receiptId } + } + + async checkProxyVerificationStatus(receiptId: string): Promise { + const url = new URL(this.apiUrl + '/api') + url.searchParams.append('module', 'contract') + url.searchParams.append('action', 'checkproxyverification') + url.searchParams.append('guid', receiptId) + if (this.apiKey) { + url.searchParams.append('apikey', this.apiKey) + } + + const response = await fetch(url.href, { method: 'GET' }) + + if (!response.ok) { + const responseText = await response.text() + console.error('Error on Etherscan API check verification status at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + responseText) + throw new Error(responseText) + } + + const checkStatusResponse: EtherscanRpcResponse = await response.json() + + if (checkStatusResponse.result === 'A corresponding implementation contract was unfortunately not detected for the proxy address.' || checkStatusResponse.result === 'The provided expected results are different than the retrieved implementation address!' || checkStatusResponse.result === 'This contract does not look like it contains any delegatecall opcode sequence.') { + return { status: 'failed', receiptId, message: checkStatusResponse.result } + } + if (checkStatusResponse.result === 'Verification in progress') { + return { status: 'pending', receiptId } + } + if (checkStatusResponse.result.startsWith("The proxy's") && checkStatusResponse.result.endsWith('and is successfully updated.')) { + return { status: 'verified', receiptId } + } + if (checkStatusResponse.result === 'Unknown UID') { + console.error('Error on Etherscan API check proxy verification status at ' + this.apiUrl + '\nStatus: ' + checkStatusResponse.status + '\nMessage: ' + checkStatusResponse.message + '\nResult: ' + checkStatusResponse.result) + return { status: 'failed', receiptId, message: checkStatusResponse.result } + } + + if (checkStatusResponse.status !== '1' || !checkStatusResponse.message.startsWith('OK')) { + console.error('Error on Etherscan API check proxy verification status at ' + this.apiUrl + '\nStatus: ' + checkStatusResponse.status + '\nMessage: ' + checkStatusResponse.message + '\nResult: ' + checkStatusResponse.result) + throw new Error(checkStatusResponse.result) + } + + return { status: 'unknown', receiptId } + } + + async lookup(contractAddress: string, chainId: string): Promise { + const url = new URL(this.apiUrl + '/api') + url.searchParams.append('module', 'contract') + url.searchParams.append('action', 'getsourcecode') + url.searchParams.append('address', contractAddress) + if (this.apiKey) { + url.searchParams.append('apikey', this.apiKey) + } + + const response = await fetch(url.href, { method: 'GET' }) + + if (!response.ok) { + const responseText = await response.text() + console.error('Error on Etherscan API lookup at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + responseText) + throw new Error(responseText) + } + + const lookupResponse: EtherscanGetSourceCodeResponse = await response.json() + + if (lookupResponse.status !== '1' || !lookupResponse.message.startsWith('OK')) { + const errorResponse = lookupResponse as unknown as EtherscanRpcResponse + console.error('Error on Etherscan API lookup at ' + this.apiUrl + '\nStatus: ' + errorResponse.status + '\nMessage: ' + errorResponse.message + '\nResult: ' + errorResponse.result) + throw new Error(errorResponse.result) + } + + if (lookupResponse.result[0].ABI === 'Contract source code not verified' || !lookupResponse.result[0].SourceCode) { + return { status: 'not verified' } + } + + const lookupUrl = this.getContractCodeUrl(contractAddress) + const { sourceFiles, targetFilePath } = this.processReceivedFiles(lookupResponse.result[0], contractAddress, chainId) + + return { status: 'verified', lookupUrl, sourceFiles, targetFilePath } + } + + getContractCodeUrl(address: string): string { + const url = new URL(this.explorerUrl + `/address/${address}#code`) + return url.href + } + + processReceivedFiles(source: EtherscanSource, contractAddress: string, chainId: string): { sourceFiles: SourceFile[]; targetFilePath?: string } { + const filePrefix = `/${this.LOOKUP_STORE_DIR}/${chainId}/${contractAddress}` + + // Covers the cases: + // SourceFile: {[FileName]: [content]} + // SourceFile: {{sources: {[FileName]: [content]}}} + let parsedFiles: any + try { + parsedFiles = JSON.parse(source.SourceCode) + } catch (e) { + try { + // Etherscan wraps the Object in one additional bracket + parsedFiles = JSON.parse(source.SourceCode.substring(1, source.SourceCode.length - 1)).sources + } catch (e) {} + } + + if (parsedFiles) { + const result: SourceFile[] = [] + let targetFilePath = '' + for (const [fileName, fileObj] of Object.entries(parsedFiles)) { + const path = `${filePrefix}/${fileName}` + + result.push({ path, content: fileObj.content }) + + if (path.endsWith(`/${source.ContractName}.sol`)) { + targetFilePath = path + } + } + return { sourceFiles: result, targetFilePath } + } + + // Parsing to JSON failed, SourceCode is the code itself + const targetFilePath = `${filePrefix}/${source.ContractName}.sol` + const sourceFiles: SourceFile[] = [{ content: source.SourceCode, path: targetFilePath }] + return { sourceFiles, targetFilePath } + } +} diff --git a/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts b/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts new file mode 100644 index 0000000000..ab5235e2aa --- /dev/null +++ b/apps/contract-verification/src/app/Verifiers/SourcifyVerifier.ts @@ -0,0 +1,169 @@ +import { CompilerAbstract, SourcesCode } from '@remix-project/remix-solidity' +import { AbstractVerifier } from './AbstractVerifier' +import type { LookupResponse, SourceFile, SubmittedContract, VerificationResponse, VerificationStatus } from '../types' +import { ethers } from 'ethers' + +interface SourcifyVerificationRequest { + address: string + chain: string + files: Record + creatorTxHash?: string + chosenContract?: string +} + +type SourcifyVerificationStatus = 'perfect' | 'full' | 'partial' | null + +interface SourcifyVerificationResponse { + result: [ + { + address: string + chainId: string + status: SourcifyVerificationStatus + libraryMap: { + [key: string]: string + } + message?: string + } + ] +} + +interface SourcifyErrorResponse { + error: string +} + +interface SourcifyFile { + name: string + path: string + content: string +} + +interface SourcifyLookupResponse { + status: Exclude + files: SourcifyFile[] +} + +export class SourcifyVerifier extends AbstractVerifier { + LOOKUP_STORE_DIR = 'sourcify-verified' + + async verify(submittedContract: SubmittedContract, compilerAbstract: CompilerAbstract): Promise { + const metadataStr = compilerAbstract.data.contracts[submittedContract.filePath][submittedContract.contractName].metadata + const sources = compilerAbstract.source.sources + + // from { "filename.sol": {content: "contract MyContract { ... }"} } + // to { "filename.sol": "contract MyContract { ... }" } + const formattedSources = Object.entries(sources).reduce((acc, [fileName, { content }]) => { + acc[fileName] = content + return acc + }, {}) + const body: SourcifyVerificationRequest = { + chain: submittedContract.chainId, + address: submittedContract.address, + files: { + 'metadata.json': metadataStr, + ...formattedSources, + }, + } + + const response = await fetch(new URL(this.apiUrl + '/verify').href, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }) + + if (!response.ok) { + const errorResponse: SourcifyErrorResponse = await response.json() + console.error('Error on Sourcify verification at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + JSON.stringify(errorResponse)) + throw new Error(errorResponse.error) + } + + const verificationResponse: SourcifyVerificationResponse = await response.json() + + if (verificationResponse.result[0].status === null) { + console.error('Error on Sourcify verification at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + verificationResponse.result[0].message) + throw new Error(verificationResponse.result[0].message) + } + + // Map to a user-facing status message + let status: VerificationStatus = 'unknown' + let lookupUrl: string | undefined = undefined + if (verificationResponse.result[0].status === 'perfect' || verificationResponse.result[0].status === 'full') { + status = 'fully verified' + lookupUrl = this.getContractCodeUrl(submittedContract.address, submittedContract.chainId, true) + } else if (verificationResponse.result[0].status === 'partial') { + status = 'partially verified' + lookupUrl = this.getContractCodeUrl(submittedContract.address, submittedContract.chainId, false) + } + + return { status, receiptId: null, lookupUrl } + } + + async lookup(contractAddress: string, chainId: string): Promise { + const url = new URL(this.apiUrl + `/files/any/${chainId}/${contractAddress}`) + + const response = await fetch(url.href, { method: 'GET' }) + + if (!response.ok) { + const errorResponse: SourcifyErrorResponse = await response.json() + + if (errorResponse.error === 'Files have not been found!') { + return { status: 'not verified' } + } + + console.error('Error on Sourcify lookup at ' + this.apiUrl + '\nStatus: ' + response.status + '\nResponse: ' + JSON.stringify(errorResponse)) + throw new Error(errorResponse.error) + } + + const lookupResponse: SourcifyLookupResponse = await response.json() + + let status: VerificationStatus = 'unknown' + let lookupUrl: string | undefined = undefined + if (lookupResponse.status === 'perfect' || lookupResponse.status === 'full') { + status = 'fully verified' + lookupUrl = this.getContractCodeUrl(contractAddress, chainId, true) + } else if (lookupResponse.status === 'partial') { + status = 'partially verified' + lookupUrl = this.getContractCodeUrl(contractAddress, chainId, false) + } + + const { sourceFiles, targetFilePath } = this.processReceivedFiles(lookupResponse.files, contractAddress, chainId) + + return { status, lookupUrl, sourceFiles, targetFilePath } + } + + getContractCodeUrl(address: string, chainId: string, fullMatch: boolean): string { + const url = new URL(this.explorerUrl + `/contracts/${fullMatch ? 'full_match' : 'partial_match'}/${chainId}/${address}/`) + return url.href + } + + processReceivedFiles(files: SourcifyFile[], contractAddress: string, chainId: string): { sourceFiles: SourceFile[]; targetFilePath?: string } { + const result: SourceFile[] = [] + let targetFilePath: string + const filePrefix = `/${this.LOOKUP_STORE_DIR}/${chainId}/${contractAddress}` + + for (const file of files) { + let filePath: string + for (const a of [contractAddress, ethers.utils.getAddress(contractAddress)]) { + const matching = file.path.match(`/${a}/(.*)$`) + if (matching) { + filePath = matching[1] + break + } + } + + if (filePath) { + result.push({ path: `${filePrefix}/${filePath}`, content: file.content }) + } + + if (file.name === 'metadata.json') { + const metadata = JSON.parse(file.content) + const compilationTarget = metadata.settings.compilationTarget + const contractPath = Object.keys(compilationTarget)[0] + targetFilePath = `${filePrefix}/sources/${contractPath}` + } + } + + return { sourceFiles: result, targetFilePath } + } +} diff --git a/apps/contract-verification/src/app/Verifiers/index.ts b/apps/contract-verification/src/app/Verifiers/index.ts new file mode 100644 index 0000000000..23de8cd89d --- /dev/null +++ b/apps/contract-verification/src/app/Verifiers/index.ts @@ -0,0 +1,30 @@ +import type { VerifierIdentifier, VerifierSettings } from '../types' +import { AbstractVerifier } from './AbstractVerifier' +import { BlockscoutVerifier } from './BlockscoutVerifier' +import { EtherscanVerifier } from './EtherscanVerifier' +import { SourcifyVerifier } from './SourcifyVerifier' + +export { AbstractVerifier } from './AbstractVerifier' +export { BlockscoutVerifier } from './BlockscoutVerifier' +export { SourcifyVerifier } from './SourcifyVerifier' +export { EtherscanVerifier } from './EtherscanVerifier' + +export function getVerifier(identifier: VerifierIdentifier, settings: VerifierSettings): AbstractVerifier { + switch (identifier) { + case 'Sourcify': + if (!settings?.explorerUrl) { + throw new Error('The Sourcify verifier requires an explorer URL.') + } + return new SourcifyVerifier(settings.apiUrl, settings.explorerUrl) + case 'Etherscan': + if (!settings?.explorerUrl) { + throw new Error('The Etherscan verifier requires an explorer URL.') + } + if (!settings?.apiKey) { + throw new Error('The Etherscan verifier requires an API key.') + } + return new EtherscanVerifier(settings.apiUrl, settings.explorerUrl, settings.apiKey) + case 'Blockscout': + return new BlockscoutVerifier(settings.apiUrl) + } +} diff --git a/apps/contract-verification/src/app/VerifyFormContext.tsx b/apps/contract-verification/src/app/VerifyFormContext.tsx new file mode 100644 index 0000000000..14d146b0c1 --- /dev/null +++ b/apps/contract-verification/src/app/VerifyFormContext.tsx @@ -0,0 +1,46 @@ +import React from 'react' +import type { Chain } from './types' +import { ContractDropdownSelection } from './components/ContractDropdown' + +// Define the type for the context +type VerifyFormContextType = { + selectedChain: Chain | undefined + setSelectedChain: React.Dispatch> + contractAddress: string + setContractAddress: React.Dispatch> + contractAddressError: string + setContractAddressError: React.Dispatch> + selectedContract: ContractDropdownSelection | undefined + setSelectedContract: React.Dispatch> + proxyAddress: string + setProxyAddress: React.Dispatch> + proxyAddressError: string + setProxyAddressError: React.Dispatch> + abiEncodedConstructorArgs: string + setAbiEncodedConstructorArgs: React.Dispatch> + abiEncodingError: string + setAbiEncodingError: React.Dispatch> +} + +// Provide a default value with the appropriate types +const defaultContextValue: VerifyFormContextType = { + selectedChain: undefined, + setSelectedChain: (selectedChain: Chain) => {}, + contractAddress: '', + setContractAddress: (contractAddress: string) => {}, + contractAddressError: '', + setContractAddressError: (contractAddressError: string) => {}, + selectedContract: undefined, + setSelectedContract: (selectedContract: ContractDropdownSelection) => {}, + proxyAddress: '', + setProxyAddress: (proxyAddress: string) => {}, + proxyAddressError: '', + setProxyAddressError: (contractAddressError: string) => {}, + abiEncodedConstructorArgs: '', + setAbiEncodedConstructorArgs: (contractAddproxyAddressress: string) => {}, + abiEncodingError: '', + setAbiEncodingError: (contractAddressError: string) => {}, +} + +// Create the context with the type +export const VerifyFormContext = React.createContext(defaultContextValue) diff --git a/apps/contract-verification/src/app/app.tsx b/apps/contract-verification/src/app/app.tsx new file mode 100644 index 0000000000..0ea4332543 --- /dev/null +++ b/apps/contract-verification/src/app/app.tsx @@ -0,0 +1,154 @@ +import { useState, useEffect, useRef } from 'react' + +import { ContractVerificationPluginClient } from './ContractVerificationPluginClient' + +import { AppContext } from './AppContext' +import { VerifyFormContext } from './VerifyFormContext' +import DisplayRoutes from './routes' +import type { ContractVerificationSettings, ThemeType, Chain, SubmittedContracts, VerificationReceipt, VerificationResponse } from './types' +import { mergeChainSettingsWithDefaults } from './utils' + +import './App.css' +import { CompilerAbstract } from '@remix-project/remix-solidity' +import { useLocalStorage } from './hooks/useLocalStorage' +import { getVerifier } from './Verifiers' +import { ContractDropdownSelection } from './components/ContractDropdown' + +const plugin = new ContractVerificationPluginClient() + +const App = () => { + const [themeType, setThemeType] = useState('dark') + const [settings, setSettings] = useLocalStorage('contract-verification:settings', { chains: {} }) + const [submittedContracts, setSubmittedContracts] = useLocalStorage('contract-verification:submitted-contracts', {}) + const [chains, setChains] = useState([]) // State to hold the chains data + const [compilationOutput, setCompilationOutput] = useState<{ [key: string]: CompilerAbstract } | undefined>() + + // Form values: + const [selectedChain, setSelectedChain] = useState() + const [contractAddress, setContractAddress] = useState('') + const [contractAddressError, setContractAddressError] = useState('') + const [selectedContract, setSelectedContract] = useState() + const [proxyAddress, setProxyAddress] = useState('') + const [proxyAddressError, setProxyAddressError] = useState('') + const [abiEncodedConstructorArgs, setAbiEncodedConstructorArgs] = useState('') + const [abiEncodingError, setAbiEncodingError] = useState('') + + const timer = useRef(null) + + useEffect(() => { + plugin.internalEvents.on('verification_activated', () => { + // Fetch compiler artefacts initially + plugin.call('compilerArtefacts' as any, 'getAllCompilerAbstracts').then((obj: any) => { + setCompilationOutput(obj) + }) + + // Subscribe to compilations + plugin.on('compilerArtefacts' as any, 'compilationSaved', (compilerAbstracts: { [key: string]: CompilerAbstract }) => { + setCompilationOutput((prev) => ({ ...(prev || {}), ...compilerAbstracts })) + }) + + // Fetch chains.json and update state + fetch('https://chainid.network/chains.json') + .then((response) => response.json()) + .then((data) => setChains(data)) + .catch((error) => console.error('Failed to fetch chains.json:', error)) + }) + + // Clean up on unmount + return () => { + plugin.off('compilerArtefacts' as any, 'compilationSaved') + } + }, []) + + // Poll status of pending receipts frequently + useEffect(() => { + const getPendingReceipts = (submissions: SubmittedContracts) => { + const pendingReceipts: VerificationReceipt[] = [] + // Check statuses of receipts + for (const submission of Object.values(submissions)) { + for (const receipt of submission.receipts) { + if (receipt.status === 'pending') { + pendingReceipts.push(receipt) + } + } + for (const proxyReceipt of submission.proxyReceipts ?? []) { + if (proxyReceipt.status === 'pending') { + pendingReceipts.push(proxyReceipt) + } + } + } + return pendingReceipts + } + + let pendingReceipts = getPendingReceipts(submittedContracts) + + if (pendingReceipts.length > 0) { + if (timer.current) { + clearInterval(timer.current) + timer.current = null + } + + const pollStatus = async () => { + const changedSubmittedContracts = { ...submittedContracts } + + for (const receipt of pendingReceipts) { + await new Promise((resolve) => setTimeout(resolve, 500)) // avoid api rate limit exceed. + + const { verifierInfo, receiptId } = receipt + if (receiptId) { + const contract = changedSubmittedContracts[receipt.contractId] + const chainSettings = mergeChainSettingsWithDefaults(contract.chainId, settings) + const verifierSettings = chainSettings.verifiers[verifierInfo.name] + + // In case the user overwrites the API later, prefer the one stored in localStorage + const verifier = getVerifier(verifierInfo.name, { ...verifierSettings, apiUrl: verifierInfo.apiUrl }) + if (!verifier.checkVerificationStatus) { + continue + } + + try { + let response: VerificationResponse + if (receipt.isProxyReceipt) { + response = await verifier.checkProxyVerificationStatus(receiptId) + } else { + response = await verifier.checkVerificationStatus(receiptId) + } + const { status, message, lookupUrl } = response + receipt.status = status + receipt.message = message + if (lookupUrl) { + receipt.lookupUrl = lookupUrl + } + } catch (e) { + receipt.failedChecks++ + // Only retry 5 times + if (receipt.failedChecks >= 5) { + receipt.status = 'failed' + receipt.message = e.message + } + } + } + } + + pendingReceipts = getPendingReceipts(changedSubmittedContracts) + if (timer.current && pendingReceipts.length === 0) { + clearInterval(timer.current) + timer.current = null + } + setSubmittedContracts((prev) => Object.assign({}, prev, changedSubmittedContracts)) + } + + timer.current = setInterval(pollStatus, 1000) + } + }, [submittedContracts]) + + return ( + + + + + + ) +} + +export default App diff --git a/apps/contract-verification/src/app/components/AccordionReceipt.tsx b/apps/contract-verification/src/app/components/AccordionReceipt.tsx new file mode 100644 index 0000000000..33ee96a7ce --- /dev/null +++ b/apps/contract-verification/src/app/components/AccordionReceipt.tsx @@ -0,0 +1,105 @@ +import React, { useMemo } from 'react' +import { SubmittedContract, VerificationReceipt } from '../types' +import { shortenAddress, CustomTooltip } from '@remix-ui/helper' +import { AppContext } from '../AppContext' +import { CopyToClipboard } from '@remix-ui/clipboard' + +interface AccordionReceiptProps { + contract: SubmittedContract + index: number +} + +export const AccordionReceipt: React.FC = ({ contract, index }) => { + const { chains } = React.useContext(AppContext) + + const [expanded, setExpanded] = React.useState(false) + + const chain = useMemo(() => { + return chains.find((c) => c.chainId === parseInt(contract.chainId)) + }, [contract, chains]) + const chainName = chain?.name ?? 'Unknown Chain' + + const hasProxy = contract.proxyAddress && contract.proxyReceipts + + const toggleAccordion = () => { + setExpanded(!expanded) + } + + return ( +
+
+ + +
+ + + {contract.contractName} at {shortenAddress(contract.address)} {contract.proxyAddress ? 'with proxy' : ''} + + +
+ + +
+ +
+
+ Chain: + {chainName} ({contract.chainId}) +
+
+ File: + {contract.filePath} +
+
+ Submitted at: + {new Date(contract.date).toLocaleString()} +
+ +
+ Verified at: + +
+ + {hasProxy && ( + <> +
+ Proxy Address: + + {shortenAddress(contract.proxyAddress)} + + +
+
+ Proxy verified at: + +
+ + )} +
+
+ ) +} + +const ReceiptsBody = ({ receipts }: { receipts: VerificationReceipt[] }) => { + return ( +
    + {receipts.map((receipt) => ( +
  • + + {receipt.verifierInfo.name} + + + + {['verified', 'partially verified', 'already verified'].includes(receipt.status) ? : receipt.status === 'fully verified' ? : receipt.status === 'failed' ? : ['pending', 'awaiting implementation verification'].includes(receipt.status) ? : } + + + {!!receipt.lookupUrl && } +
  • + ))} +
+ ) +} diff --git a/apps/contract-verification/src/app/components/ConfigInput.tsx b/apps/contract-verification/src/app/components/ConfigInput.tsx new file mode 100644 index 0000000000..0737840115 --- /dev/null +++ b/apps/contract-verification/src/app/components/ConfigInput.tsx @@ -0,0 +1,69 @@ +import React, { useEffect, useState } from 'react' +import { CustomTooltip } from '@remix-ui/helper' + +interface ConfigInputProps { + label: string + id: string + secret: boolean + initialValue: string + saveResult: (result: string) => void +} + +// Chooses one contract from the compilation output. +export const ConfigInput: React.FC = ({ label, id, secret, initialValue, saveResult }) => { + const [value, setValue] = useState(initialValue) + const [enabled, setEnabled] = useState(false) + + // Reset state when initialValue changes + useEffect(() => { + setValue(initialValue) + setEnabled(false) + }, [initialValue]) + + const handleChange = () => { + setEnabled(true) + } + + const handleSave = () => { + setEnabled(false) + saveResult(value) + } + + const handleCancel = () => { + setEnabled(false) + setValue(initialValue) + } + + return ( +
+ +
+ setValue(e.target.value)} + disabled={!enabled} + /> + + { enabled ? ( + <> + + + + ) : ( + + + + )} +
+
+ ) +} diff --git a/apps/contract-verification/src/app/components/ConstructorArguments.tsx b/apps/contract-verification/src/app/components/ConstructorArguments.tsx new file mode 100644 index 0000000000..e2a4239eaa --- /dev/null +++ b/apps/contract-verification/src/app/components/ConstructorArguments.tsx @@ -0,0 +1,135 @@ +import { useContext, useEffect, useRef, useState } from 'react' +import { ethers } from 'ethers' + +import { AppContext } from '../AppContext' +import { ContractDropdownSelection } from './ContractDropdown' + +interface ConstructorArgumentsProps { + abiEncodedConstructorArgs: string + setAbiEncodedConstructorArgs: React.Dispatch> + abiEncodingError: string + setAbiEncodingError: React.Dispatch> + selectedContract: ContractDropdownSelection +} + +export const ConstructorArguments: React.FC = ({ abiEncodedConstructorArgs, setAbiEncodedConstructorArgs, abiEncodingError, setAbiEncodingError, selectedContract }) => { + const { compilationOutput } = useContext(AppContext) + const [toggleRawInput, setToggleRawInput] = useState(false) + + const { triggerFilePath, filePath, contractName } = selectedContract + const selectedCompilerAbstract = triggerFilePath && compilationOutput[triggerFilePath] + const compiledContract = selectedCompilerAbstract?.data?.contracts?.[filePath]?.[contractName] + const abi = compiledContract?.abi + + const constructorArgs = abi && abi.find((a) => a.type === 'constructor')?.inputs + + const decodeConstructorArgs = (value: string) => { + try { + const decodedObj = ethers.utils.defaultAbiCoder.decode( + constructorArgs.map((inp) => inp.type), + value + ) + const decoded = decodedObj.map((val) => JSON.stringify(val)) + return { decoded, errorMessage: '' } + } catch (e) { + console.error(e) + const errorMessage = 'Decoding error: ' + e.message + const decoded = Array(constructorArgs?.length ?? 0).fill('') + return { decoded, errorMessage } + } + } + + const [constructorArgsValues, setConstructorArgsValues] = useState(abiEncodedConstructorArgs ? decodeConstructorArgs(abiEncodedConstructorArgs).decoded : Array(constructorArgs?.length ?? 0).fill('')) + + const constructorArgsInInitialState = useRef(true) + useEffect(() => { + if (constructorArgsInInitialState.current) { + constructorArgsInInitialState.current = false + return + } + setAbiEncodedConstructorArgs('') + setAbiEncodingError('') + setConstructorArgsValues(Array(constructorArgs?.length ?? 0).fill('')) + }, [constructorArgs]) + + const handleConstructorArgs = (value: string, index: number) => { + const changedConstructorArgsValues = [...constructorArgsValues.slice(0, index), value, ...constructorArgsValues.slice(index + 1)] + setConstructorArgsValues(changedConstructorArgsValues) + + // if any constructorArgsValue is falsey (empty etc.), don't encode yet + if (changedConstructorArgsValues.some((value) => !value)) { + setAbiEncodedConstructorArgs('') + setAbiEncodingError('') + return + } + + const types = constructorArgs.map((inp) => inp.type) + const parsedArgsValues = [] + for (const arg of changedConstructorArgsValues) { + try { + parsedArgsValues.push(JSON.parse(arg)) + } catch (e) { + parsedArgsValues.push(arg) + } + } + + try { + const newAbiEncoding = ethers.utils.defaultAbiCoder.encode(types, parsedArgsValues) + setAbiEncodedConstructorArgs(newAbiEncoding) + setAbiEncodingError('') + } catch (e) { + console.error(e) + setAbiEncodedConstructorArgs('') + setAbiEncodingError('Encoding error: ' + e.message) + } + } + + const handleRawConstructorArgs = (value: string) => { + setAbiEncodedConstructorArgs(value) + const { decoded, errorMessage } = decodeConstructorArgs(value) + setConstructorArgsValues(decoded) + setAbiEncodingError(errorMessage) + } + + if (!selectedContract) return null + if (!compilationOutput && Object.keys(compilationOutput).length === 0) return null + // No render if no constructor args + if (!constructorArgs || constructorArgs.length === 0) return null + + return ( +
+ +
+ setToggleRawInput(!toggleRawInput)} /> + +
+ {toggleRawInput ? ( +
+ {' '} + +
+ otherwise +
) } @@ -202,7 +223,7 @@ export function AccountUI(props: AccountProps) { - + }> @@ -234,3 +255,29 @@ export function AccountUI(props: AccountProps) {
) } + +const EIP712_Example = { + domain: { + chainId: 1, + name: "Example App", + verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + version: "1", + }, + message: { + prompt: "Welcome! In order to authenticate to this website, sign this request and your public address will be sent to the server in a verifiable way.", + createdAt: 1718570375196, + }, + primaryType: 'AuthRequest', + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], + AuthRequest: [ + { name: 'prompt', type: 'string' }, + { name: 'createdAt', type: 'uint256' }, + ], + }, +} \ No newline at end of file diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx index 44aad72011..7ccc8ce55b 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -6,7 +6,6 @@ import { ContractData, FuncABI, OverSizeLimit } from '@remix-project/core-plugin import * as ethJSUtil from '@ethereumjs/util' import { ContractGUI } from './contractGUI' import { CustomTooltip, deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper' -import { title } from 'process' const _paq = (window._paq = window._paq || []) export function ContractDropdownUI(props: ContractDropdownProps) { @@ -80,6 +79,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) { content: '', }) if (!currentContract) enableAtAddress(false) + if (currentContract && loadedAddress) enableAtAddress(true) } else { setAbiLabel({ display: 'none', diff --git a/libs/remix-ui/run-tab/src/lib/components/environment.tsx b/libs/remix-ui/run-tab/src/lib/components/environment.tsx index 4f6c2964ae..1cb2a91918 100644 --- a/libs/remix-ui/run-tab/src/lib/components/environment.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/environment.tsx @@ -35,7 +35,7 @@ export function EnvironmentUI(props: EnvironmentProps) { }> - + diff --git a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx index eee7ceb166..37253056f7 100644 --- a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx @@ -8,60 +8,24 @@ import { UniversalDappUI } from './universalDappUI' export function InstanceContainerUI(props: InstanceContainerProps) { const { instanceList } = props.instances - const clearInstance = () => { + const clearInstance = async() => { + const isPinnedAvailable = await props.plugin.call('fileManager', 'exists', `.deploys/pinned-contracts/${props.plugin.REACT_API.chainId}`) + if (isPinnedAvailable) await props.plugin.call('fileManager', 'remove', `.deploys/pinned-contracts/${props.plugin.REACT_API.chainId}`) props.clearInstances() } return ( -
-
- }> - - -
- - {props.pinnedInstances.instanceList.length > 0 ? ( -
- {' '} - {props.pinnedInstances.instanceList.map((instance, index) => { - return ( - - ) - })} -
- ) : ( - - - - )} - -
+
+
}> - + +
{instanceList.length}
+
+
{instanceList.length > 0 ? ( } > - + ) : null}
+ {instanceList.length > 0 ? (
{' '} @@ -81,8 +46,9 @@ export function InstanceContainerUI(props: InstanceContainerProps) { - ) : ( - - - - )} + ) : ''}
) } diff --git a/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx b/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx index ff64fb820f..f565278e37 100644 --- a/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx @@ -57,8 +57,7 @@ export function RecorderUI(props: RecorderProps) { tooltipText={} >