From 3c75ada75c8443997a0fc7e6dcbc1fdebc646b43 Mon Sep 17 00:00:00 2001 From: drafish Date: Thu, 25 Jul 2024 13:36:31 +0800 Subject: [PATCH 01/10] add remix-dapp --- apps/quick-dapp/src/actions/index.ts | 21 +- apps/remix-dapp/.eslintrc | 3 + 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 | 62 +++ 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 +++ .../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 | 111 ++++ .../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 | 321 +++++++++++ .../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/pages/Home/index.tsx | 124 +++++ apps/remix-dapp/src/reducers/state.ts | 50 ++ apps/remix-dapp/src/router.tsx | 11 + 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/remix-dapp/tsconfig.json | 16 + apps/remix-dapp/webpack.config.js | 108 ++++ apps/remix-dapp/yarn.lock | 34 ++ apps/remix-ide/project.json | 2 +- 69 files changed, 5870 insertions(+), 16 deletions(-) create mode 100644 apps/remix-dapp/.eslintrc 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/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/pages/Home/index.tsx create mode 100644 apps/remix-dapp/src/reducers/state.ts create mode 100644 apps/remix-dapp/src/router.tsx 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 create mode 100644 apps/remix-dapp/tsconfig.json create mode 100644 apps/remix-dapp/webpack.config.js create mode 100644 apps/remix-dapp/yarn.lock diff --git a/apps/quick-dapp/src/actions/index.ts b/apps/quick-dapp/src/actions/index.ts index fb42b459aa5..46c21505721 100644 --- a/apps/quick-dapp/src/actions/index.ts +++ b/apps/quick-dapp/src/actions/index.ts @@ -160,19 +160,10 @@ export const deploy = async (payload: any, callback: any) => { const { data } = await axios.get( // It's the json file contains all the static files paths of dapp-template. // It's generated through the build process automatically. - 'https://dev.remix-dapp.pages.dev/manifest.json' + `${window.origin}/plugins/remix-dapp/manifest.json` ); - let paths: any = []; - - Object.keys(data).forEach((key) => { - if (data[key].src === 'index.html') { - const { src, file, css, assets } = data[key]; - paths = paths.concat([src, file, ...css, ...assets]); - } else { - paths.push(data[key].file); - } - }); + const paths = Object.keys(data); const { logo, ...instance } = state.instance; @@ -183,7 +174,7 @@ export const deploy = async (payload: any, callback: any) => { }) const files: Record = { - 'dir/instance.json': instanceJson, + 'dir/assets/instance.json': instanceJson, }; // console.log( @@ -197,11 +188,11 @@ export const deploy = async (payload: any, callback: any) => { const path = paths[index]; // download all the static files from the dapp-template domain. // here is the codebase of dapp-template: https://github.com/drafish/remix-dapp - const resp = await axios.get(`https://dev.remix-dapp.pages.dev/${path}`); + const resp = await axios.get(`${window.origin}/plugins/remix-dapp/${path}`); files[`dir/${path}`] = resp.data; } - files['dir/logo.png'] = logo + files['dir/assets/logo.png'] = logo files['dir/CORS'] = '*' files['dir/index.html'] = files['dir/index.html'].replace( 'assets/css/themes/remix-dark_tvx1s2.css', @@ -236,7 +227,7 @@ export const deploy = async (payload: any, callback: any) => { try { // some times deployment might fail even if it says successfully, that's why we need to do the double check. - const instanceResp = await axios.get(`https://${payload.subdomain}.surge.sh/instance.json`); + const instanceResp = await axios.get(`https://${payload.subdomain}.surge.sh/assets/instance.json`); if (instanceResp.status === 200 && JSON.stringify(instanceResp.data) === instanceJson) { callback({ code: 'SUCCESS', error: '' }); return; diff --git a/apps/remix-dapp/.eslintrc b/apps/remix-dapp/.eslintrc new file mode 100644 index 00000000000..2d85f9fa667 --- /dev/null +++ b/apps/remix-dapp/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.json", +} \ No newline at end of file diff --git a/apps/remix-dapp/README.md b/apps/remix-dapp/README.md new file mode 100644 index 00000000000..a59cba8c984 --- /dev/null +++ b/apps/remix-dapp/README.md @@ -0,0 +1 @@ +# Remix Dapp diff --git a/apps/remix-dapp/package.json b/apps/remix-dapp/package.json new file mode 100644 index 00000000000..b8e4a04cb95 --- /dev/null +++ b/apps/remix-dapp/package.json @@ -0,0 +1,9 @@ +{ + "name": "remix-dapp", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "webpack-manifest-plugin": "^5.0.0" + } +} diff --git a/apps/remix-dapp/project.json b/apps/remix-dapp/project.json new file mode 100644 index 00000000000..106108513ec --- /dev/null +++ b/apps/remix-dapp/project.json @@ -0,0 +1,69 @@ +{ + "name": "remix-dapp", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/remix-dapp/src", + "projectType": "application", + "implicitDependencies": [], + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "development", + "dependsOn": ["install"], + "options": { + "compiler": "babel", + "outputPath": "dist/apps/remix-dapp", + "index": "apps/remix-dapp/src/index.html", + "baseHref": "./", + "main": "apps/remix-dapp/src/main.tsx", + "tsConfig": "apps/remix-dapp/tsconfig.app.json", + "assets": ["apps/remix-dapp/src/assets/instance.json", "apps/remix-dapp/src/assets/logo.png"], + "styles": ["apps/remix-dapp/src/App.css"], + "scripts": [], + "webpackConfig": "apps/remix-dapp/webpack.config.js" + }, + "configurations": { + "development": { + }, + "production": { + } + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/remix-dapp/**/*.ts"], + "eslintConfig": "apps/remix-dapp/.eslintrc" + } + }, + "install": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "cd apps/remix-dapp && yarn" + ], + "parallel": false + } + }, + "serve": { + "executor": "@nrwl/webpack:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "remix-dapp:build", + "hmr": true, + "baseHref": "/" + }, + "configurations": { + "development": { + "buildTarget": "remix-dapp:build:development", + "port": 2026 + }, + "production": { + "buildTarget": "remix-dapp:build:production" + } + } + } + }, + "tags": [] +} diff --git a/apps/remix-dapp/src/App.css b/apps/remix-dapp/src/App.css new file mode 100644 index 00000000000..ae748a814b4 --- /dev/null +++ b/apps/remix-dapp/src/App.css @@ -0,0 +1,25 @@ +.dragbar_terminal { + left: 0px; + top: 0px; + height: 0.3em; +} + +.dragbar_terminal:hover, +.dragbar_terminal.ondrag { + background-color: var(--secondary); + cursor: row-resize; +} + +.label_item { + word-break: break-all; + max-width: 90%; +} +.label_key { + max-width: 65%; + min-width: fit-content; +} +.label_value { + min-width: 10%; +} + + diff --git a/apps/remix-dapp/src/App.tsx b/apps/remix-dapp/src/App.tsx new file mode 100644 index 00000000000..ebe6722f391 --- /dev/null +++ b/apps/remix-dapp/src/App.tsx @@ -0,0 +1,62 @@ +import React, { useEffect, useReducer } from 'react'; +import { RouterProvider } from 'react-router-dom'; +import router from './router'; +import { IntlProvider } from 'react-intl'; +import { ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import { AppContext } from './contexts'; +import { appInitialState, appReducer } from './reducers/state'; +import { initDispatch, updateState } from './actions'; +import enJson from './locales/en'; +import zhJson from './locales/zh'; +import esJson from './locales/es'; +import frJson from './locales/fr'; +import itJson from './locales/it'; +import './App.css'; + +const localeMap: Record = { + zh: zhJson, + en: enJson, + fr: frJson, + it: itJson, + es: esJson, +}; + +function App(): JSX.Element { + const [appState, dispatch] = useReducer(appReducer, appInitialState); + const selectedLocaleCode = appState.settings.selectedLocaleCode; + useEffect(() => { + updateState(appState); + }, [appState]); + useEffect(() => { + initDispatch(dispatch); + updateState(appState); + }, []); + return ( + + + + + + + ); +} + +export default App; diff --git a/apps/remix-dapp/src/actions/index.ts b/apps/remix-dapp/src/actions/index.ts new file mode 100644 index 00000000000..3991b421d31 --- /dev/null +++ b/apps/remix-dapp/src/actions/index.ts @@ -0,0 +1,234 @@ +import axios from 'axios'; +import Web3 from 'web3'; +import { ethers } from 'ethers'; +import BN from 'bn.js'; +import { execution } from '@remix-project/remix-lib'; +import { toBytes, addHexPrefix } from '@ethereumjs/util'; +import { toast } from 'react-toastify'; +import txRunner from '../utils/txRunner'; +import metamask from '../utils/metamask'; +import walletConnect from '../utils/walletConnect'; +import buildData from '../utils/buildData'; + +const { txFormat, txHelper: { makeFullTypeDefinition } } = execution; + +const decodeInputParams = (data: any, abi: any) => { + data = toBytes(addHexPrefix(data)); + if (!data.length) data = new Uint8Array(32 * abi.inputs.length); + + const inputTypes = []; + for (let i = 0; i < abi.inputs.length; i++) { + const type = abi.inputs[i].type; + inputTypes.push( + type.indexOf('tuple') === 0 + ? makeFullTypeDefinition(abi.inputs[i]) + : type + ); + } + const abiCoder = new ethers.utils.AbiCoder(); + const decoded = abiCoder.decode(inputTypes, data); + const ret: any = {}; + for (const k in abi.inputs) { + ret[abi.inputs[k].type + ' ' + abi.inputs[k].name] = decoded[k]; + } + return ret; +} + +let dispatch: any, state: any; + +export const initDispatch = (_dispatch: any) => { + dispatch = _dispatch; +}; + +export const updateState = (_state: any) => { + state = _state; +}; + +export const setProvider = async (payload: any) => { + await dispatch({ + type: 'SET_SETTINGS', + payload: { loadedAccounts: {} }, + }); + const { provider, networkName } = payload; + const chainId = + '0x' + Number(networkName.match(/\(([^)]+)\)/)[1]).toString(16); + if (provider === 'metamask') { + const web3Provider: any = window.ethereum; + await metamask.addCustomNetwork(chainId); + await web3Provider.request({ method: 'eth_requestAccounts' }); + txRunner.setProvider(web3Provider); + txRunner.getAccounts(); + } + + if (provider === 'walletconnect') { + txRunner.setProvider(walletConnect as any); + walletConnect.subscribeToEvents(); + } +}; + +export const initInstance = async () => { + const resp = await axios.get('/assets/instance.json'); + await dispatch({ type: 'SET_INSTANCE', payload: resp.data }); + await dispatch({ + type: 'SET_SETTINGS', + payload: { networkName: resp.data.network }, + }); + await setProvider({ + networkName: resp.data.network, + provider: window.ethereum ? 'metamask' : 'walletconnect', + }); + updateInstanceBalance(resp.data.address); + setInterval(() => { + updateInstanceBalance(resp.data.address); + }, 30000); +}; + +export const updateInstanceBalance = async (address: string) => { + const balance = await txRunner.getBalanceInEther(address); + await dispatch({ type: 'SET_INSTANCE', payload: { balance } }); +}; + +export const saveSettings = async (payload: any) => { + await dispatch({ type: 'SET_SETTINGS', payload }); +}; + +export const log = async (payload: any) => { + const journalBlocks = state.terminal.journalBlocks; + const { message, style } = payload; + if (style === 'text-log') { + toast.info(message[0]); + } else if (style === 'text-danger') { + toast.error(message[0]); + } else { + toast.success('success'); + } + await dispatch({ + type: 'SET_TERMINAL', + payload: { + journalBlocks: [...journalBlocks, payload], + }, + }); +}; + +export const runTransactions = async (payload: any) => { + console.log(payload); + const { sendValue, sendUnit, gasLimit, selectedAccount } = state.settings; + const { address, decodedResponse, name } = state.instance; + const value = Web3.utils.toWei(sendValue, sendUnit); + + const tx = { + to: address, + data: '', + from: selectedAccount, + value, + }; + + const isFunction = payload.funcABI.type === 'function'; + + if (isFunction) { + const { dataHex, error } = buildData(payload.funcABI, payload.inputsValues); + + if (error) { + await log({ + message: [`${payload.logMsg} errored: ${error}`], + style: 'text-danger', + }); + return; + } + + tx.data = dataHex as string; + } else { + tx.data = payload.inputsValues; + } + + if (payload.lookupOnly) { + await log({ + message: [`${payload.logMsg}`], + style: 'text-log', + }); + } else { + await log({ + message: [`${payload.logMsg} pending ... `], + style: 'text-log', + }); + } + + const resp: any = await txRunner.runTx( + tx, + '0x' + new BN(gasLimit, 10).toString(16), + payload.lookupOnly + ); + + if (resp.error) { + await log({ + message: [`${payload.logMsg} errored: ${resp.error}`], + style: 'text-danger', + }); + return; + } + + if (payload.lookupOnly) { + await dispatch({ + type: 'SET_INSTANCE', + payload: { + decodedResponse: { + ...decodedResponse, + [payload.funcIndex]: txFormat.decodeResponse(resp, payload.funcABI), + }, + }, + }); + + await log({ + message: [ + { + tx: { + to: tx.to, + isCall: true, + from: tx.from, + envMode: 'injected', + input: tx.data, + hash: 'call' + (tx.from || '') + tx.to + tx.data, + }, + resolvedData: { + contractName: name, + to: address, + fn: payload.funcABI.name, + params: isFunction + ? decodeInputParams( + tx.data?.replace('0x', '').substring(8), + payload.funcABI + ) + : tx.data, + decodedReturnValue: txFormat.decodeResponse(resp, payload.funcABI), + }, + }, + ], + style: '', + name: 'knownTransaction', + provider: 'injected', + }); + } else { + await log({ + message: [ + { + tx: { ...resp.tx, receipt: resp.receipt }, + receipt: resp.receipt, + resolvedData: { + contractName: name, + to: address, + fn: payload.funcABI.name, + params: isFunction + ? decodeInputParams( + tx.data?.replace('0x', '').substring(8), + payload.funcABI + ) + : tx.data, + }, + }, + ], + style: '', + name: 'knownTransaction', + provider: 'injected', + }); + } +}; diff --git a/apps/remix-dapp/src/assets/instance.json b/apps/remix-dapp/src/assets/instance.json new file mode 100644 index 00000000000..99490e7383a --- /dev/null +++ b/apps/remix-dapp/src/assets/instance.json @@ -0,0 +1,507 @@ +{ + "name": "MyToken", + "address": "0x0d52590858e2a79278e665c21f9caf98d8b4b618", + "network": "Sepolia (11155111) network", + "title": "Owner", + "details": "Set & change owner", + "abi": { + "0x095ea7b3": { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x095ea7b3", + "title": "this is title", + "details": "See {IERC20-approve}. NOTE: If `value` is the maximum `uint256`, the allowance is not updated on `transferFrom`. This is semantically equivalent to an infinite approval. Requirements: - `spender` cannot be the zero address." + }, + "0x42966c68": { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x42966c68", + "details": "Destroys a `value` amount of tokens from the caller. See {ERC20-_burn}." + }, + "0x79cc6790": { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x79cc6790", + "details": "Destroys a `value` amount of tokens from `account`, deducting from the caller's allowance. See {ERC20-_burn} and {ERC20-allowance}. Requirements: - the caller must have allowance for ``accounts``'s tokens of at least `value`." + }, + "0x40c10f19": { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x40c10f19", + "details": "" + }, + "0x8456cb59": { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x8456cb59", + "details": "" + }, + "0xd505accf": { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0xd505accf", + "details": "Sets `value` as the allowance of `spender` over ``owner``'s tokens, given ``owner``'s signed approval. IMPORTANT: The same issues {IERC20-approve} has related to transaction ordering also apply here. Emits an {Approval} event. Requirements: - `spender` cannot be the zero address. - `deadline` must be a timestamp in the future. - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` over the EIP712-formatted function arguments. - the signature must use ``owner``'s current nonce (see {nonces}). For more information on the signature format, see the https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP section]. CAUTION: See Security Considerations above." + }, + "0x715018a6": { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x715018a6", + "details": "Leaves the contract without owner. It will not be possible to call `onlyOwner` functions. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby disabling any functionality that is only available to the owner." + }, + "0xa9059cbb": { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "id": "0xa9059cbb", + "details": "See {IERC20-transfer}. Requirements: - `to` cannot be the zero address. - the caller must have a balance of at least `value`." + }, + "0x23b872dd": { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x23b872dd", + "details": "See {IERC20-transferFrom}. Emits an {Approval} event indicating the updated allowance. This is not required by the EIP. See the note at the beginning of {ERC20}. NOTE: Does not update the allowance if the current allowance is the maximum `uint256`. Requirements: - `from` and `to` cannot be the zero address. - `from` must have a balance of at least `value`. - the caller must have allowance for ``from``'s tokens of at least `value`." + }, + "0xf2fde38b": { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0xf2fde38b", + "details": "Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner." + }, + "0x3f4ba83a": { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + "id": "0x3f4ba83a", + "details": "" + }, + "0xdd62ed3e": { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0xdd62ed3e", + "details": "See {IERC20-allowance}." + }, + "0x70a08231": { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x70a08231", + "details": "See {IERC20-balanceOf}." + }, + "0x313ce567": { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x313ce567", + "details": "Returns the number of decimals used to get its user representation. For example, if `decimals` equals `2`, a balance of `505` tokens should be displayed to a user as `5.05` (`505 / 10 ** 2`). Tokens usually opt for a value of 18, imitating the relationship between Ether and Wei. This is the default value returned by this function, unless it's overridden. NOTE: This information is only used for _display_ purposes: it in no way affects any of the arithmetic of the contract, including {IERC20-balanceOf} and {IERC20-transfer}." + }, + "0x3644e515": { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x3644e515", + "details": "Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}." + }, + "0x84b0196e": { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { + "internalType": "bytes1", + "name": "fields", + "type": "bytes1" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + }, + { + "internalType": "uint256", + "name": "chainId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256[]", + "name": "extensions", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x84b0196e", + "details": "See {IERC-5267}." + }, + "0x06fdde03": { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x06fdde03", + "details": "Returns the name of the token." + }, + "0x7ecebe00": { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x7ecebe00", + "details": "Returns the current nonce for `owner`. This value must be included whenever a signature is generated for {permit}. Every successful call to {permit} increases ``owner``'s nonce by one. This prevents a signature from being used multiple times." + }, + "0x8da5cb5b": { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x8da5cb5b", + "details": "Returns the address of the current owner." + }, + "0x5c975abb": { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x5c975abb", + "details": "Returns true if the contract is paused, and false otherwise." + }, + "0x95d89b41": { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x95d89b41", + "details": "Returns the symbol of the token, usually a shorter version of the name." + }, + "0x18160ddd": { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function", + "id": "0x18160ddd", + "details": "See {IERC20-totalSupply}." + } + }, + "items": { + "A": [ + "0x79cc6790", + "0x715018a6", + "0xa9059cbb", + "0x23b872dd", + "0xf2fde38b", + "0x3f4ba83a", + "0xdd62ed3e" + ], + "B": [ + "0x3644e515", + "0x84b0196e", + "0x06fdde03", + "0x7ecebe00", + "0x8da5cb5b", + "0x5c975abb", + "0x95d89b41", + "0x18160ddd" + ], + "C": [ + "0x70a08231", + "0x8456cb59", + "0xd505accf", + "0x40c10f19", + "0x42966c68", + "0x095ea7b3", + "0x313ce567" + ] + }, + "containers": [ + "A", + "B", + "C" + ], + "shortname": "remix-dapp", + "shareTo": [ + "twitter", + "facebook" + ], + "noTerminal": false, + "verified": true, + "solcVersion": { + "version": "0.8.26", + "canReceive": true + }, + "fallback": { + "stateMutability": "payable", + "type": "fallback" + }, + "receive": { + "stateMutability": "payable", + "type": "receive" + } +} diff --git a/apps/remix-dapp/src/assets/logo.png b/apps/remix-dapp/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cc79f5ece6ad259ef39bf25bea01b44c3dce0f0a GIT binary patch literal 15967 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 NQQAgY#aHh{{U1ati~Rrq literal 0 HcmV?d00001 diff --git a/apps/remix-dapp/src/components/ContractGUI/index.tsx b/apps/remix-dapp/src/components/ContractGUI/index.tsx new file mode 100644 index 00000000000..15393752bd1 --- /dev/null +++ b/apps/remix-dapp/src/components/ContractGUI/index.tsx @@ -0,0 +1,361 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import * as remixLib from '@remix-project/remix-lib'; +import { CopyToClipboard } from '@remix-ui/clipboard' +import { CustomTooltip } from '@remix-ui/helper'; + +const { txFormat, txHelper } = remixLib.execution; +export function ContractGUI(props: any) { + const [title, setTitle] = useState(''); + const [basicInput, setBasicInput] = useState(''); + const [toggleContainer, setToggleContainer] = useState(false); + const [buttonOptions, setButtonOptions] = useState<{ + title: string; + content: string; + classList: string; + dataId: string; + }>({ title: '', content: '', classList: '', dataId: '' }); + const multiFields = useRef>([]); + const basicInputRef = useRef(); + const intl = useIntl(); + + useEffect(() => { + if (props.funcABI.name) { + setTitle(props.funcABI.name); + } else { + setTitle(props.funcABI.type === 'receive' ? '(receive)' : '(fallback)'); + } + setBasicInput(''); + // we have the reset the fields before reseting the previous references. + if (basicInputRef.current) basicInputRef.current.value = ''; + multiFields.current + .filter((el) => el !== null && el !== undefined) + .forEach((el: any) => (el.value = '')); + multiFields.current = []; + }, [props.funcABI]); + + useEffect(() => { + if (props.lookupOnly) { + // // call. stateMutability is either pure or view + setButtonOptions({ + title: title + ' - call', + content: 'call', + classList: 'btn-info', + dataId: title + ' - call', + }); + } else if ( + props.funcABI.stateMutability === 'payable' || + props.funcABI.payable + ) { + // // transact. stateMutability = payable + setButtonOptions({ + title: title + ' - transact (payable)', + content: 'transact', + classList: 'btn-danger', + dataId: title + ' - transact (payable)', + }); + } else { + // // transact. stateMutability = nonpayable + setButtonOptions({ + title: title + ' - transact (not payable)', + content: 'transact', + classList: 'btn-warning', + dataId: title + ' - transact (not payable)', + }); + } + }, [props.lookupOnly, props.funcABI, title]); + + const getEncodedCall = () => { + const multiString = getMultiValsString(multiFields.current); + // copy-to-clipboard icon is only visible for method requiring input params + if (!multiString) { + return intl.formatMessage({ id: 'udapp.getEncodedCallError' }); + } + const multiJSON = JSON.parse('[' + multiString + ']'); + + const encodeObj = txFormat.encodeData(props.funcABI, multiJSON, null); + + if (encodeObj.error) { + console.error(encodeObj.error); + return encodeObj.error; + } else { + return encodeObj.data; + } + }; + + const getEncodedParams = () => { + try { + const multiString = getMultiValsString(multiFields.current); + // copy-to-clipboard icon is only visible for method requiring input params + if (!multiString) { + return intl.formatMessage({ id: 'udapp.getEncodedCallError' }); + } + const multiJSON = JSON.parse('[' + multiString + ']'); + return txHelper.encodeParams(props.funcABI, multiJSON); + } catch (e) { + console.error(e); + } + }; + + const switchMethodViewOn = () => { + setToggleContainer(true); + makeMultiVal(); + }; + + const switchMethodViewOff = () => { + setToggleContainer(false); + const multiValString = getMultiValsString(multiFields.current); + + if (multiValString) setBasicInput(multiValString); + }; + + const getMultiValsString = (fields: (HTMLInputElement | null)[]) => { + const valArray = fields as HTMLInputElement[]; + let ret = ''; + const valArrayTest = []; + + for (let j = 0; j < valArray.length; j++) { + if (ret !== '') ret += ','; + let elVal = valArray[j] ? valArray[j].value : ''; + + valArrayTest.push(elVal); + elVal = elVal.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3'); // replace non quoted number by quoted number + elVal = elVal.replace( + /(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, + '$1"$2"$3' + ); // replace non quoted hex string by quoted hex string + if (elVal) { + try { + JSON.parse(elVal); + } catch (e) { + elVal = '"' + elVal + '"'; + } + } + ret += elVal; + } + const valStringTest = valArrayTest.join(''); + + if (valStringTest) { + return ret; + } else { + return ''; + } + }; + + const makeMultiVal = () => { + const inputString = basicInput; + + if (inputString) { + const inputJSON = txFormat.parseFunctionParams(inputString); + const multiInputs = multiFields.current as HTMLInputElement[]; + + for (let k = 0; k < multiInputs.length; k++) { + if (inputJSON[k]) { + multiInputs[k].value = JSON.stringify(inputJSON[k]); + } + } + } + }; + + const handleActionClick = async () => { + props.clickCallBack(props.funcABI.inputs, basicInput); + }; + + const handleBasicInput = (e: { target: { value: any } }) => { + const value = e.target.value; + + setBasicInput(value); + }; + + const handleExpandMultiClick = () => { + const valsString = getMultiValsString(multiFields.current); + + if (valsString) { + props.clickCallBack(props.funcABI.inputs, valsString); + } else { + props.clickCallBack(props.funcABI.inputs, ''); + } + }; + + return ( +
0 + ? 'udapp_hasArgs' + : '' + }`} + > +
+ +
+ +
+
+ 0) || + props.funcABI.type === 'fallback' || + props.funcABI.type === 'receive' + ) + ? 'hidden' + : 'visible', + }} + /> + 0 + ) + ? 'hidden' + : 'visible', + }} + > +
+
+
+
+
+ {title} +
+ +
+
+ {props.funcABI.inputs.map((inp: any, index: number) => { + return ( +
+ + + { + multiFields.current[index] = el; + }} + className="form-control" + placeholder={inp.type} + data-id={`multiParamManagerInput${inp.name}`} + onChange={handleBasicInput} + /> + +
+ ); + })} +
+
+ + + + + + + +
+ +
+
+
+
+
+
+ ); +} diff --git a/apps/remix-dapp/src/components/DappTop/index.tsx b/apps/remix-dapp/src/components/DappTop/index.tsx new file mode 100644 index 00000000000..672c1c4e829 --- /dev/null +++ b/apps/remix-dapp/src/components/DappTop/index.tsx @@ -0,0 +1,74 @@ +import React, { useContext, useEffect } from 'react'; +import { AppContext } from '../../contexts'; + +const DappTop: React.FC = () => { + const { + appState: { instance }, + } = useContext(AppContext); + const { shareTo, title, details } = instance; + + const getBoxPositionOnWindowCenter = (width: number, height: number) => ({ + left: + window.outerWidth / 2 + + (window.screenX || window.screenLeft || 0) - + width / 2, + top: + window.outerHeight / 2 + + (window.screenY || window.screenTop || 0) - + height / 2, + }); + let windowConfig: any = { + width: 600, + height: 400, + }; + windowConfig = Object.assign( + windowConfig, + getBoxPositionOnWindowCenter(windowConfig.width, windowConfig.height) + ); + + const shareUrl = encodeURIComponent(window.origin); + const shareTitle = encodeURIComponent('Hello everyone, this is my dapp!'); + + return ( +
+
+ {title &&

{title}

} + {details && {details}} +
+ {shareTo && ( +
+ {shareTo.includes('twitter') && ( + { + window.open( + `https://twitter.com/intent/tweet?url=${shareUrl}&text=${shareTitle}`, + '', + Object.keys(windowConfig) + .map((key) => `${key}=${windowConfig[key]}`) + .join(', ') + ); + }} + /> + )} + {shareTo.includes('facebook') && ( + { + window.open( + `https://www.facebook.com/sharer/sharer.php?u=${shareUrl}`, + '', + Object.keys(windowConfig) + .map((key) => `${key}=${windowConfig[key]}`) + .join(', ') + ); + }} + /> + )} +
+ )} +
+ ); +}; + +export default DappTop; diff --git a/apps/remix-dapp/src/components/DragBar/index.tsx b/apps/remix-dapp/src/components/DragBar/index.tsx new file mode 100644 index 00000000000..5e33fc98743 --- /dev/null +++ b/apps/remix-dapp/src/components/DragBar/index.tsx @@ -0,0 +1,62 @@ +// eslint-disable-next-line no-use-before-define +import React, { useContext, useEffect, useState } from 'react'; +import Draggable from 'react-draggable'; +import { AppContext } from '../../contexts'; + +const DragBar = () => { + const { appState, dispatch } = useContext(AppContext); + const { hidden, height } = appState.terminal; + const [dragState, setDragState] = useState(false); + const [dragBarPosY, setDragBarPosY] = useState(0); + const nodeRef = React.useRef(null); // fix for strictmode + + function stopDrag(e: any, data: any) { + const h = window.innerHeight - data.y; + dispatch({ type: 'SET_TERMINAL', payload: { height: h, hidden: false } }); + setDragBarPosY(window.innerHeight - h - 5); + setDragState(false); + } + const handleResize = () => { + setDragBarPosY(window.innerHeight - height - 5); + }; + + useEffect(() => { + handleResize(); + }, [hidden]); + + useEffect(() => { + window.addEventListener('resize', handleResize); + // TODO: not a good way to wait on the ref doms element to be rendered of course + setTimeout(() => { + handleResize(); + }, 2000); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + function startDrag() { + setDragState(true); + } + return ( + <> +
+ +
+
+ + ); +}; + +export default DragBar; diff --git a/apps/remix-dapp/src/components/SettingsUI/account.tsx b/apps/remix-dapp/src/components/SettingsUI/account.tsx new file mode 100644 index 00000000000..6928693ed3f --- /dev/null +++ b/apps/remix-dapp/src/components/SettingsUI/account.tsx @@ -0,0 +1,58 @@ +import React, { useContext, useEffect } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faSpinner } from '@fortawesome/free-solid-svg-icons'; +import { CopyToClipboard } from '@remix-ui/clipboard' +import { AppContext } from '../../contexts'; + +export function AccountUI() { + const intl = useIntl(); + const { appState, dispatch } = useContext(AppContext); + const { selectedAccount, loadedAccounts, isRequesting, provider } = + appState.settings; + const accounts = Object.keys(loadedAccounts); + + const setAccount = (account: string) => { + dispatch({ type: 'SET_SETTINGS', payload: { selectedAccount: account } }); + }; + + useEffect(() => { + if (!selectedAccount && accounts.length > 0) setAccount(accounts[0]); + }, [accounts, selectedAccount]); + + return provider === 'metamask' ? ( +
+ +
+ +
+ +
+
+
+ ) : null; +} diff --git a/apps/remix-dapp/src/components/SettingsUI/gasPrice.tsx b/apps/remix-dapp/src/components/SettingsUI/gasPrice.tsx new file mode 100644 index 00000000000..c0087a5fbd6 --- /dev/null +++ b/apps/remix-dapp/src/components/SettingsUI/gasPrice.tsx @@ -0,0 +1,35 @@ +import React, { useContext } from 'react'; +import { CustomTooltip } from '@remix-ui/helper'; +import { FormattedMessage } from 'react-intl'; +import { AppContext } from '../../contexts'; + +export function GasPriceUI() { + const { appState, dispatch } = useContext(AppContext); + const handleGasLimit = (e: any) => { + dispatch({ type: 'settings/save', payload: { gasLimit: e.target.value } }); + }; + + const gasLimit = appState.settings.gasLimit; + + return ( +
+ + } + > + + +
+ ); +} diff --git a/apps/remix-dapp/src/components/SettingsUI/index.tsx b/apps/remix-dapp/src/components/SettingsUI/index.tsx new file mode 100644 index 00000000000..6ec83bd8ee7 --- /dev/null +++ b/apps/remix-dapp/src/components/SettingsUI/index.tsx @@ -0,0 +1,94 @@ +import React, { useContext } from 'react'; +import { NetworkUI } from './network'; +import { AccountUI } from './account'; +import { GasPriceUI } from './gasPrice'; +import { ValueUI } from './value'; +import { LocaleUI } from './locale'; +// import { ThemeUI } from './theme'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { CopyToClipboard } from '@remix-ui/clipboard' +import { AppContext } from '../../contexts'; +import { CustomTooltip, shortenAddress } from '@remix-ui/helper'; +import { isMobile } from '../../utils/tools'; +import { LowLevelInteractions } from '../UniversalDappUI/lowLevelInteractions'; + +export function SettingsUI() { + const intl = useIntl(); + const { appState } = useContext(AppContext); + const { balance, name, address, verified } = appState.instance; + + return ( +
+
+
+
+
+ + {name} at {shortenAddress(address)} + + + + +
+
+
+ : {balance} ETH +
+
+
+ +
+
+
+ + + + + {/* */} +
+ +
+ + QuickDapp by{' '} + + + + + + + {!isMobile() && verified && ( + + + + )} +
+
+ ); +} diff --git a/apps/remix-dapp/src/components/SettingsUI/locale.tsx b/apps/remix-dapp/src/components/SettingsUI/locale.tsx new file mode 100644 index 00000000000..1e1471a8728 --- /dev/null +++ b/apps/remix-dapp/src/components/SettingsUI/locale.tsx @@ -0,0 +1,53 @@ +import { useContext, useEffect } from 'react'; +import { AppContext } from '../../contexts'; + +const localeMap: Record = { + zh: 'Chinese Simplified - 简体中文', + en: 'English - English', + fr: 'French - Français', + it: 'Italian - Italiano', + es: 'Spanish - Español', +}; + +export function LocaleUI() { + const { appState, dispatch } = useContext(AppContext); + const { selectedLocaleCode } = appState.settings; + const localeCodeList = Object.keys(localeMap); + + useEffect(() => { + const defaultLocaleCode = + localStorage.getItem('selectedLocaleCode') || 'en'; + setLocaleCode(defaultLocaleCode); + }, []); + + const setLocaleCode = (localeCode: string) => { + dispatch({ + type: 'SET_SETTINGS', + payload: { selectedLocaleCode: localeCode }, + }); + localStorage.setItem('selectedLocaleCode', localeCode); + }; + + return ( +
+
+ +
+
+ ); +} diff --git a/apps/remix-dapp/src/components/SettingsUI/network.tsx b/apps/remix-dapp/src/components/SettingsUI/network.tsx new file mode 100644 index 00000000000..41873eca208 --- /dev/null +++ b/apps/remix-dapp/src/components/SettingsUI/network.tsx @@ -0,0 +1,47 @@ +import React, { useContext } from 'react'; +import { AppContext } from '../../contexts'; +import { FormattedMessage } from 'react-intl'; +import { setProvider } from '../../actions'; + +export function NetworkUI() { + const { appState, dispatch } = useContext(AppContext); + const { networkName, provider } = appState.settings; + return ( +
+ +
+ +
+
+ {networkName} +
+ {provider === 'walletconnect' && ( +
+ +
+ )} +
+ ); +} diff --git a/apps/remix-dapp/src/components/SettingsUI/theme.tsx b/apps/remix-dapp/src/components/SettingsUI/theme.tsx new file mode 100644 index 00000000000..4c0759fbb29 --- /dev/null +++ b/apps/remix-dapp/src/components/SettingsUI/theme.tsx @@ -0,0 +1,88 @@ +import { useEffect, useState } from 'react'; +import { FormattedMessage } from 'react-intl'; + +const themeMap: Record = { + Dark: { quality: 'dark', url: 'assets/css/themes/remix-dark_tvx1s2.css' }, + Light: { quality: 'light', url: 'assets/css/themes/remix-light_powaqg.css' }, + Violet: { quality: 'light', url: 'assets/css/themes/remix-violet.css' }, + Unicorn: { quality: 'light', url: 'assets/css/themes/remix-unicorn.css' }, + Midcentury: { + quality: 'light', + url: 'assets/css/themes/remix-midcentury_hrzph3.css', + }, + Black: { quality: 'dark', url: 'assets/css/themes/remix-black_undtds.css' }, + Candy: { quality: 'light', url: 'assets/css/themes/remix-candy_ikhg4m.css' }, + HackerOwl: { quality: 'dark', url: 'assets/css/themes/remix-hacker_owl.css' }, + Cerulean: { + quality: 'light', + url: 'assets/css/themes/bootstrap-cerulean.min.css', + }, + Flatly: { + quality: 'light', + url: 'assets/css/themes/bootstrap-flatly.min.css', + }, + Spacelab: { + quality: 'light', + url: 'assets/css/themes/bootstrap-spacelab.min.css', + }, + Cyborg: { + quality: 'dark', + url: 'assets/css/themes/bootstrap-cyborg.min.css', + }, +}; + +export function ThemeUI() { + const [theme, setTheme] = useState( + localStorage.getItem('selectedTheme') || 'Dark' + ); + const themeList = Object.keys(themeMap); + + useEffect(() => { + selectTheme(theme); + }, []); + + const selectTheme = (selectedTheme: string) => { + localStorage.setItem('selectedTheme', selectedTheme); + setTheme(selectedTheme); + + const themeLinkEle = document.getElementById('theme-link'); + if (themeLinkEle) { + themeLinkEle.remove(); + } + const nextTheme = themeMap[selectedTheme]; // Theme + document.documentElement.style.setProperty('--theme', nextTheme.quality); + + const theme = document.createElement('link'); + theme.setAttribute('rel', 'stylesheet'); + theme.setAttribute('href', 'https://remix.ethereum.org/' + nextTheme.url); + theme.setAttribute('id', 'theme-link'); + + document.head.insertBefore(theme, document.head.firstChild); + }; + + return ( +
+ +
+ +
+
+ ); +} diff --git a/apps/remix-dapp/src/components/SettingsUI/value.tsx b/apps/remix-dapp/src/components/SettingsUI/value.tsx new file mode 100644 index 00000000000..8731920ca72 --- /dev/null +++ b/apps/remix-dapp/src/components/SettingsUI/value.tsx @@ -0,0 +1,108 @@ +import React, { useContext, useEffect, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { BN } from 'bn.js'; +import { CustomTooltip } from '@remix-ui/helper'; +import { AppContext } from '../../contexts'; + +export function ValueUI() { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const inputValue = useRef({} as HTMLInputElement); + + const { appState, dispatch } = useContext(AppContext); + const { sendValue, sendUnit } = appState.settings; + + useEffect(() => { + if (sendValue !== inputValue.current.value) { + inputValue.current.value = sendValue; + } + }, [sendValue]); + + const validateValue = (e: { target: { value: any } }) => { + const value = e.target.value; + + if (!value) { + // assign 0 if given value is + // - empty + inputValue.current.value = '0'; + dispatch({ type: 'SET_SETTINGS', payload: { sendValue: '0' } }); + return; + } + + let v; + try { + v = new BN(value, 10); + dispatch({ + type: 'SET_SETTINGS', + payload: { sendValue: v.toString(10) }, + }); + } catch (e) { + // assign 0 if given value is + // - not valid (for ex 4345-54) + // - contains only '0's (for ex 0000) copy past or edit + inputValue.current.value = '0'; + dispatch({ type: 'SET_SETTINGS', payload: { sendValue: '0' } }); + } + + if (v?.lt(0)) { + inputValue.current.value = '0'; + dispatch({ type: 'SET_SETTINGS', payload: { sendValue: '0' } }); + } + }; + + return ( +
+ +
+ } + > + + + + +
+
+ ); +} diff --git a/apps/remix-dapp/src/components/UiTerminal/ChechTxStatus.tsx b/apps/remix-dapp/src/components/UiTerminal/ChechTxStatus.tsx new file mode 100644 index 00000000000..773a341cdf3 --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/ChechTxStatus.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +const CheckTxStatus = ({ tx, type }: any) => { + if (tx.status === 1 || tx.status === '0x1' || tx.status === true) { + return ( + + ); + } + if (type === 'call' || type === 'unknownCall' || type === 'unknown') { + return ( + + call + + ); + } else if (tx.status === 0 || tx.status === '0x0' || tx.status === false) { + return ( + + ); + } else { + return ( + + ); + } +}; + +export default CheckTxStatus; diff --git a/apps/remix-dapp/src/components/UiTerminal/Context.tsx b/apps/remix-dapp/src/components/UiTerminal/Context.tsx new file mode 100644 index 00000000000..beb9844c845 --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/Context.tsx @@ -0,0 +1,111 @@ +import React from 'react'; +import { shortenHexData } from '@remix-ui/helper'; +import { execution } from '@remix-project/remix-lib'; +const typeConversion = execution.typeConversion; + +const Context = ({ opts, provider }: { opts: any; provider: string }) => { + const data = opts.tx || ''; + const from = opts.from ? shortenHexData(opts.from) : ''; + let to = opts.to; + if (data.to) to = to + ' ' + shortenHexData(data.to); + const val = data.value; + let hash = data.hash ? shortenHexData(data.hash) : ''; + const input = data.input ? shortenHexData(data.input) : ''; + const logs = opts.logs?.decoded?.length ? opts.logs.decoded.length : 0; + const block = data.receipt + ? data.receipt.blockNumber + : data.blockNumber || ''; + const i = data.receipt + ? data.receipt.transactionIndex + : data.transactionIndex; + const value = val ? typeConversion.toInt(val) : 0; + + if (provider?.startsWith('vm')) { + return ( +
+ + + [vm] + +
+ from: {from} +
+
+ to: {to} +
+
+ value: {value} wei +
+
+ data: {input} +
+
+ logs: {logs} +
+
+ hash: {hash} +
+
+
+ ); + } else if (data.resolvedData) { + return ( +
+ + + [block:{block.toString()} txIndex:{i ? i.toString() : '-'}] + +
+ from: {from} +
+
+ to: {to} +
+
+ value: {value} wei +
+
+ data: {input} +
+
+ logs: {logs} +
+
+ hash: {hash} +
+
+
+ ); + } else { + hash = shortenHexData(data.blockHash); + return ( +
+ + + [block:{block.toString()} txIndex:{i ? i.toString() : '-'}] + +
+ from: {from} +
+
+ to: {to} +
+
+ value: {value} wei +
+
+ data: {input} +
+
+ logs: {logs} +
+
+ hash: {hash} +
+
+
+ ); + } +}; + +export default Context; diff --git a/apps/remix-dapp/src/components/UiTerminal/RenderCall.tsx b/apps/remix-dapp/src/components/UiTerminal/RenderCall.tsx new file mode 100644 index 00000000000..2ae45ace56c --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/RenderCall.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import { shortenHexData } from '@remix-ui/helper'; +import CheckTxStatus from './ChechTxStatus'; +import showTable from './Table'; +import { execution } from '@remix-project/remix-lib'; + +const typeConversion = execution.typeConversion; + +const RenderCall = ({ + tx, + resolvedData, + logs, + index, + showTableHash, + txDetails, +}: any) => { + const to = resolvedData.contractName + '.' + resolvedData.fn; + const from = tx.from ? tx.from : ' - '; + const input = tx.input ? shortenHexData(tx.input) : ''; + const txType = 'call'; + + return ( + +
txDetails(event, tx)} + > + + + [call] +
+ from: {from} +
+
+ to: {to} +
+
+ data: {input} +
+
+ +
+ {showTableHash.includes(tx.hash) + ? showTable( + { + hash: tx.hash, + isCall: tx.isCall, + contractAddress: tx.contractAddress, + data: tx, + from, + to, + gas: tx.gas, + input: tx.input, + 'decoded input': resolvedData?.params + ? JSON.stringify( + typeConversion.stringify(resolvedData.params), + null, + '\t' + ) + : ' - ', + 'decoded output': resolvedData?.decodedReturnValue + ? JSON.stringify( + typeConversion.stringify(resolvedData.decodedReturnValue), + null, + '\t' + ) + : ' - ', + val: tx.value, + logs, + transactionCost: tx.transactionCost, + executionCost: tx.executionCost, + }, + showTableHash + ) + : null} +
+ ); +}; + +export default RenderCall; diff --git a/apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx b/apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx new file mode 100644 index 00000000000..f4a31ffb73f --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import CheckTxStatus from './ChechTxStatus'; +import Context from './Context'; +import showTable from './Table'; +import { execution } from '@remix-project/remix-lib'; +const typeConversion = execution.typeConversion; + +const RenderKnownTransactions = ({ + tx, + receipt, + resolvedData, + logs, + index, + showTableHash, + txDetails, + provider, +}: any) => { + const from = tx.from; + const to = resolvedData.contractName + '.' + resolvedData.fn; + const txType = 'knownTx'; + const options = { from, to, tx, logs }; + return ( + +
txDetails(event, tx)} + > + + + +
+ {showTableHash.includes(tx.hash) + ? showTable( + { + hash: tx.hash, + status: receipt !== null ? receipt.status : null, + isCall: tx.isCall, + contractAddress: receipt.contractAddress, + blockHash: tx.blockHash, + blockNumber: tx.blockNumber, + data: tx, + from, + to, + gas: tx.gas, + input: tx.input, + 'decoded input': resolvedData?.params + ? JSON.stringify( + typeConversion.stringify(resolvedData.params), + null, + '\t' + ) + : ' - ', + 'decoded output': resolvedData?.decodedReturnValue + ? JSON.stringify( + typeConversion.stringify(resolvedData.decodedReturnValue), + null, + '\t' + ) + : ' - ', + logs, + val: tx.value, + transactionCost: tx.transactionCost, + executionCost: tx.executionCost, + }, + showTableHash + ) + : null} +
+ ); +}; + +export default RenderKnownTransactions; diff --git a/apps/remix-dapp/src/components/UiTerminal/Table.tsx b/apps/remix-dapp/src/components/UiTerminal/Table.tsx new file mode 100644 index 00000000000..79b9ff3aaa8 --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/Table.tsx @@ -0,0 +1,327 @@ +import React from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { CopyToClipboard } from '@remix-ui/clipboard' +import { shortenHexData } from '@remix-ui/helper'; +import { execution } from '@remix-project/remix-lib'; +const typeConversion = execution.typeConversion; + +const showTable = (opts: any, showTableHash: any) => { + const intl = useIntl(); + let msg = ''; + let toHash; + const data = opts.data; + if (data.to && opts.to !== data.to) { + toHash = opts.to + ' ' + data.to; + } else { + toHash = opts.to; + } + let callWarning = ''; + if (opts.isCall) { + callWarning = intl.formatMessage({ id: 'terminal.callWarning' }); + } + if (!opts.isCall) { + if (opts.status !== undefined && opts.status !== null) { + if (opts.status === 0 || opts.status === '0x0' || opts.status === false) { + msg = intl.formatMessage({ id: 'terminal.msg1' }); + } else if ( + opts.status === 1 || + opts.status === '0x1' || + opts.status === true + ) { + msg = intl.formatMessage({ id: 'terminal.msg2' }); + } + } else { + msg = intl.formatMessage({ id: 'terminal.msg3' }); + } + } + + let stringified = ' - '; + if (opts.logs?.decoded) { + stringified = typeConversion.stringify(opts.logs.decoded); + } + const val = opts.val != null ? typeConversion.toInt(opts.val) : 0; + const gasInt = opts.gas != null ? typeConversion.toInt(opts.gas) : 0; + return ( + + + {opts.status !== undefined ? ( + + + + + ) : null} + {opts.hash && !opts.isCall ? ( + + + + + ) : null} + {opts.blockHash ? ( + + + + + ) : null} + {opts.blockNumber ? ( + + + + + ) : null} + {opts.contractAddress ? ( + + + + + ) : null} + {opts.from ? ( + + + + + ) : null} + {opts.to ? ( + + + + + ) : null} + {opts.gas ? ( + + + + + ) : null} + {opts.transactionCost ? ( + + + + + ) : null} + {opts.executionCost ? ( + + + + + ) : null} + {opts.input ? ( + + + + + ) : null} + {opts['decoded input'] ? ( + + + + + ) : null} + {opts['decoded output'] ? ( + + + + + ) : null} + {opts.logs ? ( + + + + + ) : null} + {opts.val ? ( + + + + + ) : null} + +
+ + {`${opts.status} ${msg}`}
+ + + {opts.hash} + +
+ + + {opts.blockHash} + +
+ + + {opts.blockNumber.toString()} + +
+ + + {opts.contractAddress} + +
+ from + + {opts.from} + +
+ to + + {toHash} + +
+ gas + + {gasInt} gas + +
+ + + {opts.transactionCost} gas {callWarning} + +
+ + + {opts.executionCost} gas {callWarning} + +
+ + + {shortenHexData(opts.input)} + +
+ + + {opts['decoded input'].trim()} + +
+ + + {opts['decoded output']} + +
+ + + {JSON.stringify(stringified, null, '\t')} + + +
+ value + + {val} wei + +
+ ); +}; +export default showTable; diff --git a/apps/remix-dapp/src/components/UiTerminal/TxList.tsx b/apps/remix-dapp/src/components/UiTerminal/TxList.tsx new file mode 100644 index 00000000000..ee46b3357e3 --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/TxList.tsx @@ -0,0 +1,209 @@ +import React, { + useState, + useEffect, + useRef, + type SyntheticEvent, + useContext, +} from 'react'; +import RenderCall from './RenderCall'; +import RenderKnownTransactions from './RenderKnownTransactions'; +import parse from 'html-react-parser'; + +import { KNOWN_TRANSACTION } from './types'; + +import './index.css'; +import { AppContext } from '../../contexts'; + +export interface ClipboardEvent extends SyntheticEvent { + clipboardData: DataTransfer; +} + +export const TxList = (props: any) => { + const { appState } = useContext(AppContext); + const { journalBlocks } = appState.terminal; + + const [showTableHash, setShowTableHash] = useState([]); + + const messagesEndRef = useRef(null); + const typeWriterIndexes = useRef([]); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [journalBlocks.length]); + + const txDetails = (event: any, tx: any) => { + if (showTableHash.includes(tx.hash)) { + const index = showTableHash.indexOf(tx.hash); + if (index > -1) { + setShowTableHash((prevState) => prevState.filter((x) => x !== tx.hash)); + } + } else { + setShowTableHash((prevState) => [...prevState, tx.hash]); + } + scrollToBottom(); + }; + + const classNameBlock = 'remix_ui_terminal_block px-4 py-1 text-break'; + + return ( +
+ {journalBlocks?.map((x: any, index: number) => { + if (x.name === KNOWN_TRANSACTION) { + return x.message.map((trans: any) => { + return ( +
+ {trans.tx.isCall ? ( + + ) : ( + + )} +
+ ); + }); + } else if (Array.isArray(x.message)) { + return x.message.map((msg: any, i: number) => { + if (React.isValidElement(msg)) { + return ( +
+ {msg} +
+ ); + } else if (typeof msg === 'object') { + if (msg.value && isHtml(msg.value)) { + return ( +
+ {parse(msg.value)} +
+ ); + } + let stringified; + try { + stringified = JSON.stringify(msg); + } catch (e) { + console.error(e); + stringified = '< value not displayable >'; + } + return ( +
+ {stringified} +
+ ); + } else { + // typeWriterIndexes: we don't want to rerender using typewriter when the react component updates + if (x.typewriter && !typeWriterIndexes.current.includes(index)) { + typeWriterIndexes.current.push(index); + return ( +
+ { + typewrite(element, msg ? msg.toString() : null, () => { + scrollToBottom(); + }); + }} + className={x.style} + > +
+ ); + } else { + return ( +
+ + {msg ? msg.toString() : null} + +
+ ); + } + } + }); + } else { + // typeWriterIndexes: we don't want to rerender using typewriter when the react component updates + if (x.typewriter && !typeWriterIndexes.current.includes(index)) { + typeWriterIndexes.current.push(index); + return ( +
+ {' '} + { + typewrite(element, x.message, () => { + scrollToBottom(); + }); + }} + className={x.style} + > +
+ ); + } else { + if (typeof x.message !== 'function') { + return ( +
+ {' '} + {x.message} +
+ ); + } + return null; + } + } + })} +
+
+ ); +}; + +const typewrite = (elementsRef: any, message: any, callback: any) => { + (() => { + let count = 0; + const id = setInterval(() => { + if (!elementsRef) return; + count++; + elementsRef.innerText = message.substr(0, count); + // scroll when new line `
+ if (elementsRef.lastChild.tagName === `BR`) callback(); + if (message.length === count) { + clearInterval(id); + callback(); + } + }, 5); + })(); +}; + +function isHtml(value: any) { + if (!value.indexOf) return false; + return ( + value.indexOf(' pre { + max-height: 200px; +} +.remix_ui_terminal_txItem { + color: var(--text-info); + margin-right: 5px; + float: left; +} +.remix_ui_terminal_log { + cursor: pointer; +} +.remix_ui_terminal_log:hover { + opacity: 0.8; +} +.remix_ui_terminal_txStatus { + font-size: 20px; + float: left; +} +.remix_ui_terminal_succeeded { + color: var(--success); +} +.remix_ui_terminal_failed { + color: var(--danger); +} +.remix_ui_terminal_arrow { + color: var(--text-info); + font-size: 20px; + cursor: pointer; +} +.remix_ui_terminal_arrow:hover { + color: var(--secondary); +} +.remix_ui_terminal_call { + font-size: 7px; + min-width: 20px; + min-height: 20px; + color: var(--text-info); +} + +.remix_ui_terminal_tx { + color: var(--text-info); + float: left; +} + +.remix_ui_terminal_tr, +.remix_ui_terminal_td { + border-collapse: collapse; + font-size: 10px; + color: var(--text-info); + border: 1px solid var(--text-info); + transition: + max-height 0.3s, + padding 0.3s; +} +table .active { + transition: + max-height 0.6s, + padding 0.6s; +} +.remix_ui_terminal_tr, +.remix_ui_terminal_td { + padding: 4px; + vertical-align: baseline; +} +.remix_ui_terminal_td:first-child { + min-width: 30%; + width: 30%; + align-items: baseline; + font-weight: bold; +} diff --git a/apps/remix-dapp/src/components/UiTerminal/index.tsx b/apps/remix-dapp/src/components/UiTerminal/index.tsx new file mode 100644 index 00000000000..78554b7f2ac --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/index.tsx @@ -0,0 +1,174 @@ +import React, { + useState, + useEffect, + useRef, + type SyntheticEvent, + useContext, +} from 'react'; +import { FormattedMessage } from 'react-intl'; +// import { CommentCount, DiscussionEmbed } from 'disqus-react'; +import { CustomTooltip } from '@remix-ui/helper'; +import TxList from './TxList'; + +import './index.css'; +import { AppContext } from '../../contexts'; + +export interface ClipboardEvent extends SyntheticEvent { + clipboardData: DataTransfer; +} + +export const RemixUiTerminal = (props: any) => { + const { appState, dispatch } = useContext(AppContext); + const { journalBlocks, height, hidden } = appState.terminal; + + const [display, setDisplay] = useState('transaction'); + + const messagesEndRef = useRef(null); + const typeWriterIndexes = useRef([]); + + // terminal dragable + const panelRef = useRef(null); + const terminalMenu = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [journalBlocks.length]); + + const handleClearConsole = () => { + typeWriterIndexes.current = []; + dispatch({ type: 'SET_TERMINAL', payload: { journalBlocks: [] } }); + }; + /* start of autoComplete */ + + const handleToggleTerminal = () => { + dispatch({ + type: 'SET_TERMINAL', + payload: { hidden: !hidden, height: hidden ? 250 : 35 }, + }); + }; + + return ( +
+
+
+
+
+ + ) : ( + + ) + } + > + + +
+ } + > + + +
+ {/*
{ + setDisplay('transaction'); + }} + > + { + journalBlocks.filter( + (item: any) => item.name === 'knownTransaction' + ).length + }{' '} + Transactions +
+ {shortname && ( +
{ + setDisplay('comment'); + }} + > + + Comments + +
+ )} */} +
+
+
+
+ +
+ {/* {shortname && ( +
+ +
+ )} */} +
+
+
+
+ ); +}; + +export default RemixUiTerminal; diff --git a/apps/remix-dapp/src/components/UiTerminal/types.ts b/apps/remix-dapp/src/components/UiTerminal/types.ts new file mode 100644 index 00000000000..630ace4e097 --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/types.ts @@ -0,0 +1,32 @@ +export interface ROOTS { + steps: any; + cmd: string; + gidx: number; + idx: number; +} + +export const KNOWN_TRANSACTION = 'knownTransaction'; +export const UNKNOWN_TRANSACTION = 'unknownTransaction'; +export const EMPTY_BLOCK = 'emptyBlock'; +export const NEW_TRANSACTION = 'newTransaction'; +export const NEW_BLOCK = 'newBlock'; +export const NEW_CALL = 'newCall'; + +export const HTML = 'html'; +export const LOG = 'log'; +export const TYPEWRITERLOG = 'typewriterlog'; +export const TYPEWRITERWARNING = 'typewriterwarning'; +export const TYPEWRITERSUCCESS = 'typewritersuccess'; +export const INFO = 'info'; +export const WARN = 'warn'; +export const ERROR = 'error'; +export const SCRIPT = 'script'; +export const CLEAR_CONSOLE = 'clearconsole'; +export const LISTEN_ON_NETWORK = 'listenOnNetWork'; +export const CMD_HISTORY = 'cmdHistory'; + +export interface RemixUiTerminalProps { + plugin: any; + onReady: (api: any) => void; + visible: boolean; +} diff --git a/apps/remix-dapp/src/components/UiTerminal/utils.ts b/apps/remix-dapp/src/components/UiTerminal/utils.ts new file mode 100644 index 00000000000..70d8d9f716d --- /dev/null +++ b/apps/remix-dapp/src/components/UiTerminal/utils.ts @@ -0,0 +1,68 @@ +export const getKeyOf = (item: any) => { + return Object.keys(item)[0]; +}; + +export const getValueOf = (item: any) => { + return Object.values(item)[0]; +}; + +export const Objectfilter = (obj: any, filterValue: any) => + obj.filter((item: any) => Object.keys(item)[0].includes(filterValue)); + +export const matched = (arr: [], value: string) => + arr + .map((x) => Object.keys(x).some((x) => x.startsWith(value))) + .some((x) => x); + +const findDeep = ( + object: any, + fn: any, + found = { break: false, value: undefined }, +) => { + if (typeof object !== 'object' || object === null) return; + for (const i in object) { + if (found.break) break; + let el = object[i]; + if (el?.innerText != null) el = el.innerText; + if (fn(el, i, object)) { + found.value = el; + found.break = true; + break; + } else { + findDeep(el, fn, found); + } + } + return found.value; +}; + +export const find = (args: any, query: any) => { + query = query.trim(); + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression + const isMatch = !!findDeep(args, function check(value: null | undefined) { + if (value === undefined || value === null) return false; + if (typeof value === 'function') return false; + if (typeof value === 'object') return false; + const contains = String(value).includes(query.trim()); + return contains; + }); + return isMatch; +}; + +export const wrapScript = (script: string) => { + const isKnownScript = ['remix.', 'console.', 'git', 'gpt'].some((prefix) => + script.trim().startsWith(prefix), + ); + if (isKnownScript) return script; + return ` + try { + const ret = ${script}; + if (ret instanceof Promise) { + ret.then((result) => { console.log(result) }).catch((error) => { console.log(error) }) + } else { + console.log(ret) + } + } catch (e) { + console.log(e.message) + } + `; +}; diff --git a/apps/remix-dapp/src/components/UniversalDappUI/index.css b/apps/remix-dapp/src/components/UniversalDappUI/index.css new file mode 100644 index 00000000000..3a0345e72fb --- /dev/null +++ b/apps/remix-dapp/src/components/UniversalDappUI/index.css @@ -0,0 +1,41 @@ +.udapp_methCaret { + margin-left: 4px; + cursor: pointer; + font-size: 16px; +} + +.udapp_multiArg label { + margin: 0 4px 0 0; + font-size: 10px; +} +.udapp_hasArgs .udapp_multiArg input { + border-left: 1px solid #dddddd; + width: 67%; +} +.udapp_hasArgs input { + font-size: 10px !important; + border-radius: 3px; + border-top-left-radius: 0 !important; + border-bottom-left-radius: 0 !important; +} +.udapp_hasArgs button { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 11px; + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; + border-right: 0; +} +.udapp_intro { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + white-space: pre-wrap; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} +.udapp_intro:hover { + -webkit-line-clamp: inherit; +} + diff --git a/apps/remix-dapp/src/components/UniversalDappUI/index.tsx b/apps/remix-dapp/src/components/UniversalDappUI/index.tsx new file mode 100644 index 00000000000..3d3cde33bcc --- /dev/null +++ b/apps/remix-dapp/src/components/UniversalDappUI/index.tsx @@ -0,0 +1,321 @@ +import React, { useContext, useState } from 'react'; +import * as remixLib from '@remix-project/remix-lib'; +import { ContractGUI } from '../ContractGUI'; +import { TreeView, TreeViewItem } from '@remix-ui/tree-view' +import { BN } from 'bn.js'; +import './index.css'; +import { AppContext } from '../../contexts'; +import { runTransactions } from '../../actions'; + +const txHelper = remixLib.execution.txHelper; + +const getFuncABIInputs = (funABI: any) => { + if (!funABI.inputs) { + return ''; + } + return txHelper.inputParametersDeclarationToString(funABI.inputs); +}; + +export interface FuncABI { + name: string; + type: string; + inputs: { name: string; type: string }[]; + stateMutability: string; + payable?: boolean; + constant?: any; +} + +export function UniversalDappUI(props: any) { + const { appState } = useContext(AppContext); + const instance = appState.instance; + + const address = instance.address; + const { abi: contractABI, items, containers } = instance; + const [expandPath, setExpandPath] = useState([]); + + const runTransaction = ( + lookupOnly: boolean, + funcABI: FuncABI, + valArr: { name: string; type: string }[] | null, + inputsValues: string, + funcIndex?: number + ) => { + const functionName = + funcABI.type === 'function' ? funcABI.name : `(${funcABI.type})`; + const logMsg = `${lookupOnly ? 'call' : 'transact'} to ${ + instance.name + }.${functionName}`; + + runTransactions({ + lookupOnly, + funcABI, + inputsValues, + name: instance.name, + contractABI, + address, + logMsg, + funcIndex, + }); + }; + + const extractDataDefault = (item: any[] | any, parent?: any) => { + const ret: any = {}; + + if (BN.isBN(item)) { + ret.self = item.toString(10); + ret.children = []; + } else { + if (item instanceof Array) { + ret.children = item.map((item, index) => { + return { key: index, value: item }; + }); + ret.self = 'Array'; + ret.isNode = true; + ret.isLeaf = false; + } else if (item instanceof Object) { + ret.children = Object.keys(item).map((key) => { + return { key, value: item[key] }; + }); + ret.self = 'Object'; + ret.isNode = true; + ret.isLeaf = false; + } else { + ret.self = item; + ret.children = null; + ret.isNode = false; + ret.isLeaf = true; + } + } + return ret; + }; + + const handleExpand = (path: string) => { + if (expandPath.includes(path)) { + const filteredPath = expandPath.filter((value) => value !== path); + + setExpandPath(filteredPath); + } else { + setExpandPath([...expandPath, path]); + } + }; + + const label = (key: string | number, value: string) => { + return ( +
+ + +
+ ); + }; + + const renderData = ( + item: any[], + parent: any, + key: string | number, + keyPath: string + ) => { + const data = extractDataDefault(item, parent); + const children = (data.children || []).map( + (child: { value: any[]; key: string }, index: any) => { + return renderData( + child.value, + data, + child.key, + keyPath + '/' + child.key + ); + } + ); + + if (children && children.length > 0) { + return ( + { + handleExpand(keyPath); + }} + expand={expandPath.includes(keyPath)} + > + + {children} + + + ); + } else { + return ( + { + handleExpand(keyPath); + }} + expand={expandPath.includes(keyPath)} + /> + ); + } + }; + + return ( +
+ {containers + ? containers.map((id: any) => { + return ( +
+ {items[id].map((funcId: any, index: any) => { + const funcABI = contractABI[funcId]; + if (funcABI.type !== 'function') return null; + const isConstant = + funcABI.constant !== undefined ? funcABI.constant : false; + const lookupOnly = + funcABI.stateMutability === 'view' || + funcABI.stateMutability === 'pure' || + isConstant; + const inputs = getFuncABIInputs(funcABI); + return ( +
+
+
+ {funcABI.title &&

{funcABI.title}

} + { + runTransaction( + lookupOnly, + funcABI, + valArray, + inputsValues, + funcId + ); + }} + inputs={inputs} + lookupOnly={lookupOnly} + key={funcId} + /> + {funcABI.details && ( +
+ {funcABI.details} +
+ )} + {lookupOnly && ( +
+ + {Object.keys( + instance.decodedResponse || {} + ).map((key) => { + const funcIndex = funcId; + const response = + instance.decodedResponse[key]; + + return key === funcIndex + ? Object.keys(response || {}).map( + (innerkey, _index) => { + return renderData( + instance.decodedResponse[key][ + innerkey + ], + response, + innerkey, + innerkey + ); + } + ) + : null; + })} + +
+ )} +
+
+
+ ); + })} +
+ ); + }) + : contractABI?.map((funcABI: any, index: any) => { + if (funcABI.type !== 'function') return null; + const isConstant = + funcABI.constant !== undefined ? funcABI.constant : false; + const lookupOnly = + funcABI.stateMutability === 'view' || + funcABI.stateMutability === 'pure' || + isConstant; + const inputs = getFuncABIInputs(funcABI); + + return ( +
+
+
+
+ { + runTransaction( + lookupOnly, + funcABI, + valArray, + inputsValues, + index + ); + }} + inputs={inputs} + lookupOnly={lookupOnly} + key={index} + /> + {lookupOnly && ( +
+ + {Object.keys(instance.decodedResponse || {}).map( + (key) => { + const funcIndex = index.toString(); + const response = instance.decodedResponse[key]; + + return key === funcIndex + ? Object.keys(response || {}).map( + (innerkey, _index) => { + return renderData( + instance.decodedResponse[key][ + innerkey + ], + response, + innerkey, + innerkey + ); + } + ) + : null; + } + )} + +
+ )} +
+
+
+
+ ); + })} +
+ ); +} diff --git a/apps/remix-dapp/src/components/UniversalDappUI/lowLevelInteractions.tsx b/apps/remix-dapp/src/components/UniversalDappUI/lowLevelInteractions.tsx new file mode 100644 index 00000000000..6e8709552b3 --- /dev/null +++ b/apps/remix-dapp/src/components/UniversalDappUI/lowLevelInteractions.tsx @@ -0,0 +1,211 @@ +import React, { useContext, useState } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { CustomTooltip, is0XPrefixed, isHexadecimal, isNumeric } from '@remix-ui/helper'; +import { AppContext } from '../../contexts'; +import './index.css'; +import { runTransactions } from '../../actions'; + +export interface FuncABI { + name: string; + type: string; + inputs: { name: string; type: string }[]; + stateMutability: string; + payable?: boolean; + constant?: any; +} + +export function LowLevelInteractions(props: any) { + const { appState } = useContext(AppContext); + const { instance, settings } = appState; + + const address = instance.address; + const contractABI = instance.abi; + const intl = useIntl(); + const [llIError, setLlIError] = useState(''); + const [calldataValue, setCalldataValue] = useState(''); + + const sendData = () => { + setLlIError(''); + const fallback = instance.fallback; + const receive = instance.receive; + const args = { + funcABI: fallback || receive, + address, + contractName: instance.name, + contractABI, + }; + const amount = settings.sendValue; + + if (amount !== '0') { + // check for numeric and receive/fallback + if (!isNumeric(amount)) { + setLlIError(intl.formatMessage({ id: 'udapp.llIError1' })); + return; + } else if ( + !receive && + !(fallback && fallback.stateMutability === 'payable') + ) { + setLlIError(intl.formatMessage({ id: 'udapp.llIError2' })); + return; + } + } + let calldata = calldataValue; + + if (calldata) { + if (calldata.length < 4 && is0XPrefixed(calldata)) { + setLlIError(intl.formatMessage({ id: 'udapp.llIError3' })); + return; + } else { + if (is0XPrefixed(calldata)) { + calldata = calldata.substr(2, calldata.length); + } + if (!isHexadecimal(calldata)) { + setLlIError(intl.formatMessage({ id: 'udapp.llIError4' })); + return; + } + } + if (!fallback) { + setLlIError(intl.formatMessage({ id: 'udapp.llIError5' })); + return; + } + } + + if (!receive && !fallback) { + setLlIError(intl.formatMessage({ id: 'udapp.llIError6' })); + return; + } + + // we have to put the right function ABI: + // if receive is defined and that there is no calldata => receive function is called + // if fallback is defined => fallback function is called + if (receive && !calldata) args.funcABI = receive; + else if (fallback) args.funcABI = fallback; + + if (!args.funcABI) { + setLlIError(intl.formatMessage({ id: 'udapp.llIError7' })); + return; + } + runTransaction(false, args.funcABI, null, calldataValue); + }; + + const runTransaction = ( + lookupOnly: boolean, + funcABI: FuncABI, + valArr: { name: string; type: string }[] | null, + inputsValues: string, + funcIndex?: number + ) => { + const functionName = + funcABI.type === 'function' ? funcABI.name : `(${funcABI.type})`; + const logMsg = `${lookupOnly ? 'call' : 'transact'} to ${ + instance.name + }.${functionName}`; + + runTransactions({ + lookupOnly, + funcABI, + inputsValues, + name: instance.name, + contractABI, + address, + logMsg, + funcIndex, + }); + }; + + const handleCalldataChange = (e: { target: { value: any } }) => { + const value = e.target.value; + + setCalldataValue(value); + }; + + return ( +
+
+
+
+
+
+ +
+ } + > + { + // receive method added to solidity v0.6.x. use this as diff. + instance.solcVersion.canReceive === false ? ( + + + + ) : ( + + + + ) + } + +
+
+ +
+ } + > + + + } + > + + +
+
+
+ +
+
+
+
+
+ ); +} diff --git a/apps/remix-dapp/src/contexts/index.ts b/apps/remix-dapp/src/contexts/index.ts new file mode 100644 index 00000000000..e1fc5812f23 --- /dev/null +++ b/apps/remix-dapp/src/contexts/index.ts @@ -0,0 +1,3 @@ +import {createContext} from 'react' + +export const AppContext = createContext({}) diff --git a/apps/remix-dapp/src/index.css b/apps/remix-dapp/src/index.css new file mode 100644 index 00000000000..d1e5f2733ab --- /dev/null +++ b/apps/remix-dapp/src/index.css @@ -0,0 +1,4 @@ +html, body, #root { + height: 100%; +} + diff --git a/apps/remix-dapp/src/index.html b/apps/remix-dapp/src/index.html new file mode 100644 index 00000000000..bdb2c5188fb --- /dev/null +++ b/apps/remix-dapp/src/index.html @@ -0,0 +1,17 @@ + + + + + + + + RemixDapp + + + + +
+ + diff --git a/apps/remix-dapp/src/locales/en/index.ts b/apps/remix-dapp/src/locales/en/index.ts new file mode 100644 index 00000000000..5a47cee2565 --- /dev/null +++ b/apps/remix-dapp/src/locales/en/index.ts @@ -0,0 +1,14 @@ +function readAndCombineJsonFiles() { + // @ts-expect-error + const dataContext = require.context('./', true, /\.json$/) + + let combinedData = {} + dataContext.keys().forEach((key) => { + const jsonData = dataContext(key) + combinedData = { ...combinedData, ...jsonData } + }) + + return combinedData +} + +export default readAndCombineJsonFiles(); diff --git a/apps/remix-dapp/src/locales/en/terminal.json b/apps/remix-dapp/src/locales/en/terminal.json new file mode 100644 index 00000000000..20dd9f8fd3a --- /dev/null +++ b/apps/remix-dapp/src/locales/en/terminal.json @@ -0,0 +1,44 @@ +{ + "terminal.listen": "listen on all transactions", + "terminal.listenVM": "Listen on all transactions is disabled for VM environment", + "terminal.listenTitle": "If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you", + "terminal.search": "Search with transaction hash or address", + "terminal.used": "used", + "terminal.debug": "Debug", + "terminal.welcomeText1": "Welcome to", + "terminal.welcomeText2": "Your files are stored in", + "terminal.welcomeText3": "You can use this terminal to", + "terminal.welcomeText4": "Check transactions details and start debugging", + "terminal.welcomeText5": "Execute JavaScript scripts", + "terminal.welcomeText6": "Input a script directly in the command line interface", + "terminal.welcomeText7": "Select a Javascript file in the file explorer and then run `remix.execute()` or `remix.exeCurrent()` in the command line interface", + "terminal.welcomeText8": "Right click on a JavaScript file in the file explorer and then click `Run`", + "terminal.welcomeText9": "The following libraries are accessible", + "terminal.welcomeText10": "Type the library name to see available commands", + "terminal.text1": "This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.", + "terminal.hideTerminal": "Hide Terminal", + "terminal.showTerminal": "Show Terminal", + "terminal.clearConsole": "Clear console", + "terminal.pendingTransactions": "Pending Transactions", + "terminal.toasterMsg1": "no content to execute", + "terminal.toasterMsg2": "provider for path {fileName} not found", + "terminal.vmMode": "VM mode", + "terminal.vmModeMsg": "Cannot debug this call. Debugging calls is only possible in Remix VM mode.", + "terminal.ok": "Ok", + "terminal.cancel": "Cancel", + "terminal.callWarning": "(Cost only applies when called by a contract)", + "terminal.msg1": "Transaction mined but execution failed", + "terminal.msg2": "Transaction mined and execution succeed", + "terminal.msg3": "Status not available at the moment", + "terminal.status": "status", + "terminal.transactionHash": "transaction hash", + "terminal.blockHash": "block hash", + "terminal.blockNumber": "block number", + "terminal.contractAddress": "contract address", + "terminal.transactionCost": "transaction cost", + "terminal.executionCost": "execution cost", + "terminal.input": "input", + "terminal.decodedInput": "decoded input", + "terminal.decodedOutput": "decoded output", + "terminal.logs": "logs" +} diff --git a/apps/remix-dapp/src/locales/en/udapp.json b/apps/remix-dapp/src/locales/en/udapp.json new file mode 100644 index 00000000000..fb0635d007b --- /dev/null +++ b/apps/remix-dapp/src/locales/en/udapp.json @@ -0,0 +1,155 @@ +{ + "udapp.displayName": "Deploy & run transactions", + + "udapp._comment_gasPrice.tsx": "libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx", + "udapp.gasLimit": "Gas limit", + "udapp.tooltipText4": "The default gas limit is 3M. Adjust as needed.", + + "udapp._comment_value.tsx": "libs/remix-ui/run-tab/src/lib/components/value.tsx", + "udapp.value": "Value", + "udapp.tooltipText5": "Enter an amount and choose its unit", + + "udapp._comment_contractDropdownUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx", + "udapp.contract": "Contract", + "udapp.compiledBy": "compiled by {compilerName}", + "udapp.warningEvmVersion": "Please make sure that the current network is compatible with this evm version: {evmVersion}. Otherwise any deployment will fail.", + "udapp.infoSyncCompiledContractTooltip": "Click here to import contracts compiled from an external framework.This action is enabled when Remix is connected to an external framework (hardhat, truffle, foundry) through remixd.", + "udapp.remixIpfsUdappTooltip": "Publishing the source code and metadata to IPFS facilitates source code verification using Sourcify and will greatly foster contract adoption (auditing, debugging, calling it, etc...)", + "udapp.deploy": "Deploy", + "udapp.publishTo": "Publish to", + "udapp.atAddress": "At Address", + "udapp.atAddressOptionsTitle1": "address of contract", + "udapp.atAddressOptionsTitle2": "Interact with the deployed contract - requires the .abi file or compiled .sol file to be selected in the editor (with the same compiler configuration)", + "udapp.atAddressOptionsTitle3": "Compile a *.sol file or select a *.abi file.", + "udapp.atAddressOptionsTitle4": "To interact with a deployed contract, either enter its address and compile its source *.sol file (with the same compiler settings) or select its .abi file in the editor. ", + "udapp.contractOptionsTitle1": "Please compile *.sol file to deploy or access a contract", + "udapp.contractOptionsTitle2": "Select a compiled contract to deploy or to use with At Address.", + "udapp.contractOptionsTitle3": "Select and compile *.sol file to deploy or access a contract.", + "udapp.contractOptionsTitle4": "When there is a compiled .sol file, choose the contract to deploy or to use with At Address.", + "udapp.checkSumWarning": "It seems you are not using a checksumed address.A checksummed address is an address that contains uppercase letters, as specified in {a}.Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.", + "udapp.isOverSizePromptEip170": "Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fail if the current network has activated the eip 170. More info: {a}", + "udapp.isOverSizePromptEip3860": "Contract creation init code exceeds the allowed max code size of 49152 bytes. The deployment will likely fail if the current network has activated the eip 3860. More info: {a}", + "udapp.thisContractMayBeAbstract": "This contract may be abstract, it may not implement an abstract parent's methods completely or it may not invoke an inherited contract's constructor correctly.", + "udapp.noCompiledContracts": "No compiled contracts", + "udapp.addressOfContract": "Address of contract", + "udapp.loadContractFromAddress": "Load contract from Address", + "udapp.ok": "OK", + "udapp.alert": "Alert", + "udapp.proceed": "Proceed", + "udapp.cancel": "Cancel", + "udapp.abiFileSelected": "ABI file selected", + "udapp.evmVersion": "evm version", + "udapp.addressNotValid": "The address is not valid", + + "udapp._comment_account.tsx": "libs/remix-ui/run-tab/src/lib/components/account.tsx", + "udapp.account": "Account", + "udapp.locales": "Language", + "udapp.themes": "Themes", + "udapp.signAMessage": "Sign a message", + "udapp.enterAMessageToSign": "Enter a message to sign", + "udapp.hash": "hash", + "udapp.signature": "signature", + "udapp.injectedTitle": "Unfortunately it's not possible to create an account using injected provider. Please create the account directly from your provider (i.e metamask or other of the same type).", + "udapp.createNewAccount": "Create a new account", + "udapp.web3Title": "Creating an account is possible only in Personal mode. Please go to Settings to enable it.", + "udapp.defaultTitle": "Unfortunately it's not possible to create an account using an external wallet ({selectExEnv}).", + "udapp.text1": "Please provide a Passphrase for the account creation", + "udapp.tooltipText1": "Account list is empty, please make sure the current provider is properly connected to remix", + "udapp.modalTitle1": "Passphrase to sign a message", + "udapp.modalMessage1": "Enter your passphrase for this account to sign the message", + "udapp.copyAccount": "Copy account to clipboard", + "udapp.signMsgUsingAccount": "Sign a message using this account", + + "udapp._comment_environment.tsx": "libs/remix-ui/run-tab/src/lib/components/environment.tsx", + "udapp.environment": "Environment", + "udapp.environmentDocs": "Click for docs about Environment", + "udapp.tooltipText2": "Open chainlist.org and get the connection specs of the chain you want to interact with.", + "udapp.tooltipText3": "Click to open a bridge for converting L1 mainnet ETH to the selected network currency.", + + "udapp._comment_instanceContainerUI.tsx": "libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx", + "udapp.deployedContracts": "Deployed Contracts", + "udapp.deployAndRunClearInstances": "Clear instances list and reset recorder", + "udapp.deployAndRunNoInstanceText": "Currently you have no contract instances to interact with.", + "udapp.tooltipText6": "Autogenerated generic user interfaces for interaction with deployed contracts", + + "udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx", + "udapp.transactionsRecorded": "Transactions recorded", + "udapp.transactionsCountTooltip": "The number of recorded transactions", + "udapp.transactionSaveTooltip1": "No transactions to save", + "udapp.transactionSaveTooltip2": "Save {count} transaction as scenario file", + "udapp.transactionSaveTooltip3": "Save {count} transactions as scenario file", + "udapp.transactionsWalkthroughTooltip": "Start walkthrough tour for recorder.", + "udapp.infoRecorderTooltip": "Save transactions (deployed contracts and function executions) and replay them in another environment e.g Transactions created in Remix VM can be replayed in the Injected Provider.", + "udapp.livemodeRecorderTooltip": "If contracts are updated after recording transactions, checking this box will run recorded transactions with the latest copy of the compiled contracts", + "udapp.livemodeRecorderLabel": "Run transactions using the latest compilation result", + "udapp.runRecorderTooltip": "Run transaction(s) from the current scenario file", + "udapp.save": "Save", + "udapp.run": "Run", + + "udapp._comment_contractGUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx", + "udapp.parameters": "Parameters", + "udapp.copyParameters": "Copy encoded input parameters to clipboard", + "udapp.copyCalldata": "Copy calldata to clipboard", + "udapp.deployWithProxy": "Deploy with Proxy", + "udapp.upgradeWithProxy": "Upgrade with Proxy", + "udapp.getEncodedCallError": "cannot encode empty arguments", + "udapp.proxyAddressError1": "proxy address cannot be empty", + "udapp.proxyAddressError2": "not a valid contract address", + "udapp.tooltipText11": "Proxy address cannot be empty", + "udapp.tooltipText12": "Input required", + "udapp.tooltipText13": "Deployed {date}", + + "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", + "udapp.tooltipText7": "Remove from the list", + "udapp.tooltipText8": "Click for docs about using 'receive'/'fallback'", + "udapp.tooltipText9": "The Calldata to send to fallback function of the contract.", + "udapp.tooltipText10": "Send data to contract.", + "udapp.balance": "Balance", + "udapp.lowLevelInteractions": "Low level interactions", + "udapp.llIError1": "Value to send should be a number", + "udapp.llIError2": "In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function", + "udapp.llIError3": "The calldata should be a valid hexadecimal value with size of at least one byte.", + "udapp.llIError4": "The calldata should be a valid hexadecimal value.", + "udapp.llIError5": "'Fallback' function is not defined", + "udapp.llIError6": "Both 'receive' and 'fallback' functions are not defined", + "udapp.llIError7": "Please define a 'Fallback' function to send calldata and a either 'Receive' or payable 'Fallback' to send ethers", + "udapp.copy": "Copy", + + "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", + "udapp.mainnetText1": "You are about to create a transaction on {name} Network. Confirm the details to send the info to your provider.", + "udapp.mainnetText2": "The provider for many users is MetaMask. The provider will ask you to sign the transaction before it is sent to {name} Network.", + "udapp.amount": "Amount", + "udapp.gasEstimation": "Gas estimation", + "udapp.maxPriorityFee": "Max Priority fee", + "udapp.maxFee": "Max fee (Not less than base fee {baseFeePerGas} Gwei)", + "udapp.contractCreation": "Contract Creation", + "udapp.transactionFee": "Transaction is invalid. Max fee should not be less than Base fee", + "udapp.title1": "Represents the part of the tx fee that goes to the miner.", + "udapp.title2": "Represents the maximum amount of fee that you will pay for this transaction. The minimun needs to be set to base fee.", + "udapp.gasPrice": "Gas price", + "udapp.gweiText": "visit {a} for current gas price info.", + "udapp.maxTransactionFee": "Max transaction fee", + "udapp.mainnetText3": "Do not show this warning again.", + + "udapp._comment_run-tab.tsx": "libs/remix-ui/run-tab/src/lib/run-tab.tsx", + "udapp.gasEstimationPromptText": "Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?", + + "udapp._comment_custom-dropdown.tsx": "libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx", + "udapp.enterProxyAddress": "Enter Proxy Address", + + "udapp._comment_provider": "apps/remix-ide/src/app/providers", + "udapp.customVmForkProviderText": "Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state.", + "udapp.nodeUrl": "Node URL", + "udapp.blockNumber": "Block number (or \"latest\")", + "udapp.externalHttpProviderText1": "Note: To use Geth & https://remix.ethereum.org, configure it to allow requests from Remix:(see Geth Docs on rpc server)", + "udapp.externalHttpProviderText2": "To run Remix & a local Geth test node, use this command: (see Geth Docs on Dev mode)", + "udapp.externalHttpProviderText3": "WARNING: It is not safe to use the --http.corsdomain flag with a wildcard: --http.corsdomain *", + "udapp.externalHttpProviderText4": "For more info: Remix Docs on External HTTP Provider", + "udapp.foundryProviderText1": "Note: To run Anvil on your system, run:", + "udapp.foundryProviderText2": "For more info, visit: Foundry Documentation", + "udapp.ganacheProviderText1": "Note: To run Ganache on your system, run:", + "udapp.ganacheProviderText2": "For more info, visit: Ganache Documentation", + "udapp.hardhatProviderText1": "Note: To run Hardhat network node on your system, go to hardhat project folder and run command:", + "udapp.hardhatProviderText2": "For more info, visit: Hardhat Documentation", + "udapp.viewSourceCode": "View Source Code" +} diff --git a/apps/remix-dapp/src/locales/es/index.ts b/apps/remix-dapp/src/locales/es/index.ts new file mode 100644 index 00000000000..2b7f6d597c0 --- /dev/null +++ b/apps/remix-dapp/src/locales/es/index.ts @@ -0,0 +1,18 @@ +import enJson from '../en'; + +function readAndCombineJsonFiles() { + // @ts-expect-error + const dataContext = require.context('./', true, /\.json$/) + + let combinedData = {} + dataContext.keys().forEach((key) => { + const jsonData = dataContext(key) + combinedData = { ...combinedData, ...jsonData } + }) + + return combinedData +} + +// There may have some un-translated content. Always fill in the gaps with EN JSON. +// No need for a defaultMessage prop when render a FormattedMessage component. +export default Object.assign({}, enJson, readAndCombineJsonFiles()); diff --git a/apps/remix-dapp/src/locales/es/terminal.json b/apps/remix-dapp/src/locales/es/terminal.json new file mode 100644 index 00000000000..9c59e05ada0 --- /dev/null +++ b/apps/remix-dapp/src/locales/es/terminal.json @@ -0,0 +1,43 @@ +{ + "terminal.listen": "escuchar en todas las transacciones", + "terminal.listenTitle": "Si está selccionado, Remix escuchará en todas las transacciones minadas en el entorno actual y no solo las transacciones creadas por ti", + "terminal.search": "Buscar con el hash de transacción o dirección", + "terminal.used": "usado", + "terminal.debug": "Depurar", + "terminal.welcomeText1": "Bienvenido a", + "terminal.welcomeText2": "Sus archivos están guardados en", + "terminal.welcomeText3": "Puedes usar este terminal para", + "terminal.welcomeText4": "Revisa los detalles de las transacciones y empieza a depurar", + "terminal.welcomeText5": "Ejecutar scripts de JavaScript", + "terminal.welcomeText6": "Introduce un script directamente en la interfaz de línea de comandos", + "terminal.welcomeText7": "Selecciona un archivo Javascript en el explorador de archivos y luego ejecuta `remix.execute()` o `remix.exeCurrent()` en la interfaz de línea de comandos", + "terminal.welcomeText8": "Haz clic derecho en un archivo JavaScript en el explorador de archivos y luego haz clic en `Ejecutar`", + "terminal.welcomeText9": "Las siguientes librerías son accesibles", + "terminal.welcomeText10": "Escriba el nombre de la librería para ver los comandos disponibles", + "terminal.text1": "Este tipo de comando ha quedado obsoleto y ya no está funcionando. Por favor, ejecute remix.help() para listar los comandos disponibles.", + "terminal.hideTerminal": "Ocultar Terminal", + "terminal.showTerminal": "Mostrar Terminal", + "terminal.clearConsole": "Limpiar la consola", + "terminal.pendingTransactions": "Transacciones Pendientes", + "terminal.toasterMsg1": "no hay contenido para ejecutar", + "terminal.toasterMsg2": "proveedor para la ruta {fileName} no encontrado", + "terminal.vmMode": "Modo MV", + "terminal.vmModeMsg": "No se puede depurar esta llamada. La depuración de llamadas sólo es posible en el modo MV Remix.", + "terminal.ok": "Aceptar", + "terminal.cancel": "Cancelar", + "terminal.callWarning": "(El costo solo aplica cuando es llamado por un contrato)", + "terminal.msg1": "Transacción minada pero la ejecución falló", + "terminal.msg2": "Transacción minada y ejecución exitosa", + "terminal.msg3": "Estado no disponible en este momento", + "terminal.status": "estado", + "terminal.transactionHash": "hash de transacción", + "terminal.blockHash": "hash de bloque", + "terminal.blockNumber": "número de bloque", + "terminal.contractAddress": "dirección del contrato", + "terminal.transactionCost": "coste de transacción", + "terminal.executionCost": "costo de ejecución", + "terminal.input": "entrada", + "terminal.decodedInput": "entrada descodificada", + "terminal.decodedOutput": "salida descodificada", + "terminal.logs": "registros" +} diff --git a/apps/remix-dapp/src/locales/es/udapp.json b/apps/remix-dapp/src/locales/es/udapp.json new file mode 100644 index 00000000000..936eb2f75e3 --- /dev/null +++ b/apps/remix-dapp/src/locales/es/udapp.json @@ -0,0 +1,141 @@ +{ + "udapp.displayName": "Publicar y ejecutar transacciones", + "udapp._comment_gasPrice.tsx": "libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx", + "udapp.gasLimit": "Límite de gas", + "udapp.tooltipText4": "El límite de gas por defecto es de 3M. Ajuste según sea necesario.", + "udapp._comment_value.tsx": "libs/remix-ui/run-tab/src/lib/components/value.tsx", + "udapp.value": "Valor", + "udapp.tooltipText5": "Introduzca una cantidad y elija su unidad", + "udapp._comment_contractDropdownUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx", + "udapp.contract": "Contrato", + "udapp.compiledBy": "compilado por {compilerName}", + "udapp.warningEvmVersion": "Por favor, asegúrese de que la red actual es compatible con esta versión evm: {evmVersion}. De lo contrario, cualquier publicación fallará.", + "udapp.infoSyncCompiledContractTooltip": "Haz clic aquí para importar contratos compilados por un framework externo. Esta acción es activada cuando Remix está conectado a un framework externo (hardhat, truffle, foundry) a través de remixd.", + "udapp.remixIpfsUdappTooltip": "Publicar el código fuente y los metadatos en IPFS facilita la verificación del código fuente usando Sourcify y fomentará enormemente la adopción del contrato (auditoría, depuración, el llamarlo, etc...)", + "udapp.deploy": "Publicar", + "udapp.publishTo": "Publicar en", + "udapp.atAddress": "En la Dirección", + "udapp.atAddressOptionsTitle1": "dirección del contrato", + "udapp.atAddressOptionsTitle2": "Interactuar con el contrato publicado - requiere que el archivo .abi o el archivo compilado .sol sea seleccionado en el editor (con la misma configuración del compilador)", + "udapp.atAddressOptionsTitle3": "Compila un archivo *.sol o seleccione un archivo *.abi.", + "udapp.atAddressOptionsTitle4": "Para interactuar con un contrato publicado, ingrese su dirección y compile su archivo fuente *.sol (con la misma configuración del compilador) o seleccione su archivo .abi en el editor. ", + "udapp.contractOptionsTitle1": "Por favor compile el archivo *.sol para publicar o acceder a un contrato", + "udapp.contractOptionsTitle2": "Seleccione un contrato compilado para publicar o usar En la Dirección.", + "udapp.contractOptionsTitle3": "Seleccione y compile el archivo *.sol para publicar o acceder a un contrato.", + "udapp.contractOptionsTitle4": "Cuando hay un archivo .sol compilado, elija el contrato para publicar o utilizar con En la Dirección.", + "udapp.checkSumWarning": "Parece que no está utilizando una dirección de suma comprobada. Una dirección de suma comprobada es una dirección que contiene letras mayúsculas, como se especifica en {a}. Las direcciones de sumas comprobadas están destinadas a ayudar a evitar que los usuarios envíen transacciones a una dirección incorrecta.", + "udapp.isOverSizePromptEip170": "La inicialización de la creación de contratos devuelve información con una longitud de más de 24576 bytes. La publicación probablemente fallará si la red actual ha activado el eip 170. Más información en: {a}", + "udapp.isOverSizePromptEip3860": "El código de inicio de creación del contrato excede el tamaño máximo de código permitido de 49152 bytes. La publicación probablemente fallará si la red actual ha activado el eip 3860. Más información en: {a}", + "udapp.thisContractMayBeAbstract": "Este contrato podría ser abstracto, podría no implementar los métodos de un padre abstracto completamente o podría no invocar correctamente al constructor de un contrato heredado.", + "udapp.noCompiledContracts": "No hay contratos compilados", + "udapp.addressOfContract": "Dirección del contrato", + "udapp.loadContractFromAddress": "Cargar contrato desde la Dirección", + "udapp.ok": "ACEPTAR", + "udapp.alert": "Alerta", + "udapp.proceed": "Continuar", + "udapp.cancel": "Cancelar", + "udapp.abiFileSelected": "Archivo ABI seleccionado", + "udapp.evmVersion": "versión de evm", + "udapp.addressNotValid": "La dirección no es válida", + "udapp._comment_account.tsx": "libs/remix-ui/run-tab/src/lib/components/account.tsx", + "udapp.account": "Cuenta", + "udapp.locales": "Idioma", + "udapp.themes": "Temas", + "udapp.signAMessage": "Firmar un mensaje", + "udapp.enterAMessageToSign": "Introduce un mensaje para firmar", + "udapp.hash": "hash", + "udapp.signature": "firma", + "udapp.injectedTitle": "Lamentablemente no es posible crear una cuenta usando un proveedor inyectado. Por favor, crea la cuenta directamente desde tu proveedor (es decir, metamask u otra del mismo tipo).", + "udapp.createNewAccount": "Crear una nueva cuenta", + "udapp.web3Title": "La creación de una cuenta solo es posible en el modo Personal. Por favor, ve a Ajustes para habilitarla.", + "udapp.defaultTitle": "Lamentablemente no es posible crear una cuenta usando una billetera externa ({selectExEnv}).", + "udapp.text1": "Por favor proporcione una frase de contraseña para la creación de la cuenta", + "udapp.tooltipText1": "La lista de cuentas está vacía, por favor asegúrese de que el proveedor actual está conectado correctamente a remix", + "udapp.modalTitle1": "Frase de contraseña para firmar un mensaje", + "udapp.modalMessage1": "Introduce su frase de contraseña para esta cuenta para firmar el mensaje", + "udapp.copyAccount": "Copiar cuenta al portapapeles", + "udapp.signMsgUsingAccount": "Firmar un mensaje usando esta cuenta", + "udapp._comment_environment.tsx": "libs/remix-ui/run-tab/src/lib/components/environment.tsx", + "udapp.environment": "Entorno", + "udapp.environmentDocs": "Haga clic para ver la documentación sobre El Entorno", + "udapp.tooltipText2": "Abra chainlist.org y obtenga las especificaciones de conexión de la cadena con la que desea interactuar.", + "udapp.tooltipText3": "Haz clic para abrir un puente para convertir ETH de L1 mainnet a la moneda de red seleccionada.", + "udapp._comment_instanceContainerUI.tsx": "libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx", + "udapp.deployedContracts": "Contratos Publicados", + "udapp.deployAndRunClearInstances": "Limpiar lista de instancias y restablecer grabadora", + "udapp.deployAndRunNoInstanceText": "Actualmente no tiene instancias de contrato con las que interactuar.", + "udapp.tooltipText6": "Interfaces de usuario genéricas generadas automáticamente para la interacción con los contratos publicados", + "udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx", + "udapp.transactionsRecorded": "Transacciones grabadas", + "udapp.transactionsCountTooltip": "El número de transacciones grabadas", + "udapp.transactionSaveTooltip1": "No hay transacciones para guardar", + "udapp.transactionSaveTooltip2": "Guardar {count} transacción como archivo de escenario", + "udapp.transactionSaveTooltip3": "Guardar {count} transacciones como archivos de escenario", + "udapp.infoRecorderTooltip": "Guardar transacciones (contratos publicados y ejecuciones de funciones) y reproducirlas en otro entorno. Por ejemplo: las transacciones creadas en la MV de Remix pueden reproducirse en el Proveedor Inyectado.", + "udapp.livemodeRecorderTooltip": "Si los contratos se actualizan después de grabar las transacciones, marcando esta casilla ejecutará las transacciones grabadas con la última copia de los contratos compilados", + "udapp.livemodeRecorderLabel": "Ejecutar transacciones usando el último resultado de compilación", + "udapp.runRecorderTooltip": "Ejecutar transacción(es) desde el archivo de escenario actual", + "udapp.save": "Guardar", + "udapp.run": "Ejecutar", + "udapp._comment_contractGUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx", + "udapp.parameters": "Parámetros", + "udapp.copyParameters": "Copiar parámetros de entrada codificados al portapapeles", + "udapp.copyCalldata": "Copiar calldata al portapapeles", + "udapp.deployWithProxy": "Publicar con Proxy", + "udapp.upgradeWithProxy": "Actualizar con Proxy", + "udapp.getEncodedCallError": "no se pueden codificar argumentos vacíos", + "udapp.proxyAddressError1": "la dirección del proxy no puede estar vacía", + "udapp.proxyAddressError2": "no es una dirección de contrato válida", + "udapp.tooltipText11": "La dirección del Proxy no puede estar vacía", + "udapp.tooltipText12": "Entrada requerida", + "udapp.tooltipText13": "Publicado en {date}", + "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", + "udapp.tooltipText7": "Eliminar de la lista", + "udapp.tooltipText8": "Haga clic para ver los documentos sobre el uso de 'receive'/'fallback'", + "udapp.tooltipText9": "El datos de llamada a enviar a la función fallback del contrato.", + "udapp.tooltipText10": "Enviar datos al contrato.", + "udapp.balance": "Saldo", + "udapp.lowLevelInteractions": "Interacciones de bajo nivel", + "udapp.llIError1": "El valor a enviar debe ser un número", + "udapp.llIError2": "Para recibir la transferencia de Ether el contrato debe tener una función de 'receive' o 'fallback' payable", + "udapp.llIError3": "Los datos de llamada deben ser un valor hexadecimal válido con el tamaño de al menos un byte.", + "udapp.llIError4": "Los datos de llamada deben ser un valor hexadecimal válido.", + "udapp.llIError5": "La función 'fallback' no está definida", + "udapp.llIError6": "Las funciones 'receive' y 'fallback' no están definidas", + "udapp.llIError7": "Por favor defina una función 'Fallback' para enviar calldata y un 'Receice' o payable 'Fallback' para enviar ethers", + "udapp.copy": "Copiar", + "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", + "udapp.mainnetText1": "Estás a punto de crear una transacción en la Red {name}. Confirma los detalles para enviar la información a tu proveedor.", + "udapp.mainnetText2": "El proveedor para muchos usuarios es MetaMask. El proveedor le pedirá que firme la transacción antes de enviarla a la Red {name}.", + "udapp.amount": "Cantidad", + "udapp.gasEstimation": "Estimación de gas", + "udapp.maxPriorityFee": "Tarifa Máxima de Prioridad", + "udapp.maxFee": "Comisión máxima (no menos de la comisión base {baseFeePerGas} Gwei)", + "udapp.contractCreation": "Creación de Contrato", + "udapp.transactionFee": "La transacción no es válida. La comisión máxima no debe ser menor que la Comisión Base", + "udapp.title1": "Representa la parte de la comisión de tx que va al minero.", + "udapp.title2": "Representa la cantidad máxima de comisión que usted pagará por esta transacción. El mínimo debe estar establecido en la comisión base.", + "udapp.gasPrice": "Precio gas", + "udapp.gweiText": "visita {a} para obtener el precio actual del gas.", + "udapp.maxTransactionFee": "Comisión de transacción máxima", + "udapp.mainnetText3": "No volver a mostrar esta advertencia.", + "udapp._comment_run-tab.tsx": "libs/remix-ui/run-tab/src/lib/run-tab.tsx", + "udapp.gasEstimationPromptText": "La estimación del gas ha fallado con el siguiente mensaje (ver abajo). La ejecución de la transacción probablemente fallará. ¿Desea forzar el envío?", + "udapp._comment_custom-dropdown.tsx": "libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx", + "udapp.enterProxyAddress": "Introduzca la Dirección del Proxy", + "udapp._comment_provider": "apps/remix-ide/src/app/providers", + "udapp.customVmForkProviderText": "Por favor, proporcione información sobre el fork personalizado. Si la URL del nodo no se proporciona, la MV comenzará con un estado vacío.", + "udapp.nodeUrl": "URL del Nodo", + "udapp.blockNumber": "Número de bloque (o \"último\")", + "udapp.externalHttpProviderText1": "Nota: Para usar Geth & https://remix.ethereum.org, configúrelo para permitir solicitudes de Remix:(ver Documentación de Geth en el servidor rpc)", + "udapp.externalHttpProviderText2": "Para ejecutar Remix y un nodo de prueba Geth local, utilice este comando: (vea Documentación de Geth en el modo Desarrollador)", + "udapp.externalHttpProviderText3": "ALERTA: No es seguro usar la bandera --http.corsdomain con un comodín: --http.corsdomain *", + "udapp.externalHttpProviderText4": "Para más información en: Documentación de Remix en Provedor HTTP Externo", + "udapp.foundryProviderText1": "Nota: Para ejecutar Anvil en su sistema, ejecute:", + "udapp.foundryProviderText2": "Para más información, visite: Documentación de Foundry", + "udapp.ganacheProviderText1": "Nota: Para ejecutar Ganache en su sistema, ejecute:", + "udapp.ganacheProviderText2": "Para más información, visite: Documentación de Ganache", + "udapp.hardhatProviderText1": "Nota: Para ejecutar el nodo de red Hardhat en su sistema, vaya a la carpeta del proyecto hardhat y ejecute el comando:", + "udapp.hardhatProviderText2": "Para más información, visite: Documentación de Hardhat", + "udapp.viewSourceCode": "Ver código fuente" +} diff --git a/apps/remix-dapp/src/locales/fr/index.ts b/apps/remix-dapp/src/locales/fr/index.ts new file mode 100644 index 00000000000..2b7f6d597c0 --- /dev/null +++ b/apps/remix-dapp/src/locales/fr/index.ts @@ -0,0 +1,18 @@ +import enJson from '../en'; + +function readAndCombineJsonFiles() { + // @ts-expect-error + const dataContext = require.context('./', true, /\.json$/) + + let combinedData = {} + dataContext.keys().forEach((key) => { + const jsonData = dataContext(key) + combinedData = { ...combinedData, ...jsonData } + }) + + return combinedData +} + +// There may have some un-translated content. Always fill in the gaps with EN JSON. +// No need for a defaultMessage prop when render a FormattedMessage component. +export default Object.assign({}, enJson, readAndCombineJsonFiles()); diff --git a/apps/remix-dapp/src/locales/fr/terminal.json b/apps/remix-dapp/src/locales/fr/terminal.json new file mode 100644 index 00000000000..8183140a975 --- /dev/null +++ b/apps/remix-dapp/src/locales/fr/terminal.json @@ -0,0 +1,43 @@ +{ + "terminal.listen": "écouter toutes les transactions", + "terminal.listenTitle": "Si cette case est cochée, Remix écoutera toutes les transactions minées dans l'environnement actuel et pas seulement les transactions créées par vous", + "terminal.search": "Rechercher avec le hash de la transaction ou l'adrresse", + "terminal.used": "utilisé", + "terminal.debug": "Débogage", + "terminal.welcomeText1": "Bienvenue dans", + "terminal.welcomeText2": "Vos fichiers sont stockés dans", + "terminal.welcomeText3": "Vous pouvez utiliser ce terminal pour", + "terminal.welcomeText4": "Vérifiez les détails des transactions et commencez à déboguer", + "terminal.welcomeText5": "Exécuter des scripts JavaScript", + "terminal.welcomeText6": "Entrez un script directement dans l'interface en ligne de commande", + "terminal.welcomeText7": "Sélectionnez un fichier Javascript dans l'explorateur de fichiers, puis exécutez `remix.execute()` ou `remix.exeCurrent()` dans l'interface en ligne de commande", + "terminal.welcomeText8": "Faites un clic droit sur un fichier JavaScript dans l'explorateur de fichiers, puis cliquez sur `Run`", + "terminal.welcomeText9": "Les bibliothèques suivantes sont accessibles", + "terminal.welcomeText10": "Tapez le nom de la bibliothèque pour voir les commandes disponibles", + "terminal.text1": "Ce type de commande a été déprécié et ne fonctionne plus. Veuillez lancer remix.help() pour lister les commandes disponibles.", + "terminal.hideTerminal": "Masquer le terminal", + "terminal.showTerminal": "Afficher le terminal", + "terminal.clearConsole": "Effacer la console", + "terminal.pendingTransactions": "Transactions en cours", + "terminal.toasterMsg1": "aucun contenu à exécuter", + "terminal.toasterMsg2": "provider pour le chemin {fileName} introuvable", + "terminal.vmMode": "Mode VM", + "terminal.vmModeMsg": "Impossible de déboguer ce call. Les calls de débogage ne sont possibles qu'en mode VM de Remix.", + "terminal.ok": "Ok", + "terminal.cancel": "Annuler", + "terminal.callWarning": "(Les frais ne s'appliquent qu'en cas d'appel par contrat)", + "terminal.msg1": "Transaction minée mais exécution échouée", + "terminal.msg2": "Transaction minée et exécutée avec succès", + "terminal.msg3": "Statut non disponible pour le moment", + "terminal.status": "statut", + "terminal.transactionHash": "hash de transaction", + "terminal.blockHash": "Hash du block", + "terminal.blockNumber": "numéro du block", + "terminal.contractAddress": "Adresse du contrat", + "terminal.transactionCost": "Coût de la transaction", + "terminal.executionCost": "coût d'exécution", + "terminal.input": "entrée", + "terminal.decodedInput": "entrée décodée", + "terminal.decodedOutput": "sortie décodée", + "terminal.logs": "logs" +} diff --git a/apps/remix-dapp/src/locales/fr/udapp.json b/apps/remix-dapp/src/locales/fr/udapp.json new file mode 100644 index 00000000000..288e4b29dee --- /dev/null +++ b/apps/remix-dapp/src/locales/fr/udapp.json @@ -0,0 +1,141 @@ +{ + "udapp.displayName": "Déployer et exécuter des transactions", + "udapp._comment_gasPrice.tsx": "libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx", + "udapp.gasLimit": "Gas limit", + "udapp.tooltipText4": "La 'gas limit' par défaut est de 3M. Ajustez au besoin.", + "udapp._comment_value.tsx": "libs/remix-ui/run-tab/src/lib/components/value.tsx", + "udapp.value": "Valeur", + "udapp.tooltipText5": "Entrez un montant et choisissez son unité", + "udapp._comment_contractDropdownUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx", + "udapp.contract": "Contract", + "udapp.compiledBy": "compilé par {compilerName}", + "udapp.warningEvmVersion": "Veuillez vous assurer que le réseau actuel est compatible avec cette version evm : {evmVersion}. Sinon, tout déploiement échouera.", + "udapp.infoSyncCompiledContractTooltip": "Cliquez ici pour importer des contrats compilés à partir d'un framework externe. Cette action est activée lorsque Remix est connecté à un framework externe (hardhat, truffe, foundry) par remixd.", + "udapp.remixIpfsUdappTooltip": "La publication du code source et des métadonnées sur IPFS facilite la vérification du code source à l'aide de Sourcify et favorisera grandement l'adoption de contrats (audit, débogage, interactions, etc...)", + "udapp.deploy": "Déployer", + "udapp.publishTo": "Publier vers", + "udapp.atAddress": "At Address", + "udapp.atAddressOptionsTitle1": "adresse du contrat", + "udapp.atAddressOptionsTitle2": "Interagir avec le contrat déployé - nécessite que le fichier .abi ou le fichier .sol compilé soit sélectionné dans l'éditeur (avec la même configuration du compilateur)", + "udapp.atAddressOptionsTitle3": "Compiler un fichier *.sol ou sélectionner un fichier *.abi.", + "udapp.atAddressOptionsTitle4": "Pour interagir avec un contrat déployé, entrez son adresse et compilez sa source *.sol (avec les mêmes paramètres du compilateur) ou sélectionnez son fichier .abi dans l'éditeur. ", + "udapp.contractOptionsTitle1": "Sélectionnez et compilez le fichier *.sol pour déployer ou accéder à un contrat", + "udapp.contractOptionsTitle2": "Sélectionnez un contrat compilé à déployer ou à utiliser avec At Address.", + "udapp.contractOptionsTitle3": "Sélectionnez et compilez le fichier *.sol pour déployer ou accéder à un contrat.", + "udapp.contractOptionsTitle4": "Quand il y a un fichier .sol compilé, choisissez le contrat à déployer ou à utiliser avec At Address.", + "udapp.checkSumWarning": "Il semble que votre addresse de passe pas un verification checksummed. Une adresse checksummed est une adresse qui contient des lettres majuscules, comme indiqué dans {a}. Les adresses checksummed sont destinées à empêcher les utilisateurs d'envoyer des transactions à la mauvaise adresse.", + "udapp.isOverSizePromptEip170": "L'initialisation du smart-contrat indique qu'il est d'une taille supérieure à 24576 octets. Le déploiement échouera probablement si le réseau actuel a activé l'eip 170. Plus d'infos : {a}", + "udapp.isOverSizePromptEip3860": "L'initialisation du smart-contrat indique qu'il est d'une taille supérieure à 49152 octets. Le déploiement échouera probablement si le réseau actuel a activé l'eip 3860. Plus d'infos : {a}", + "udapp.thisContractMayBeAbstract": "Ce contrat peut être abstract, il ne pourra peut-être pas implémenter complétement les méthodes d'un parent abstract ou il ne pourra peut-être pas invoquer le constructeur d'un contrat hérité correctement.", + "udapp.noCompiledContracts": "Pas de contrats compilés", + "udapp.addressOfContract": "Adresse du contrat", + "udapp.loadContractFromAddress": "Charger le contrat à partir de l'adresse", + "udapp.ok": "Ok", + "udapp.alert": "Avertissement", + "udapp.proceed": "Continuer", + "udapp.cancel": "Annuler", + "udapp.abiFileSelected": "Fichier ABI sélectionné", + "udapp.evmVersion": "version de l'evm", + "udapp.addressNotValid": "Cette adresse n'est pas valide", + "udapp._comment_account.tsx": "libs/remix-ui/run-tab/src/lib/components/account.tsx", + "udapp.account": "Compte ", + "udapp.locales": "Langage", + "udapp.themes": "Thèmes", + "udapp.signAMessage": "Signer le message", + "udapp.enterAMessageToSign": "Entrez un message à signer", + "udapp.hash": "hash", + "udapp.signature": "signature", + "udapp.injectedTitle": "Malheureusement, il n'est pas possible de créer un compte en utilisant un provider injecté. Veuillez créer le compte directement à partir de votre provider (par exemple métamask ou autre du même type).", + "udapp.createNewAccount": "Créer un nouveau compte", + "udapp.web3Title": "La création d'un compte n'est possible qu'en mode personnel. Veuillez aller dans Paramètres pour l'activer.", + "udapp.defaultTitle": "Malheureusement, il n'est pas possible de créer un compte en utilisant un wallet externe ({selectExEnv}).", + "udapp.text1": "Veuillez fournir une Passphrase pour la création du compte", + "udapp.tooltipText1": "La liste des comptes est vide, assurez-vous que le provider actuel est bien connecté au remix", + "udapp.modalTitle1": "Passphrase pour signer un message", + "udapp.modalMessage1": "Entrez votre Passphrase pour ce compte pour signer le message", + "udapp.copyAccount": "Copier l'identifiant de compte dans le presse-papier", + "udapp.signMsgUsingAccount": "Signer un message en utilisant ce compte", + "udapp._comment_environment.tsx": "libs/remix-ui/run-tab/src/lib/components/environment.tsx", + "udapp.environment": "Environnement", + "udapp.environmentDocs": "Cliquer pour la documentation sur l'environnement", + "udapp.tooltipText2": "Ouvrez chainlist.org et obtenez les spécifications de connexion de la chaîne avec laquelle vous voulez interagir.", + "udapp.tooltipText3": "Cliquez pour ouvrir un pont de connexion pour convertir l'ETH principal L1 dans la devise réseau sélectionnée.", + "udapp._comment_instanceContainerUI.tsx": "libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx", + "udapp.deployedContracts": "Contrats déployés", + "udapp.deployAndRunClearInstances": "Effacer la liste des instances et réinitialiser l'enregistreur", + "udapp.deployAndRunNoInstanceText": "Actuellement, vous n'avez aucune instance de contrat avec laquelle interagir.", + "udapp.tooltipText6": "Interfaces utilisateur génériques générées automatiquement pour l'interaction avec les contrats déployés", + "udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx", + "udapp.transactionsRecorded": "Transactions enregistrées", + "udapp.transactionsCountTooltip": "Le nombre de transactions enregistrées", + "udapp.transactionSaveTooltip1": "Aucune transaction à enregistrer", + "udapp.transactionSaveTooltip2": "Enregistrer {count} transaction en tant que fichier de scénario", + "udapp.transactionSaveTooltip3": "Enregistrer {count} transactions en tant que fichier de scénario", + "udapp.infoRecorderTooltip": "Enregistrez les transactions (contrats déployés et exécutions de fonctions) et rejouez-les dans un autre environnement. Les transactions créées dans la VM de Remix peuvent être rejouées dans le provider injecté.", + "udapp.livemodeRecorderTooltip": "Si les contrats sont mis à jour après l'enregistrement des transactions, cochez cette case exécutera les transactions enregistrées avec la dernière copie des contrats compilés", + "udapp.livemodeRecorderLabel": "Exécuter les transactions en utilisant le dernier résultat de compilation", + "udapp.runRecorderTooltip": "Exécuter la(les) transaction(s) depuis le fichier de scénario actuel", + "udapp.save": "Sauvegarder", + "udapp.run": "Exécuter", + "udapp._comment_contractGUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx", + "udapp.parameters": "Paramètres", + "udapp.copyParameters": "Copier les paramètres d'entrée encodés dans le presse-papiers", + "udapp.copyCalldata": "Copier les calldata dans le presse-papiers", + "udapp.deployWithProxy": "Déployer avec Proxy", + "udapp.upgradeWithProxy": "Mettre à jour avec le Proxy", + "udapp.getEncodedCallError": "ne peut pas encoder d'arguments vides", + "udapp.proxyAddressError1": "l'adresse du proxy ne peut pas être vide", + "udapp.proxyAddressError2": "n'est pas une adresse de contrat valide", + "udapp.tooltipText11": "L'adresse du proxy ne peut pas être vide", + "udapp.tooltipText12": "Entrée nécessaire", + "udapp.tooltipText13": "Déployé {date}", + "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", + "udapp.tooltipText7": "Supprimer de la liste", + "udapp.tooltipText8": "Cliquez sur la documentation à propos de l'utilisation de 'receive'/'fallback'", + "udapp.tooltipText9": "Les Calldata à envoyer à la fonction fallback du contrat.", + "udapp.tooltipText10": "Envoyer des données au contrat.", + "udapp.balance": "Balance", + "udapp.lowLevelInteractions": "Interactions de bas niveau", + "udapp.llIError1": "La valeur à envoyer doit être un nombre", + "udapp.llIError2": "Afin de recevoir le transfert d'Ether, le contrat devrait avoir soit la fonction 'receive' soit la fonction 'fallback' payable", + "udapp.llIError3": "Les calldata doivent être une valeur hexadécimale valide avec une taille d'au moins un byte.", + "udapp.llIError4": "Les calldata doivent être une valeur hexadécimale valide.", + "udapp.llIError5": "La fonction 'Fallback' n'est pas définie", + "udapp.llIError6": "Les fonctions 'receive' et 'fallback' ne sont pas définies", + "udapp.llIError7": "Définissez s'il vous plaît une fonction 'Fallback' pour envoyer des calldata et soit une fonction 'Receive' ou une fonction 'Fallback' payable pour envoyer des ethers", + "udapp.copy": "Copier", + "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", + "udapp.mainnetText1": "Vous êtes sur le point de créer une transaction sur le réseau {name} . Confirmez les détails pour envoyer les informations à votre fournisseur.", + "udapp.mainnetText2": "Le provider pour de nombreux utilisateurs est MetaMask. Le provider vous demandera de signer la transaction avant qu'elle ne soit envoyée au réseau {name}.", + "udapp.amount": "Montant", + "udapp.gasEstimation": "Estimation du gaz", + "udapp.maxPriorityFee": "Max Priority fee", + "udapp.maxFee": "Max fee (pas moins que les base fee {baseFeePerGas} Gwei)", + "udapp.contractCreation": "Création du contrat", + "udapp.transactionFee": "La transaction n'est pas valide. Max fee ne devraient pas être inférieurs aux Base fee", + "udapp.title1": "Représente la partie du tarif Tx qui va au mineur.", + "udapp.title2": "Représente le montant maximal des fees que vous allez payer pour cette transaction. Le minimum doit être fixé à Base fee.", + "udapp.gasPrice": "Gas Price", + "udapp.gweiText": "visitez {a} pour plus d'informations sur le prix du gas.", + "udapp.maxTransactionFee": "Max transaction fee", + "udapp.mainnetText3": "Ne plus afficher cet avertissement.", + "udapp._comment_run-tab.tsx": "libs/remix-ui/run-tab/src/lib/run-tab.tsx", + "udapp.gasEstimationPromptText": "L'estimation du gaz a été erronée avec le message suivant (voir ci-dessous). L'exécution de la transaction va probablement échouer. Voulez-vous forcer l'envoi ?", + "udapp._comment_custom-dropdown.tsx": "libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx", + "udapp.enterProxyAddress": "Entrez l'adresse du proxy", + "udapp._comment_provider": "apps/remix-ide/src/app/providers", + "udapp.customVmForkProviderText": "Veuillez fournir des informations sur le fork personnalisé. Si l'URL du nœud n'est pas fournie, la VM commencera par un état vide.", + "udapp.nodeUrl": "URL du noeud", + "udapp.blockNumber": "Numéro de Block (ou \"latest\")", + "udapp.externalHttpProviderText1": "Remarque : Pour utiliser Geth & https://remix.ethereum.org, configurez-le pour autoriser les requêtes de Remix:(voir Geth Docs on rpc server)", + "udapp.externalHttpProviderText2": "Pour exécuter Remix & un nœud de test local Geth, utilisez cette commande : (voir Geth Docs on Dev mode)", + "udapp.externalHttpProviderText3": " WARNING : Il n'est pas sûr d'utiliser le flag --http.corsdomain avec une wildcard : --http.corsdomain *", + "udapp.externalHttpProviderText4": "Pour plus d'informations: Remix Docs on External HTTP Provider", + "udapp.foundryProviderText1": "Note : Pour lancer Anvil sur votre système, exécutez :", + "udapp.foundryProviderText2": "Pour plus d'informations, visitez : Foundry Documentation", + "udapp.ganacheProviderText1": "Note : Pour lancer Ganache sur votre système, exécutez :", + "udapp.ganacheProviderText2": "Pour plus d'informations, visitez : Ganache Documentation", + "udapp.hardhatProviderText1": "Note : Pour exécuter un noeud réseau Hardhat sur votre système, allez dans le dossier de projet hardhat et exécutez la commande :", + "udapp.hardhatProviderText2": "Pour plus d'informations, visitez : Hardhat Documentation", + "udapp.viewSourceCode": "Afficher le code source" +} diff --git a/apps/remix-dapp/src/locales/it/index.ts b/apps/remix-dapp/src/locales/it/index.ts new file mode 100644 index 00000000000..2b7f6d597c0 --- /dev/null +++ b/apps/remix-dapp/src/locales/it/index.ts @@ -0,0 +1,18 @@ +import enJson from '../en'; + +function readAndCombineJsonFiles() { + // @ts-expect-error + const dataContext = require.context('./', true, /\.json$/) + + let combinedData = {} + dataContext.keys().forEach((key) => { + const jsonData = dataContext(key) + combinedData = { ...combinedData, ...jsonData } + }) + + return combinedData +} + +// There may have some un-translated content. Always fill in the gaps with EN JSON. +// No need for a defaultMessage prop when render a FormattedMessage component. +export default Object.assign({}, enJson, readAndCombineJsonFiles()); diff --git a/apps/remix-dapp/src/locales/it/terminal.json b/apps/remix-dapp/src/locales/it/terminal.json new file mode 100644 index 00000000000..8bdc6c7dc80 --- /dev/null +++ b/apps/remix-dapp/src/locales/it/terminal.json @@ -0,0 +1,43 @@ +{ + "terminal.listen": "ascolta su tutte le transazioni", + "terminal.listenTitle": "Se è selezionato, Remix ascolterà tutte le transazioni generate nell'environment attuale e non solo quelle create da te", + "terminal.search": "Cercare con hash o indirizzo della transazione", + "terminal.used": "usato", + "terminal.debug": "Debug", + "terminal.welcomeText1": "Benvenuto a", + "terminal.welcomeText2": "I tuoi file sono memorizzati in", + "terminal.welcomeText3": "Puoi usare questo terminale per", + "terminal.welcomeText4": "Controllare i dettagli delle transazioni e avviare il debug", + "terminal.welcomeText5": "Esegui gli script JavaScript", + "terminal.welcomeText6": "Immettere uno script direttamente nell'interfaccia della riga di comando", + "terminal.welcomeText7": "Seleziona un file Javascript nel file explorer ed esegui `remix.execute()` o `remix.exeCurrent()` nell'interfaccia Command Line (Terminale)", + "terminal.welcomeText8": "Fare clic con il tasto destro su un file JavaScript nell'esplora file e quindi fare clic su `Run`", + "terminal.welcomeText9": "Le seguenti librerie sono accessibili", + "terminal.welcomeText10": "Digitare il nome della libreria per vedere i comandi disponibili", + "terminal.text1": "Questo tipo di comando è stato deprecato e non è più funzionante. Si prega di eseguire remix.help() per elencare i comandi disponibili.", + "terminal.hideTerminal": "Nascondi Terminale", + "terminal.showTerminal": "Mostra Terminale", + "terminal.clearConsole": "Pulisci console", + "terminal.pendingTransactions": "Transazioni in corso", + "terminal.toasterMsg1": "Nessun contenuto da eseguire", + "terminal.toasterMsg2": "provider del percorso {fileName} non trovato", + "terminal.vmMode": "Modalità VM", + "terminal.vmModeMsg": "Impossibile eseguire il debug di questa chiamata. Il debug delle chiamate è possibile solo in modalità VM Remix.", + "terminal.ok": "Ok", + "terminal.cancel": "Annulla", + "terminal.callWarning": "(Il costo si applica solo quando viene invocato da un contratto)", + "terminal.msg1": "Transazione minata ma esecuzione fallita", + "terminal.msg2": "Transazione minata ed esecuzione riuscita", + "terminal.msg3": "Status non disponibile al momento", + "terminal.status": "Status", + "terminal.transactionHash": "hash della transazione", + "terminal.blockHash": "hash del blocco", + "terminal.blockNumber": "numero del blocco", + "terminal.contractAddress": "indirizzo del contratto", + "terminal.transactionCost": "costo della transazione", + "terminal.executionCost": "costo di esecuzione", + "terminal.input": "input", + "terminal.decodedInput": "input decodificato", + "terminal.decodedOutput": "output decodificato", + "terminal.logs": "Log" +} diff --git a/apps/remix-dapp/src/locales/it/udapp.json b/apps/remix-dapp/src/locales/it/udapp.json new file mode 100644 index 00000000000..525e61cdd27 --- /dev/null +++ b/apps/remix-dapp/src/locales/it/udapp.json @@ -0,0 +1,141 @@ +{ + "udapp.displayName": "Distribuisci ed esegui transazioni", + "udapp._comment_gasPrice.tsx": "libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx", + "udapp.gasLimit": "Limite di gas", + "udapp.tooltipText4": "Il limite di gas predefinito è 3M. Regolare come necessario.", + "udapp._comment_value.tsx": "libs/remix-ui/run-tab/src/lib/components/value.tsx", + "udapp.value": "Valore", + "udapp.tooltipText5": "Inserisci un importo e scegli la sua unità", + "udapp._comment_contractDropdownUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx", + "udapp.contract": "Contratto", + "udapp.compiledBy": "compilato da {compilerName}", + "udapp.warningEvmVersion": "Assicurarsi che la rete corrente sia compatibile con questa versione della evm: {evmVersion}. In caso contrario, la distribuzione non andrà a buon fine.", + "udapp.infoSyncCompiledContractTooltip": "Fare clic qui per importare contratti compilati da un framework esterno. Questa azione è abilitata quando Remix è collegato a un framework esterno (hardhat, truffle, foundry) attraverso remixd.", + "udapp.remixIpfsUdappTooltip": "La pubblicazione del codice sorgente e dei metadati su IPFS facilita la verifica del codice sorgente con Sourcify e favorisce notevolmente l'adozione del contratto (auditing, debug, invocarlo, ecc.)", + "udapp.deploy": "Distribuisci", + "udapp.publishTo": "Pubblica su", + "udapp.atAddress": "All'Indirizzo", + "udapp.atAddressOptionsTitle1": "indirizzo del contratto", + "udapp.atAddressOptionsTitle2": "Interagire con il contratto distribuito - richiede che il file .abi o il file .sol compilato sia selezionato nell'editor (con la stessa configurazione del compilatore)", + "udapp.atAddressOptionsTitle3": "Compila un file *.sol o seleziona un file *.abi.", + "udapp.atAddressOptionsTitle4": "Per interagire con un contratto distribuito, è necessario inserire il suo indirizzo e compilare il file sorgente *.sol (con le stesse impostazioni del compilatore) oppure selezionare il file .abi nell'editor. ", + "udapp.contractOptionsTitle1": "Si prega di compilare *.sol file per distribuire o accedere a un contratto", + "udapp.contractOptionsTitle2": "Selezionare un contratto compilato da distribuire o da utilizzare con At Address.", + "udapp.contractOptionsTitle3": "Selezionare e compilare il file *.sol per distribuire o accedere a un contratto.", + "udapp.contractOptionsTitle4": "Quando c'è un file .sol compilato, scegliere il contratto da distribuire o da usare con At Address.", + "udapp.checkSumWarning": "Sembra che non si stia utilizzando un indirizzo con checksum. Un indirizzo con checksum è un indirizzo che contiene lettere maiuscole, come specificato in {a}. Gli indirizzi con checksum sono pensati per evitare che gli utenti inviino transazioni all'indirizzo sbagliato.", + "udapp.isOverSizePromptEip170": "L'inizializzazione della creazione del contratto restituisce dati di lunghezza superiore a 24576 byte. La distribuzione probabilmente fallirà se la rete corrente ha attivato l'eip 170. Ulteriori informazioni: {a}", + "udapp.isOverSizePromptEip3860": "Il codice di avvio della creazione del contratto supera la dimensione massima consentita di 49152 byte. È probabile che la distribuzione fallisca se la rete corrente ha attivato l'eip 3860. Ulteriori informazioni: {a}", + "udapp.thisContractMayBeAbstract": "Questo contratto potrebbe essere astratto, potrebbe non implementare completamente i metodi di un contratto genitore astratto o potrebbe non invocare correttamente il costruttore di un contratto ereditato.", + "udapp.noCompiledContracts": "Nessun contratto compilato", + "udapp.addressOfContract": "Indirizzo del contratto", + "udapp.loadContractFromAddress": "Caricare il contratto dall'indirizzo", + "udapp.ok": "OK", + "udapp.alert": "Avviso", + "udapp.proceed": "Procedi", + "udapp.cancel": "Annulla", + "udapp.abiFileSelected": "File ABI selezionato", + "udapp.evmVersion": "versione EVM", + "udapp.addressNotValid": "L'indirizzo non è valido.", + "udapp._comment_account.tsx": "libs/remix-ui/run-tab/src/lib/components/account.tsx", + "udapp.account": "Account", + "udapp.locales": "Lingua", + "udapp.themes": "Temi", + "udapp.signAMessage": "Firma un messaggio", + "udapp.enterAMessageToSign": "Inserire un messaggio da firmare", + "udapp.hash": "hash", + "udapp.signature": "firma", + "udapp.injectedTitle": "Purtroppo non è possibile creare un account utilizzando un provider iniettato. Per favore crea l'account direttamente dal tuo provider (cioè metamask o altro dello stesso tipo).", + "udapp.createNewAccount": "Crea un nuovo account", + "udapp.web3Title": "La creazione di un account è possibile solo in modalità personale. Vai su Impostazioni per abilitarlo.", + "udapp.defaultTitle": "Purtroppo non è possibile creare un account utilizzando un portafoglio esterno ({selectExEnv}).", + "udapp.text1": "Fornisci una frase segreta per la creazione dell'account", + "udapp.tooltipText1": "L'elenco degli account è vuoto, assicurati che il provider corrente sia collegato correttamente a Remix", + "udapp.modalTitle1": "Passphrase per firmare un messaggio", + "udapp.modalMessage1": "Inserisci la tua frase segreta per questo account per firmare il messaggio", + "udapp.copyAccount": "Copia account negli appunti", + "udapp.signMsgUsingAccount": "Firma un messaggio utilizzando questo account", + "udapp._comment_environment.tsx": "libs/remix-ui/run-tab/src/lib/components/environment.tsx", + "udapp.environment": "Ambiente", + "udapp.environmentDocs": "Fare clic per i documenti sull'Ambiente", + "udapp.tooltipText2": "Aprire chainlist.org e ottenere le specifiche di connessione della chain con cui si desidera interagire.", + "udapp.tooltipText3": "Fare clic per aprire un bridge per convertire ETH mainnet L1 nella valuta di rete selezionata.", + "udapp._comment_instanceContainerUI.tsx": "libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx", + "udapp.deployedContracts": "Contratti Distribuiti", + "udapp.deployAndRunClearInstances": "Cancellare l'elenco delle istanze e ripristinare il registratore", + "udapp.deployAndRunNoInstanceText": "Attualmente non si dispone di istanze contrattuali con cui interagire.", + "udapp.tooltipText6": "Interfacce utente generiche generate automaticamente per interagire con gli smart contratti deploiati", + "udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx", + "udapp.transactionsRecorded": "Transazioni registrate", + "udapp.transactionsCountTooltip": "Il numero di transazioni registrate", + "udapp.transactionSaveTooltip1": "Nessuna transazione da salvare", + "udapp.transactionSaveTooltip2": "Salvare la transazione {count} come file di scenario", + "udapp.transactionSaveTooltip3": "Salvare le transazioni {count} come file di scenario", + "udapp.infoRecorderTooltip": "Salvare le transazioni (contratti distribuiti ed esecuzioni di funzioni) e riprodurle in un altro ambiente, ad esempio le transazioni create nella macchina virtuale Remix possono essere riprodotte in Injected Provider.", + "udapp.livemodeRecorderTooltip": "Se i contratti vengono aggiornati dopo la registrazione delle transazioni, selezionando questa casella si eseguono le transazioni registrate con l'ultima copia dei contratti compilati", + "udapp.livemodeRecorderLabel": "Eseguire le transazioni utilizzando l'ultimo risultato della compilazione", + "udapp.runRecorderTooltip": "Eseguire una o più transazioni dal file di scenario attuale", + "udapp.save": "Salva", + "udapp.run": "Esegui", + "udapp._comment_contractGUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx", + "udapp.parameters": "Parametri", + "udapp.copyParameters": "Copiare i parametri di ingresso codificati negli appunti", + "udapp.copyCalldata": "Copiare calldata negli appunti", + "udapp.deployWithProxy": "Distribuisci con Proxy", + "udapp.upgradeWithProxy": "Aggiorna con Proxy", + "udapp.getEncodedCallError": "non è possibile codificare argomenti vuoti", + "udapp.proxyAddressError1": "l'indirizzo proxy non può essere vuoto", + "udapp.proxyAddressError2": "indirizzo di contratto non valido", + "udapp.tooltipText11": "L'indirizzo proxy non può essere vuoto", + "udapp.tooltipText12": "Input richiesto", + "udapp.tooltipText13": "Deploiato", + "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", + "udapp.tooltipText7": "Rimuovi dalla lista", + "udapp.tooltipText8": "Fare clic per i documenti sull'uso di 'receive'/'fallback'", + "udapp.tooltipText9": "Il Calldata per inviare alla funzione di fallback del contratto.", + "udapp.tooltipText10": "Invia dati al contratto.", + "udapp.balance": "Saldo", + "udapp.lowLevelInteractions": "Interazioni di livello basso", + "udapp.llIError1": "Il valore da inviare deve essere un numero", + "udapp.llIError2": "Per ricevere il trasferimento di Ether il contratto dovrebbe avere una funzione di 'receive' o di 'fallback' da pagare", + "udapp.llIError3": "Calldata deve essere un valore esadecimale valido con dimensioni di almeno un byte.", + "udapp.llIError4": "Calldata deve essere un valore esadecimale valido.", + "udapp.llIError5": "La funzione 'Fallback' non è definita", + "udapp.llIError6": "Le funzioni 'receive' e 'fallback' non sono definite", + "udapp.llIError7": "Definisci una funzione 'Fallback' per inviare Calldata e una 'Receive' o 'Fallback' payable per inviare ETH", + "udapp.copy": "Copia", + "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", + "udapp.mainnetText1": "Stai per creare una transazione su {name} Network. Conferma i dettagli per inviare le informazioni al tuo provider.", + "udapp.mainnetText2": "Il provider per molti utenti è MetaMask. Il provider ti chiederà di firmare la transazione prima che venga inviata alla rete {name}.", + "udapp.amount": "Importo", + "udapp.gasEstimation": "Stima del gas", + "udapp.maxPriorityFee": "Tariffa massima prioritaria", + "udapp.maxFee": "Commissione massima (Non meno della commissione base {baseFeePerGas} Gwei).", + "udapp.contractCreation": "Creazione Contratto", + "udapp.transactionFee": "Transazione non valida. La commissione massima non dovrebbe essere inferiore alla commissione base", + "udapp.title1": "Rappresenta la parte della commissione della transazione che va al minatore.", + "udapp.title2": "Rappresenta l'importo massimo della commissione che pagherai per questa transazione. Il minimo deve essere impostato su Base Fee.", + "udapp.gasPrice": "Prezzo del gas", + "udapp.gweiText": "visita {a} per informazioni sul prezzo corrente del gas.", + "udapp.maxTransactionFee": "Tariffa massima transazione", + "udapp.mainnetText3": "Non mostrare più questo avviso", + "udapp._comment_run-tab.tsx": "libs/remix-ui/run-tab/src/lib/run-tab.tsx", + "udapp.gasEstimationPromptText": "Stima del gas errata con il seguente messaggio (vedi sotto). Probabilmente l'esecuzione della transazione fallirà. Vuoi forzare l'invio?", + "udapp._comment_custom-dropdown.tsx": "libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx", + "udapp.enterProxyAddress": "Inserisci Indirizzo Proxy", + "udapp._comment_provider": "apps/remix-ide/src/app/providers", + "udapp.customVmForkProviderText": "Fornisci informazioni sul fork personalizzato. Se l'URL del nodo non è fornito, la VM inizierà con uno stato vuoto.", + "udapp.nodeUrl": "Url del nodo", + "udapp.blockNumber": "Numero blocco (o \"latest\" - \"ultimo\")", + "udapp.externalHttpProviderText1": "Nota: Per utilizzare Geth & https://remix.ethereum.org, configuralo per consentire le richieste da Remix:(vedi Geth Docs sul server rpc)", + "udapp.externalHttpProviderText2": "Per eseguire Remix & un nodo di prova Geth locale, usa questo comando: (vedi Geth Docs on Dev mode)", + "udapp.externalHttpProviderText3": "ATTENZIONE: Non è sicuro usare il flag --http.corsdomain con un wildcard: --http.corsdomain *", + "udapp.externalHttpProviderText4": "Per ulteriori informazioni: Remix Docs on External HTTP Provider", + "udapp.foundryProviderText1": "Nota: Per eseguire Anvil sul sistema, eseguire:", + "udapp.foundryProviderText2": "Per ulteriori informazioni, visita: Foundry Documentation", + "udapp.ganacheProviderText1": "Nota: Per eseguire Ganache sul sistema, eseguire:", + "udapp.ganacheProviderText2": "Per ulteriori informazioni, visita: Ganache Documentation", + "udapp.hardhatProviderText1": "Nota: Per eseguire il nodo di rete Hardhat sul sistema, vai alla cartella del progetto hardhat ed esegui il comando:", + "udapp.hardhatProviderText2": "Per ulteriori informazioni, visita: Hardhat Documentation", + "udapp.viewSourceCode": "Visualizza il codice sorgente" +} diff --git a/apps/remix-dapp/src/locales/zh/index.ts b/apps/remix-dapp/src/locales/zh/index.ts new file mode 100644 index 00000000000..2b7f6d597c0 --- /dev/null +++ b/apps/remix-dapp/src/locales/zh/index.ts @@ -0,0 +1,18 @@ +import enJson from '../en'; + +function readAndCombineJsonFiles() { + // @ts-expect-error + const dataContext = require.context('./', true, /\.json$/) + + let combinedData = {} + dataContext.keys().forEach((key) => { + const jsonData = dataContext(key) + combinedData = { ...combinedData, ...jsonData } + }) + + return combinedData +} + +// There may have some un-translated content. Always fill in the gaps with EN JSON. +// No need for a defaultMessage prop when render a FormattedMessage component. +export default Object.assign({}, enJson, readAndCombineJsonFiles()); diff --git a/apps/remix-dapp/src/locales/zh/terminal.json b/apps/remix-dapp/src/locales/zh/terminal.json new file mode 100644 index 00000000000..40e6d3791cd --- /dev/null +++ b/apps/remix-dapp/src/locales/zh/terminal.json @@ -0,0 +1,43 @@ +{ + "terminal.listen": "监听所有交易", + "terminal.listenTitle": "如果选中,Remix 将监听在当前环境中挖掘到的所有交易,而不仅仅是您创建的交易", + "terminal.search": "按交易哈希或地址搜索", + "terminal.used": "已使用", + "terminal.debug": "调试", + "terminal.welcomeText1": "欢迎使用", + "terminal.welcomeText2": "您的文件储存在", + "terminal.welcomeText3": "您可使用此终端", + "terminal.welcomeText4": "查看交易详情并启动调试", + "terminal.welcomeText5": "执行 JavaScript 脚本", + "terminal.welcomeText6": "直接在命令行界面输入脚本", + "terminal.welcomeText7": "在文件浏览器中选择一个 Javascript 文件,然后在命令行界面运行 `remix.execute()` 或 `remix.exeCurrent()` ", + "terminal.welcomeText8": "在文件浏览器中右键点击一个 JavaScript 文件,然后点击 `Run`", + "terminal.welcomeText9": "可以访问以下库", + "terminal.welcomeText10": "输入库名查看可用的指令", + "terminal.text1": "这类命令已被弃用,不再起作用。请运行 remix.help() 列出可用命令。", + "terminal.hideTerminal": "隐藏终端", + "terminal.showTerminal": "展示终端", + "terminal.clearConsole": "清空控制台", + "terminal.pendingTransactions": "待处理交易", + "terminal.toasterMsg1": "没有要执行的内容", + "terminal.toasterMsg2": "找不到路径 {fileName} 的 provider", + "terminal.vmMode": "VM 模式", + "terminal.vmModeMsg": "无法调试此调用。调试调用只能在 Remix VM 模式下进行。", + "terminal.ok": "确认", + "terminal.cancel": "取消", + "terminal.callWarning": "(当被一个合约调用是才需要费用)", + "terminal.msg1": "交易已打包但执行失败", + "terminal.msg2": "交易已打包且执行成功", + "terminal.msg3": "暂无状态信息", + "terminal.status": "状态", + "terminal.transactionHash": "交易哈希", + "terminal.blockHash": "区块哈希", + "terminal.blockNumber": "区块号", + "terminal.contractAddress": "合约地址", + "terminal.transactionCost": "交易成本", + "terminal.executionCost": "执行成本", + "terminal.input": "输入", + "terminal.decodedInput": "解码输入", + "terminal.decodedOutput": "解码输出", + "terminal.logs": "日志" +} diff --git a/apps/remix-dapp/src/locales/zh/udapp.json b/apps/remix-dapp/src/locales/zh/udapp.json new file mode 100644 index 00000000000..35dad029bb1 --- /dev/null +++ b/apps/remix-dapp/src/locales/zh/udapp.json @@ -0,0 +1,141 @@ +{ + "udapp.displayName": "部署 & 发交易", + "udapp._comment_gasPrice.tsx": "libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx", + "udapp.gasLimit": "Gas 上限", + "udapp.tooltipText4": "默认 Gas 限制为 3M。根据需要进行调整。", + "udapp._comment_value.tsx": "libs/remix-ui/run-tab/src/lib/components/value.tsx", + "udapp.value": "以太币数量", + "udapp.tooltipText5": "输入金额并选择单位", + "udapp._comment_contractDropdownUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx", + "udapp.contract": "合约", + "udapp.compiledBy": "由 {compilerName} 编译", + "udapp.warningEvmVersion": "请确保当前网络与此 evm 版本兼容:{evmVersion}。否则任何部署都会失败。", + "udapp.infoSyncCompiledContractTooltip": "单击此处导入从外部框架编译的合约。{br}当 Remix 通过 remixd 连接到外部{br}框架 (hardhat、truffle、foundry) 时启用此操作。", + "udapp.remixIpfsUdappTooltip": "将源代码和元数据发布到 IPFS 有助于{br}使用 Sourcify 验证源代码,并将极大地促进{br}合约采用(审计、调试、调用等)", + "udapp.deploy": "部署", + "udapp.publishTo": "发布到", + "udapp.atAddress": "At Address", + "udapp.atAddressOptionsTitle1": "合约地址", + "udapp.atAddressOptionsTitle2": "与已部署的合约交互 - 需要在编辑器中选择 .abi 文件或{br}编译的 .sol 文件{br}(具有相同的编译器配置)", + "udapp.atAddressOptionsTitle3": "编译一个 *.sol 文件或选中一个 *.abi 文件。", + "udapp.atAddressOptionsTitle4": "要与已部署的合约进行交互,{br} 输入其地址并编译其源 *.sol 文件{br}(使用相同的编译器设置)或在编辑器中选择其 .abi 文件。", + "udapp.contractOptionsTitle1": "请编译 *.sol 文件以部署或访问合约", + "udapp.contractOptionsTitle2": "选择要部署或与 At Address 一起使用的已编译合约。", + "udapp.contractOptionsTitle3": "选择并编译 *.sol 文件以部署或访问合约。", + "udapp.contractOptionsTitle4": "当有编译的 .sol 文件时,选择 {br} 合约进行部署或与 AtAddress 一起使用。", + "udapp.checkSumWarning": "您似乎没有使用 checksumed address 。{br} checksumed address 是包含大写字母的地址,如 {a} 中所指定。{br} checksumed address 旨在帮助防止用户将交易发送到错误地址。", + "udapp.isOverSizePromptEip170": "合约创建初始化返回长度超过24576字节的数据。部署可能会失败。 {br}更多信息:{a}", + "udapp.isOverSizePromptEip3860": "合约创建初始化代码超出了允许的最大代码大小 49152 字节。如果当前网络已激活 eip 3860,则部署可能会失败。更多信息:{a}", + "udapp.thisContractMayBeAbstract": "这个合约可能是抽象的,它可能没有完全实现抽象父类的方法,或者它可能没有正确调用继承合约的构造函数。", + "udapp.noCompiledContracts": "没有已编译的合约", + "udapp.addressOfContract": "合约地址", + "udapp.loadContractFromAddress": "加载此地址的合约", + "udapp.ok": "确认", + "udapp.alert": "警告", + "udapp.proceed": "继续", + "udapp.cancel": "取消", + "udapp.abiFileSelected": "已选择 ABI 文件", + "udapp.evmVersion": "evm 版本", + "udapp.addressNotValid": "该地址无效", + "udapp._comment_account.tsx": "libs/remix-ui/run-tab/src/lib/components/account.tsx", + "udapp.account": "账户", + "udapp.locales": "语言", + "udapp.themes": "主题", + "udapp.signAMessage": "给一个消息签名", + "udapp.enterAMessageToSign": "输入一个需要签名的消息", + "udapp.hash": "哈希", + "udapp.signature": "签名", + "udapp.injectedTitle": "很遗憾,使用 injected provider 无法创建账户。请直接在 injected provider(如 metamask 或其他同类型程序)中创建账户。", + "udapp.createNewAccount": "创建一个新账户", + "udapp.web3Title": "只有在个人模式下才能创建账户。请前往 \"设置\" 启用。", + "udapp.defaultTitle": "很遗憾,无法使用外部钱包 ({selectExEnv}) 创建帐户。", + "udapp.text1": "请提供用于创建帐户的密码", + "udapp.tooltipText1": "帐户列表为空,请确保当前 provider 已正确连接到 remix", + "udapp.modalTitle1": "用于签署消息的密码", + "udapp.modalMessage1": "输入此帐户的密码以签署消息", + "udapp.copyAccount": "将帐户复制到剪贴板", + "udapp.signMsgUsingAccount": "使用此帐户签署消息", + "udapp._comment_environment.tsx": "libs/remix-ui/run-tab/src/lib/components/environment.tsx", + "udapp.environment": "环境", + "udapp.environmentDocs": "点击查看环境文档", + "udapp.tooltipText2": "打开 chainlist.org 并获取您想要交互的链的连接规范。", + "udapp.tooltipText3": "单击可打开用于将 L1 主网 ETH 转换为所选网络货币的桥接器。", + "udapp._comment_instanceContainerUI.tsx": "libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx", + "udapp.deployedContracts": "已部署的合约", + "udapp.deployAndRunClearInstances": "清空合约实例并重置交易记录", + "udapp.deployAndRunNoInstanceText": "当前您没有可交互的合约实例.", + "udapp.tooltipText6": "自动生成的通用用户界面,用于与已部署的合约进行交互", + "udapp._comment_recorderCardUI.tsx": "libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx", + "udapp.transactionsRecorded": "已记录的交易", + "udapp.transactionsCountTooltip": "已记录的交易数", + "udapp.transactionSaveTooltip1": "没有可保存的交易", + "udapp.transactionSaveTooltip2": "将 {count} 笔交易保存到一个场景文件", + "udapp.transactionSaveTooltip3": "将 {count} 笔交易保存到一个场景文件", + "udapp.infoRecorderTooltip": "保存交易 (合约部署和方法执行) {br}然后在另一个环境中回放,比如在 Remix VM {br}创建的交易可以在 Injected Provider 中回放。", + "udapp.livemodeRecorderTooltip": "如果记录交易后合约有更新,{br}选中这个复选框就会以最新的{br}合约编译结果执行已记录的交易", + "udapp.livemodeRecorderLabel": "用最新的编译结果执行交易", + "udapp.runRecorderTooltip": "从当前场景文件中执行交易", + "udapp.save": "保存", + "udapp.run": "执行", + "udapp._comment_contractGUI.tsx": "libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx", + "udapp.parameters": "参数", + "udapp.copyParameters": "复制已编码的输入参数到粘贴板", + "udapp.copyCalldata": "复制 calldata 到粘贴板", + "udapp.deployWithProxy": "使用代理部署", + "udapp.upgradeWithProxy": "使用代理升级", + "udapp.getEncodedCallError": "无法对空参数进行编码", + "udapp.proxyAddressError1": "代理地址不能为空", + "udapp.proxyAddressError2": "不是有效的合约地址", + "udapp.tooltipText11": "代理地址不能为空", + "udapp.tooltipText12": "必填", + "udapp.tooltipText13": "已部署 {date}", + "udapp._comment_universalDappUI.tsx": "libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx", + "udapp.tooltipText7": "从列表中删除", + "udapp.tooltipText8": "点击查看有关使用 'receive'/'fallback' 的文档", + "udapp.tooltipText9": "发送到合约 fallback 函数的 Calldata。", + "udapp.tooltipText10": "将数据发送到合约。", + "udapp.balance": "余额", + "udapp.lowLevelInteractions": "低级交互", + "udapp.llIError1": "要发送的值应该是一个数字", + "udapp.llIError2": "为了接收以太币传输,合约应该具有 'receive' 或可支付的 'fallback' 功能", + "udapp.llIError3": "calldata 应是有效的十六进制值,大小至少为 1 个字节。", + "udapp.llIError4": "calldata 应该是有效的十六进制值。", + "udapp.llIError5": "'Fallback' 函数未定义", + "udapp.llIError6": "'receive' 和 'fallback' 函数都未定义", + "udapp.llIError7": "定义一个 'Fallback' 函数来发送 calldata ,并且定义一个 'Receive' 或 可支付的 'Fallback' 来发送以太币", + "udapp.copy": "复制", + "udapp._comment_mainnet.tsx": "libs/remix-ui/run-tab/src/lib/components/mainnet.tsx", + "udapp.mainnetText1": "您即将在 {name} 网络上创建一笔交易。确认详细信息,将信息发送给您的 provider 。", + "udapp.mainnetText2": "许多用户的 provider 是 MetaMask。在将交易发送到 {name} 网络之前,provider 会要求您签署交易。", + "udapp.amount": "金额", + "udapp.gasEstimation": "Gas 预算", + "udapp.maxPriorityFee": "最高优先权费用", + "udapp.maxFee": "最高费用(不低于基本费用{baseFeePerGas} Gwei)", + "udapp.contractCreation": "合约创建", + "udapp.transactionFee": "交易无效。最高费用不应低于基本费用", + "udapp.title1": "代表支付给矿工的交易费用部分。", + "udapp.title2": "代表您为此交易支付的最高费用金额。最低费用需要设置为基本费用。", + "udapp.gasPrice": "Gas 价格", + "udapp.gweiText": "请访问 {a} 以获取当前的 Gas 价格信息。", + "udapp.maxTransactionFee": "最高交易费用", + "udapp.mainnetText3": "不要再显示此警告。", + "udapp._comment_run-tab.tsx": "/libs/remix-ui/run-tab/src/lib/run-tab.tsx", + "udapp.gasEstimationPromptText": "Gas 预算错误并显示以下消息(见下文)。交易执行可能会失败。您想强制发送吗?", + "udapp._comment_custom-dropdown.tsx": "libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx", + "udapp.enterProxyAddress": "输入代理地址", + "udapp._comment_provider": "apps/remix-ide/src/app/providers", + "udapp.customVmForkProviderText": "请提供有关 custom fork 的信息。如果未提供节点 URL,VM 将以空状态启动。", + "udapp.nodeUrl": "节点 URL", + "udapp.blockNumber": "块号 (默认 \"latest\")", + "udapp.externalHttpProviderText1": "注意: 要用 Geth & https://remix.ethereum.org, 配置使它允许来自 Remix 的请求:(看 Geth rpc 服务的文档)", + "udapp.externalHttpProviderText2": "要运行 Remix & 一个本地的 Geth 测试节点, 使用这个命令: (看 Geth 开发模式文档)", + "udapp.externalHttpProviderText3": "警告: --http.corsdomain 使用通配符很不安全: --http.corsdomain *", + "udapp.externalHttpProviderText4": "更多信息: Remix External HTTP Provider 文档", + "udapp.foundryProviderText1": "注意: 要在您的系统里运行 Anvil, 运行:", + "udapp.foundryProviderText2": "更多信息, 访问: Foundry 文档", + "udapp.ganacheProviderText1": "注意: 要在您的系统里运行 Ganache, 运行:", + "udapp.ganacheProviderText2": "更多信息, 访问: Ganache 文档", + "udapp.hardhatProviderText1": "注意: 要在您的系统里运行 Hardhat 网络节点, 进到 hardhat 项目目录并且运行命令:", + "udapp.hardhatProviderText2": "更多信息, 访问: Hardhat 文档", + "udapp.viewSourceCode": "查看源码" +} diff --git a/apps/remix-dapp/src/main.tsx b/apps/remix-dapp/src/main.tsx new file mode 100644 index 00000000000..599c94b683a --- /dev/null +++ b/apps/remix-dapp/src/main.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; + +const root = ReactDOM.createRoot( + document.getElementById('root') as HTMLElement +); +root.render(); diff --git a/apps/remix-dapp/src/pages/Home/index.tsx b/apps/remix-dapp/src/pages/Home/index.tsx new file mode 100644 index 00000000000..61b53be5b95 --- /dev/null +++ b/apps/remix-dapp/src/pages/Home/index.tsx @@ -0,0 +1,124 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { UniversalDappUI } from '../../components/UniversalDappUI'; +import { SettingsUI } from '../../components/SettingsUI'; +import RemixUiTerminal from '../../components/UiTerminal'; +import DragBar from '../../components/DragBar'; +import DappTop from '../../components/DappTop'; +import { AppContext } from '../../contexts'; +import { initInstance } from '../../actions'; +import { isMobile } from '../../utils/tools'; +import TxList from '../../components/UiTerminal/TxList'; + +const HomePage: React.FC = () => { + const { + appState: { terminal, instance }, + } = useContext(AppContext); + const [active, setActive] = useState('functions'); + const { height } = terminal; + useEffect(() => { + initInstance(); + }, []); + + return isMobile() ? ( +
+
+ + +
+ {!instance.noTerminal && ( +
+ +
+ )} +
+ +
+
    +
  • { + setActive('functions'); + }} + > + + Functions + +
  • + {!instance.noTerminal && ( +
  • { + setActive('transactions'); + }} + > + + Transactions + +
  • + )} +
  • { + setActive('settings'); + }} + > + + Settings + +
  • +
+
+ ) : ( +
+
+
+
+
+ +
+ +
+ +
+
+ +
+
+ {!instance.noTerminal && ( + <> + + + + )} +
+ ); +}; + +export default HomePage; diff --git a/apps/remix-dapp/src/reducers/state.ts b/apps/remix-dapp/src/reducers/state.ts new file mode 100644 index 00000000000..eee54d6b510 --- /dev/null +++ b/apps/remix-dapp/src/reducers/state.ts @@ -0,0 +1,50 @@ +export const appInitialState: any = { + instance: { + name: '', + address: '', + balance: 0, + network: '', + decodedResponse: {}, + abi: [], + solcVersion: {}, + }, + settings: { + sendValue: '0', + sendUnit: 'wei', + gasLimit: 3000000, + networkName: 'Goerli', + loadedAccounts: {}, + isRequesting: false, + isSuccessful: false, + error: null, + selectedAccount: '', + selectedLocaleCode: 'en', + provider: window.ethereum ? 'metamask' : 'walletconnect', + }, + terminal: { journalBlocks: [], hidden: false, height: 250 }, +}; + +export const appReducer = (state = appInitialState, action: any): any => { + switch (action.type) { + case 'SET_INSTANCE': + return { + ...state, + instance: { ...state.instance, ...action.payload }, + }; + + case 'SET_SETTINGS': + return { + ...state, + settings: { ...state.settings, ...action.payload }, + }; + + case 'SET_TERMINAL': + return { + ...state, + terminal: { ...state.terminal, ...action.payload }, + }; + + default: + throw new Error(); + } +}; diff --git a/apps/remix-dapp/src/router.tsx b/apps/remix-dapp/src/router.tsx new file mode 100644 index 00000000000..370bfe4fc84 --- /dev/null +++ b/apps/remix-dapp/src/router.tsx @@ -0,0 +1,11 @@ +import { createBrowserRouter } from 'react-router-dom'; +import HomePage from './pages/Home'; + +const router = createBrowserRouter([ + { + path: '/', + element: , + }, +]); + +export default router; diff --git a/apps/remix-dapp/src/types.ts b/apps/remix-dapp/src/types.ts new file mode 100644 index 00000000000..3e1c2edbc65 --- /dev/null +++ b/apps/remix-dapp/src/types.ts @@ -0,0 +1,13 @@ +export interface RequestArguments { + readonly method: string; + readonly params?: readonly unknown[] | object; + readonly id?: string; +} + +export type Chain = { + chainId: number; + name: string; + currency: string; + explorerUrl: string; + rpcUrl: string; +}; diff --git a/apps/remix-dapp/src/utils/buildData.ts b/apps/remix-dapp/src/utils/buildData.ts new file mode 100644 index 00000000000..130669d47b0 --- /dev/null +++ b/apps/remix-dapp/src/utils/buildData.ts @@ -0,0 +1,41 @@ +import { execution } from '@remix-project/remix-lib'; + +const { txFormat: { parseFunctionParams }, txHelper: { encodeFunctionId, encodeParams: encodeParamsHelper } } = execution + +const buildData = (funAbi: any, params: string) => { + let funArgs = []; + let data: Buffer | string = ''; + let dataHex = ''; + + if (params.indexOf('raw:0x') === 0) { + // in that case we consider that the input is already encoded and *does not* contain the method signature + dataHex = params.replace('raw:0x', ''); + data = Buffer.from(dataHex, 'hex'); + } else { + try { + if (params.length > 0) { + funArgs = parseFunctionParams(params); + } + } catch (e) { + return { error: 'Error encoding arguments: ' + e }; + } + try { + data = encodeParamsHelper(funAbi, funArgs); + dataHex = data.toString(); + } catch (e) { + return { error: 'Error encoding arguments: ' + e }; + } + if (data.slice(0, 9) === 'undefined') { + dataHex = data.slice(9); + } + if (data.slice(0, 2) === '0x') { + dataHex = data.slice(2); + } + } + + dataHex = encodeFunctionId(funAbi) + dataHex; + + return { dataHex }; +}; + +export default buildData; diff --git a/apps/remix-dapp/src/utils/chains.ts b/apps/remix-dapp/src/utils/chains.ts new file mode 100644 index 00000000000..3ddce33988f --- /dev/null +++ b/apps/remix-dapp/src/utils/chains.ts @@ -0,0 +1,151 @@ +export const mainnet = { + chainId: 1, + name: 'Ethereum', + currency: 'ETH', + explorerUrl: 'https://etherscan.io', + rpcUrl: 'https://cloudflare-eth.com', +}; + +export const sepolia = { + chainId: 11155111, + name: 'Sepolia', + currency: 'ETH', + explorerUrl: 'https://sepolia.etherscan.io', + rpcUrl: 'https://sepolia.infura.io/v3/b6bf7d3508c941499b10025c0776eaf8', +}; + +export const goerli = { + chainId: 5, + name: 'Goerli', + currency: 'ETH', + explorerUrl: 'https://goerli.etherscan.io', + rpcUrl: 'https://rpc.ankr.com/eth_goerli', +}; + +export const arbitrum = { + chainId: 42161, + name: 'Arbitrum', + currency: 'ETH', + explorerUrl: 'https://arbiscan.io', + rpcUrl: 'https://arb1.arbitrum.io/rpc', +}; + +export const arbitrumGoerli = { + chainId: 421613, + name: 'Arbitrum Goerli', + currency: 'ETH', + explorerUrl: 'https://goerli.arbiscan.io', + rpcUrl: 'https://goerli-rollup.arbitrum.io/rpc', +}; + +export const avalanche = { + chainId: 43114, + name: 'Avalanche', + currency: 'AVAX', + explorerUrl: 'https://snowtrace.io', + rpcUrl: 'https://api.avax.network/ext/bc/C/rpc', +}; + +export const bsc = { + chainId: 56, + name: 'Binance Smart Chain', + currency: 'BNB', + explorerUrl: 'https://bscscan.com', + rpcUrl: 'https://rpc.ankr.com/bsc', +}; + +export const optimism = { + chainId: 10, + name: 'Optimism', + currency: 'ETH', + explorerUrl: 'https://optimistic.etherscan.io', + rpcUrl: 'https://mainnet.optimism.io', +}; + +export const optimismGoerli = { + chainId: 420, + name: 'Optimism Goerli', + currency: 'ETH', + explorerUrl: 'https://goerli-optimism.etherscan.io', + rpcUrl: 'https://goerli.optimism.io', +}; + +export const polygon = { + chainId: 137, + name: 'Polygon', + currency: 'MATIC', + explorerUrl: 'https://polygonscan.com', + rpcUrl: 'https://polygon-rpc.com', +}; + +export const polygonMumbai = { + chainId: 80001, + name: 'Polygon Mumbai', + currency: 'MATIC', + explorerUrl: 'https://mumbai.polygonscan.com', + rpcUrl: 'https://rpc.ankr.com/polygon_mumbai', +}; + +export const gnosis = { + chainId: 100, + name: 'Gnosis', + currency: 'xDAI', + explorerUrl: 'https://gnosis.blockscout.com', + rpcUrl: 'https://rpc.gnosischain.com', +}; + +export const zkSync = { + chainId: 324, + name: 'ZkSync', + currency: 'ETH', + explorerUrl: 'https://explorer.zksync.io', + rpcUrl: 'https://mainnet.era.zksync.io', +}; + +export const zora = { + chainId: 7777777, + name: 'Zora', + currency: 'ETH', + explorerUrl: 'https://explorer.zora.energy', + rpcUrl: 'https://rpc.zora.energy', +}; + +export const celo = { + chainId: 42220, + name: 'Celo', + currency: 'CELO', + explorerUrl: 'https://explorer.celo.org/mainnet', + rpcUrl: 'https://forno.celo.org', +}; + +export const base = { + chainId: 8453, + name: 'Base', + currency: 'BASE', + explorerUrl: 'https://basescan.org', + rpcUrl: 'https://mainnet.base.org', +}; + +export const aurora = { + chainId: 1313161554, + name: 'Aurora', + currency: 'ETH', + explorerUrl: 'https://explorer.aurora.dev', + rpcUrl: 'https://mainnet.aurora.dev', +}; + +export const ronin = { + chainId: 2020, + name: 'Ronin', + currency: 'RON', + explorerUrl: 'https://app.roninchain.com', + rpcUrl: 'https://api.roninchain.com/rpc', +}; + +export const saigon = { + chainId: 2021, + name: 'Saigon Testnet', + currency: 'RON', + explorerUrl: 'https://saigon-explorer.roninchain.com', + rpcUrl: 'https://saigon-testnet.roninchain.com/rpc', +}; diff --git a/apps/remix-dapp/src/utils/constants.ts b/apps/remix-dapp/src/utils/constants.ts new file mode 100644 index 00000000000..63def02f9b7 --- /dev/null +++ b/apps/remix-dapp/src/utils/constants.ts @@ -0,0 +1,53 @@ +import { + arbitrum, + arbitrumGoerli, + mainnet, + polygon, + polygonMumbai, + optimism, + optimismGoerli, + goerli, + sepolia, + ronin, + saigon, + aurora, + avalanche, + base, + bsc, + celo, + gnosis, + zkSync, + zora, +} from './chains'; + +export const constants = { + chains: [ + arbitrum, + arbitrumGoerli, + mainnet, + polygon, + polygonMumbai, + optimism, + optimismGoerli, + goerli, + sepolia, + ronin, + saigon, + aurora, + avalanche, + base, + bsc, + celo, + gnosis, + zkSync, + zora, + ], +}; + +export const PROJECT_ID = 'a4ba105e642ae64cdb5b9a86debc0a66'; +export const METADATA = { + name: 'Quick Dapp', + description: 'Quick Dapp', + url: window.origin, + icons: ['https://remix.ethereum.org/favicon.ico'], +}; diff --git a/apps/remix-dapp/src/utils/metamask.ts b/apps/remix-dapp/src/utils/metamask.ts new file mode 100644 index 00000000000..75f8cf5a7a0 --- /dev/null +++ b/apps/remix-dapp/src/utils/metamask.ts @@ -0,0 +1,96 @@ +import { RequestArguments } from '../types'; + +class MetaMask { + async addCustomNetwork(chainId: any) { + const paramsObj: any = { chainId }; + if (chainId === '0xa') { + paramsObj.chainName = 'Optimism'; + paramsObj.rpcUrls = ['https://mainnet.optimism.io']; + } + if (chainId === '0xa4b1') { + paramsObj.chainName = 'Arbitrum One'; + paramsObj.rpcUrls = ['https://arb1.arbitrum.io/rpc']; + } + if (chainId === '0x50877ed6') { + paramsObj.chainName = 'SKALE Chaos Testnet'; + paramsObj.rpcUrls = [ + 'https://staging-v3.skalenodes.com/v1/staging-fast-active-bellatrix', + ]; + paramsObj.nativeCurrency = { + name: 'sFUEL', + symbol: 'sFUEL', + decimals: 18, + }; + } + + const { chainName, rpcUrls, nativeCurrency, blockExplorerUrls } = paramsObj; + try { + await (window as any).ethereum.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: chainId }], + }); + } catch (switchError: any) { + // This error code indicates that the chain has not been added to MetaMask. + if (switchError.code === 4902) { + try { + const paramsObj: Record = { + chainId: chainId, + chainName: chainName, + rpcUrls: rpcUrls, + }; + if (nativeCurrency) paramsObj.nativeCurrency = nativeCurrency; + if (blockExplorerUrls) + paramsObj.blockExplorerUrls = blockExplorerUrls; + await (window as any).ethereum.request({ + method: 'wallet_addEthereumChain', + params: [paramsObj], + }); + + await (window as any).ethereum.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: chainId }], + }); + } catch (addError) { + // handle "add" error + } + } + // handle other "switch" errors + } + } + + async sendAsync(data: RequestArguments) { + return new Promise((resolve) => { + //@ts-expect-error sendAsync is a legacy function we know MetaMask supports it + window.ethereum.sendAsync(data, (error, response) => { + if (error) { + if ( + error.data && + error.data.originalError && + error.data.originalError.data + ) { + resolve({ + jsonrpc: '2.0', + error: error.data.originalError, + id: data.id, + }); + } else if (error.data && error.data.message) { + resolve({ + jsonrpc: '2.0', + error: error.data && error.data, + id: data.id, + }); + } else { + resolve({ + jsonrpc: '2.0', + error, + id: data.id, + }); + } + } + return resolve(response); + }); + }); + } +} + +export default new MetaMask(); diff --git a/apps/remix-dapp/src/utils/tools.ts b/apps/remix-dapp/src/utils/tools.ts new file mode 100644 index 00000000000..594d210f244 --- /dev/null +++ b/apps/remix-dapp/src/utils/tools.ts @@ -0,0 +1,17 @@ +export function isMobile() { + const userAgentInfo = navigator.userAgent; + const mobileAgents = [ + 'Android', + 'iPhone', + 'SymbianOS', + 'Windows Phone', + 'iPad', + 'iPod', + ]; + for (let v = 0; v < mobileAgents.length; v++) { + if (userAgentInfo.indexOf(mobileAgents[v]) > 0) { + return true; + } + } + return false; +} diff --git a/apps/remix-dapp/src/utils/txRunner.ts b/apps/remix-dapp/src/utils/txRunner.ts new file mode 100644 index 00000000000..6f22ca6c134 --- /dev/null +++ b/apps/remix-dapp/src/utils/txRunner.ts @@ -0,0 +1,277 @@ +import Web3, { + FMT_NUMBER, + type EthExecutionAPI, + type SupportedProviders, + FMT_BYTES, + type Bytes, +} from 'web3'; +import { addHexPrefix, toBytes } from '@ethereumjs/util'; +import { execution } from '@remix-project/remix-lib'; +import { toBigInt } from 'web3-utils'; +import { saveSettings } from '../actions'; + +const web3 = new Web3(); + +export const shortenAddress = (address: string, etherBalance?: string) => { + const len = address.length; + + return ( + address.slice(0, 5) + + '...' + + address.slice(len - 5, len) + + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '') + ); +}; + +async function pause() { + return await new Promise((resolve, reject) => { + setTimeout(resolve, 1000); + }); +} + +async function tryTillReceiptAvailable(txhash: Bytes) { + try { + const receipt = await web3.eth.getTransactionReceipt(txhash, { + number: FMT_NUMBER.NUMBER, + bytes: FMT_BYTES.HEX, + }); + if (receipt) { + if (!receipt.to && !receipt.contractAddress) { + // this is a contract creation and the receipt doesn't contain a contract address. we have to keep polling... + console.log( + 'this is a contract creation and the receipt does nott contain a contract address. we have to keep polling...' + ); + return receipt; + } else return receipt; + } + } catch (e) { + /* empty */ + } + await pause(); + // eslint-disable-next-line @typescript-eslint/return-await + return await tryTillReceiptAvailable(txhash); +} + +async function tryTillTxAvailable(txhash: Bytes) { + try { + const tx = await web3.eth.getTransaction(txhash, { + number: FMT_NUMBER.NUMBER, + bytes: FMT_BYTES.HEX, + }); + if (tx?.blockHash) return tx; + return tx; + } catch (e) { + /* empty */ + } + // eslint-disable-next-line @typescript-eslint/return-await + return await tryTillTxAvailable(txhash); +} + +export class TxRunner { + lastBlock: any; + currentFork: string; + listenOnLastBlockId: any; + mainNetGenesisHash: string | undefined; + blockGasLimit: any; + blockGasLimitDefault: any; + + constructor() { + this.lastBlock = null; + this.blockGasLimitDefault = 4300000; + this.blockGasLimit = this.blockGasLimitDefault; + this.currentFork = 'shanghai'; + this.mainNetGenesisHash = + '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'; + + this.listenOnLastBlock(); + + setInterval(() => { + this.getAccounts(); + }, 30000); + } + + setProvider( + provider: string | SupportedProviders | undefined + ) { + web3.setProvider(provider); + } + + getAccounts() { + saveSettings({ isRequesting: true }); + void web3.eth + .getAccounts() + .then(async (accounts) => { + const loadedAccounts: any = {}; + for (const account of accounts) { + const balance = await this.getBalanceInEther(account); + loadedAccounts[account] = shortenAddress(account, balance); + } + saveSettings({ loadedAccounts, isRequesting: false }); + }) + .catch((err) => { + console.log(err); + saveSettings({ isRequesting: false }); + }); + } + + async getBalanceInEther(address: string) { + const balance = await web3.eth.getBalance(address); + return Web3.utils.fromWei(balance.toString(10), 'ether'); + } + + async getGasPrice() { + return await web3.eth.getGasPrice(); + } + + async runTx(tx: any, gasLimit: any, useCall: boolean) { + if (useCall) { + const returnValue = await web3.eth.call({ ...tx, gas: gasLimit }); + + return toBytes(addHexPrefix(returnValue)); + } + + const network = await this.detectNetwork(); + + const txCopy = { + ...tx, + type: undefined, + maxFeePerGas: undefined, + gasPrice: undefined, + }; + + if (network?.lastBlock) { + if (network.lastBlock.baseFeePerGas) { + // the sending stack (web3.js / metamask need to have the type defined) + // this is to avoid the following issue: https://github.com/MetaMask/metamask-extension/issues/11824 + txCopy.type = '0x2'; + txCopy.maxFeePerGas = Math.ceil( + Number( + ( + toBigInt(network.lastBlock.baseFeePerGas) + + toBigInt(network.lastBlock.baseFeePerGas) / BigInt(3) + ).toString() + ) + ); + } else { + txCopy.type = '0x1'; + txCopy.gasPrice = undefined; + } + } + + try { + const gasEstimation = await web3.eth.estimateGas(txCopy); + tx.gas = !gasEstimation ? gasLimit : gasEstimation; + return await this._executeTx(tx, network); + } catch (error) { + console.log(error); + return { error }; + } + } + + async detectNetwork() { + const id = Number(await web3.eth.net.getId()); + let name = ''; + if (id === 1) name = 'Main'; + else if (id === 3) name = 'Ropsten'; + else if (id === 4) name = 'Rinkeby'; + else if (id === 5) name = 'Goerli'; + else if (id === 42) name = 'Kovan'; + else if (id === 11155111) name = 'Sepolia'; + else name = 'Custom'; + + if (id === 1) { + const block = await web3.eth.getBlock(0); + if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom'; + return { + id, + name, + lastBlock: this.lastBlock, + currentFork: this.currentFork, + }; + } else { + return { + id, + name, + lastBlock: this.lastBlock, + currentFork: this.currentFork, + }; + } + } + + stopListenOnLastBlock() { + if (this.listenOnLastBlockId) clearInterval(this.listenOnLastBlockId); + this.listenOnLastBlockId = null; + } + + async _updateChainContext() { + try { + const block = await web3.eth.getBlock('latest'); + // we can't use the blockGasLimit cause the next blocks could have a lower limit : https://github.com/ethereum/remix/issues/506 + this.blockGasLimit = block?.gasLimit + ? Math.floor( + Number(web3.utils.toNumber(block.gasLimit)) - + (5 * Number(web3.utils.toNumber(block.gasLimit))) / 1024 + ) + : web3.utils.toNumber(this.blockGasLimitDefault); + this.lastBlock = block; + try { + this.currentFork = execution.forkAt( + await web3.eth.net.getId(), + block.number + ); + } catch (e) { + this.currentFork = 'merge'; + console.log( + `unable to detect fork, defaulting to ${this.currentFork}..` + ); + console.error(e); + } + } catch (e) { + console.error(e); + this.blockGasLimit = this.blockGasLimitDefault; + } + } + + listenOnLastBlock() { + this.listenOnLastBlockId = setInterval(() => { + void this._updateChainContext(); + }, 15000); + } + + async _executeTx(tx: any, network: any) { + if (network?.lastBlock?.baseFeePerGas) { + // the sending stack (web3.js / metamask need to have the type defined) + // this is to avoid the following issue: https://github.com/MetaMask/metamask-extension/issues/11824 + tx.type = '0x2'; + } + + let currentDateTime = new Date(); + const start = currentDateTime.getTime() / 1000; + + try { + const { transactionHash } = await web3.eth.sendTransaction( + tx, + undefined, + { checkRevertBeforeSending: false, ignoreGasPricing: true } + ); + const receipt = await tryTillReceiptAvailable(transactionHash); + tx = await tryTillTxAvailable(transactionHash); + + currentDateTime = new Date(); + const end = currentDateTime.getTime() / 1000; + console.log('tx duration', end - start); + return { + receipt, + tx, + transactionHash: receipt ? receipt.transactionHash : null, + }; + } catch (error: any) { + console.log( + `Send transaction failed: ${error.message} . if you use an injected provider, please check it is properly unlocked. ` + ); + return { error }; + } + } +} + +export default new TxRunner(); diff --git a/apps/remix-dapp/src/utils/walletConnect.ts b/apps/remix-dapp/src/utils/walletConnect.ts new file mode 100644 index 00000000000..1f7a78329ad --- /dev/null +++ b/apps/remix-dapp/src/utils/walletConnect.ts @@ -0,0 +1,129 @@ +import { createWeb3Modal, defaultConfig } from '@web3modal/ethers5/react'; +import { + constants, + PROJECT_ID as projectId, + METADATA as metadata, +} from './constants'; +import EventManager from 'events'; +import { Chain, RequestArguments } from '../types'; +import txRunner from './txRunner'; +import { saveSettings } from '../actions'; + +class WalletConnect { + web3modal: ReturnType; + ethersConfig: ReturnType; + chains: Chain[]; + currentChain?: number; + internalEvents: EventManager; + currentAccount?: string; + + constructor() { + this.internalEvents = new EventManager(); + const ethersConfig = defaultConfig({ + metadata, + rpcUrl: 'https://cloudflare-eth.com', + }); + + this.web3modal = createWeb3Modal({ + projectId, + chains: constants.chains, + metadata, + ethersConfig, + }); + this.ethersConfig = ethersConfig; + this.chains = constants.chains; + } + + subscribeToEvents() { + this.web3modal.subscribeProvider(({ address, isConnected, chainId }) => { + if (isConnected) { + txRunner.getAccounts(); + if (address !== this.currentAccount) { + this.currentAccount = address; + } + if (this.currentChain !== chainId) { + this.currentChain = chainId; + } + } else { + saveSettings({ loadedAccounts: {} }); + this.currentAccount = ''; + this.currentChain = 0; + } + }); + } + + async sendAsync(data: RequestArguments) { + const address = this.web3modal.getAddress(); + const provider = this.web3modal.getWalletProvider(); + if (address && provider) { + if (data.method === 'eth_accounts') { + return { + jsonrpc: '2.0', + result: [address], + id: data.id, + }; + } else { + //@ts-expect-error this flag does not correspond to EIP-1193 but was introduced by MetaMask + if (provider.isMetamask && provider.sendAsync) { + return new Promise((resolve) => { + //@ts-expect-error sendAsync is a legacy function we know MetaMask supports it + provider.sendAsync(data, (error, response) => { + if (error) { + if ( + error.data && + error.data.originalError && + error.data.originalError.data + ) { + resolve({ + jsonrpc: '2.0', + error: error.data.originalError, + id: data.id, + }); + } else if (error.data && error.data.message) { + resolve({ + jsonrpc: '2.0', + error: error.data && error.data, + id: data.id, + }); + } else { + resolve({ + jsonrpc: '2.0', + error, + id: data.id, + }); + } + } + return resolve(response); + }); + }); + } else { + try { + const message = await provider.request(data); + return { jsonrpc: '2.0', result: message, id: data.id }; + } catch (e: any) { + return { + jsonrpc: '2.0', + error: { message: e.message, code: -32603 }, + id: data.id, + }; + } + } + } + } else { + const err = `Cannot make ${data.method} request. Remix client is not connected to walletconnect client`; + console.error(err); + return { + jsonrpc: '2.0', + error: { message: err, code: -32603 }, + id: data.id, + }; + } + } + + async deactivate() { + console.log('deactivating walletconnect plugin...'); + await this.web3modal.disconnect(); + } +} + +export default new WalletConnect(); diff --git a/apps/remix-dapp/src/vite-env.d.ts b/apps/remix-dapp/src/vite-env.d.ts new file mode 100644 index 00000000000..11f02fe2a00 --- /dev/null +++ b/apps/remix-dapp/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/remix-dapp/tsconfig.app.json b/apps/remix-dapp/tsconfig.app.json new file mode 100644 index 00000000000..af84f21cfc8 --- /dev/null +++ b/apps/remix-dapp/tsconfig.app.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": [ + "jest.config.ts", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx" + ], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/apps/remix-dapp/tsconfig.json b/apps/remix-dapp/tsconfig.json new file mode 100644 index 00000000000..5aab5e79111 --- /dev/null +++ b/apps/remix-dapp/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/apps/remix-dapp/webpack.config.js b/apps/remix-dapp/webpack.config.js new file mode 100644 index 00000000000..8a8306b36a4 --- /dev/null +++ b/apps/remix-dapp/webpack.config.js @@ -0,0 +1,108 @@ +const {composePlugins, withNx} = require('@nrwl/webpack') +const webpack = require('webpack') +const TerserPlugin = require('terser-webpack-plugin') +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') +const { WebpackManifestPlugin } = require('webpack-manifest-plugin') + +// Nx plugins for webpack. +module.exports = composePlugins(withNx(), (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + // add fallback for node modules + config.resolve.fallback = { + ...config.resolve.fallback, + crypto: require.resolve('crypto-browserify'), + stream: require.resolve('stream-browserify'), + path: require.resolve('path-browserify'), + http: require.resolve('stream-http'), + https: require.resolve('https-browserify'), + constants: require.resolve('constants-browserify'), + os: false, //require.resolve("os-browserify/browser"), + timers: false, // require.resolve("timers-browserify"), + zlib: require.resolve('browserify-zlib'), + fs: false, + module: false, + tls: false, + net: false, + readline: false, + child_process: false, + buffer: require.resolve('buffer/'), + vm: require.resolve('vm-browserify'), + } + + // add externals + config.externals = { + ...config.externals, + solc: 'solc', + } + + // add public path + config.output.publicPath = './' + + // add copy & provide plugin + config.plugins.push( + new webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + url: ['url', 'URL'], + process: 'process/browser', + }) + ) + + // set the define plugin to load the WALLET_CONNECT_PROJECT_ID + config.plugins.push( + new webpack.DefinePlugin({ + WALLET_CONNECT_PROJECT_ID: JSON.stringify(process.env.WALLET_CONNECT_PROJECT_ID), + }) + ) + + config.plugins.push( + new WebpackManifestPlugin({ + fileName: 'manifest.json', + publicPath: '/', + generate: (seed, files, entrypoints) => { + const manifest = files.reduce((manifest, { name, path }) => { + manifest[name] = path; + return manifest; + }, seed); + return manifest; + }, + filter: (file) => { + return !(file.path.includes('assets') || file.path.includes('.map')); + }, + }) + ) + + // souce-map loader + config.module.rules.push({ + test: /\.js$/, + use: ['source-map-loader'], + enforce: 'pre', + }) + + config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings + + // set minimizer + config.optimization.minimizer = [ + new TerserPlugin({ + parallel: true, + terserOptions: { + ecma: 2015, + compress: false, + mangle: false, + format: { + comments: false, + }, + }, + extractComments: false, + }), + new CssMinimizerPlugin(), + ] + + config.watchOptions = { + ignored: /node_modules/, + } + + config.experiments.syncWebAssembly = true + + return config +}) diff --git a/apps/remix-dapp/yarn.lock b/apps/remix-dapp/yarn.lock new file mode 100644 index 00000000000..609188ee555 --- /dev/null +++ b/apps/remix-dapp/yarn.lock @@ -0,0 +1,34 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +tapable@^2.0.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +webpack-manifest-plugin@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-5.0.0.tgz#084246c1f295d1b3222d36e955546433ca8df803" + integrity sha512-8RQfMAdc5Uw3QbCQ/CBV/AXqOR8mt03B6GJmRbhWopE8GzRfEpn+k0ZuWywxW+5QZsffhmFDY1J6ohqJo+eMuw== + dependencies: + tapable "^2.0.0" + webpack-sources "^2.2.0" + +webpack-sources@^2.2.0: + version "2.3.1" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" + integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" diff --git a/apps/remix-ide/project.json b/apps/remix-ide/project.json index 4a37224fa34..563c6d29107 100644 --- a/apps/remix-ide/project.json +++ b/apps/remix-ide/project.json @@ -3,7 +3,7 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/remix-ide/src", "projectType": "application", - "implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "vyper", "solhint", "walletconnect", "circuit-compiler", "learneth", "quick-dapp"], + "implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "vyper", "solhint", "walletconnect", "circuit-compiler", "learneth", "quick-dapp", "remix-dapp"], "targets": { "build": { "executor": "@nrwl/webpack:webpack", From a3e0ef07ce8073609b2f5c93b720998c698a7cc2 Mon Sep 17 00:00:00 2001 From: drafish Date: Thu, 25 Jul 2024 14:05:00 +0800 Subject: [PATCH 02/10] fix quickdapp e2e test --- apps/remix-ide-e2e/src/tests/quickDapp.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide-e2e/src/tests/quickDapp.test.ts b/apps/remix-ide-e2e/src/tests/quickDapp.test.ts index fa10a63d2ff..d4a4844a5ff 100644 --- a/apps/remix-ide-e2e/src/tests/quickDapp.test.ts +++ b/apps/remix-ide-e2e/src/tests/quickDapp.test.ts @@ -166,7 +166,7 @@ const tests = { .waitForElementVisible('*[data-id="treeViewDiv0"]', 20000) .assert.containsText('*[data-id="treeViewDiv0"]', 'uint256: 11') .perform((done) => { - axios.get(`https://${surgeSubdomain}.surge.sh/logo.png?t=${new Date().getTime()}`, { responseType: 'arraybuffer' }).then((resp) => { + axios.get(`https://${surgeSubdomain}.surge.sh/assets/logo.png?t=${new Date().getTime()}`, { responseType: 'arraybuffer' }).then((resp) => { const hash = crypto.createHash('sha256'); hash.update(resp.data); const hashValue = hash.digest('hex'); From a38d06706049feae288d6d4f1ea669249f591ea2 Mon Sep 17 00:00:00 2001 From: drafish Date: Mon, 29 Jul 2024 10:31:26 +0800 Subject: [PATCH 03/10] split homepage --- apps/remix-dapp/src/App.tsx | 11 +- .../remix-dapp/src/components/Home/mobile.tsx | 88 +++++++++++++ apps/remix-dapp/src/components/Home/pc.tsx | 47 +++++++ apps/remix-dapp/src/pages/Home/index.tsx | 124 ------------------ apps/remix-dapp/src/reducers/state.ts | 1 + apps/remix-dapp/src/router.tsx | 11 -- 6 files changed, 143 insertions(+), 139 deletions(-) create mode 100644 apps/remix-dapp/src/components/Home/mobile.tsx create mode 100644 apps/remix-dapp/src/components/Home/pc.tsx delete mode 100644 apps/remix-dapp/src/pages/Home/index.tsx delete mode 100644 apps/remix-dapp/src/router.tsx diff --git a/apps/remix-dapp/src/App.tsx b/apps/remix-dapp/src/App.tsx index ebe6722f391..79b770ae581 100644 --- a/apps/remix-dapp/src/App.tsx +++ b/apps/remix-dapp/src/App.tsx @@ -1,18 +1,19 @@ import React, { useEffect, useReducer } from 'react'; -import { RouterProvider } from 'react-router-dom'; -import router from './router'; import { IntlProvider } from 'react-intl'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; import { AppContext } from './contexts'; import { appInitialState, appReducer } from './reducers/state'; -import { initDispatch, updateState } from './actions'; +import { initDispatch, initInstance, updateState } from './actions'; import enJson from './locales/en'; import zhJson from './locales/zh'; import esJson from './locales/es'; import frJson from './locales/fr'; import itJson from './locales/it'; import './App.css'; +import { isMobile } from './utils/tools'; +import MobilePage from './components/Home/mobile'; +import PCPage from './components/Home/pc'; const localeMap: Record = { zh: zhJson, @@ -31,7 +32,9 @@ function App(): JSX.Element { useEffect(() => { initDispatch(dispatch); updateState(appState); + initInstance(); }, []); + return ( - + {isMobile() ? : } { + const { + appState: { instance }, + } = useContext(AppContext); + const [active, setActive] = useState('functions'); + + return
+
+
+
+ +
+ +
+ +
+ {!instance.noTerminal && ( +
+ +
+ )} +
+ +
+
    +
  • { + setActive('functions'); + }} + > + + Functions + +
  • + {!instance.noTerminal && ( +
  • { + setActive('transactions'); + }} + > + + Transactions + +
  • + )} +
  • { + setActive('settings'); + }} + > + + Settings + +
  • +
+
+}; + +export default MobilePage; diff --git a/apps/remix-dapp/src/components/Home/pc.tsx b/apps/remix-dapp/src/components/Home/pc.tsx new file mode 100644 index 00000000000..0cb31f3d025 --- /dev/null +++ b/apps/remix-dapp/src/components/Home/pc.tsx @@ -0,0 +1,47 @@ +import React, { useContext, useEffect } from 'react'; +import { UniversalDappUI } from '../../components/UniversalDappUI'; +import { SettingsUI } from '../../components/SettingsUI'; +import RemixUiTerminal from '../../components/UiTerminal'; +import DragBar from '../../components/DragBar'; +import DappTop from '../../components/DappTop'; +import { AppContext } from '../../contexts'; + +const PCPage: React.FC = () => { + const { + appState: { terminal, instance }, + } = useContext(AppContext); + const { height } = terminal; + + return
+
+
+
+
+ +
+ +
+ +
+
+ +
+
+ {!instance.noTerminal && ( + <> + + + + )} +
+}; + +export default PCPage; diff --git a/apps/remix-dapp/src/pages/Home/index.tsx b/apps/remix-dapp/src/pages/Home/index.tsx deleted file mode 100644 index 61b53be5b95..00000000000 --- a/apps/remix-dapp/src/pages/Home/index.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { UniversalDappUI } from '../../components/UniversalDappUI'; -import { SettingsUI } from '../../components/SettingsUI'; -import RemixUiTerminal from '../../components/UiTerminal'; -import DragBar from '../../components/DragBar'; -import DappTop from '../../components/DappTop'; -import { AppContext } from '../../contexts'; -import { initInstance } from '../../actions'; -import { isMobile } from '../../utils/tools'; -import TxList from '../../components/UiTerminal/TxList'; - -const HomePage: React.FC = () => { - const { - appState: { terminal, instance }, - } = useContext(AppContext); - const [active, setActive] = useState('functions'); - const { height } = terminal; - useEffect(() => { - initInstance(); - }, []); - - return isMobile() ? ( -
-
- - -
- {!instance.noTerminal && ( -
- -
- )} -
- -
-
    -
  • { - setActive('functions'); - }} - > - - Functions - -
  • - {!instance.noTerminal && ( -
  • { - setActive('transactions'); - }} - > - - Transactions - -
  • - )} -
  • { - setActive('settings'); - }} - > - - Settings - -
  • -
-
- ) : ( -
-
-
-
-
- -
- -
- -
-
- -
-
- {!instance.noTerminal && ( - <> - - - - )} -
- ); -}; - -export default HomePage; diff --git a/apps/remix-dapp/src/reducers/state.ts b/apps/remix-dapp/src/reducers/state.ts index eee54d6b510..bcf01f41582 100644 --- a/apps/remix-dapp/src/reducers/state.ts +++ b/apps/remix-dapp/src/reducers/state.ts @@ -7,6 +7,7 @@ export const appInitialState: any = { decodedResponse: {}, abi: [], solcVersion: {}, + containers: [] }, settings: { sendValue: '0', diff --git a/apps/remix-dapp/src/router.tsx b/apps/remix-dapp/src/router.tsx deleted file mode 100644 index 370bfe4fc84..00000000000 --- a/apps/remix-dapp/src/router.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { createBrowserRouter } from 'react-router-dom'; -import HomePage from './pages/Home'; - -const router = createBrowserRouter([ - { - path: '/', - element: , - }, -]); - -export default router; From c5c9f7c6f55ef02b32bfb4c24da4334bd9dc1c32 Mon Sep 17 00:00:00 2001 From: drafish Date: Mon, 29 Jul 2024 10:32:43 +0800 Subject: [PATCH 04/10] update remix-dapp --- .../src/components/UiTerminal/Context.tsx | 30 +-- .../UiTerminal/RenderKnownTransactions.tsx | 66 +++--- .../src/components/UniversalDappUI/index.tsx | 192 ++++++------------ apps/remix-dapp/src/reducers/state.ts | 34 ++-- 4 files changed, 110 insertions(+), 212 deletions(-) diff --git a/apps/remix-dapp/src/components/UiTerminal/Context.tsx b/apps/remix-dapp/src/components/UiTerminal/Context.tsx index beb9844c845..504e06b558c 100644 --- a/apps/remix-dapp/src/components/UiTerminal/Context.tsx +++ b/apps/remix-dapp/src/components/UiTerminal/Context.tsx @@ -20,35 +20,7 @@ const Context = ({ opts, provider }: { opts: any; provider: string }) => { : data.transactionIndex; const value = val ? typeConversion.toInt(val) : 0; - if (provider?.startsWith('vm')) { - return ( -
- - - [vm] - -
- from: {from} -
-
- to: {to} -
-
- value: {value} wei -
-
- data: {input} -
-
- logs: {logs} -
-
- hash: {hash} -
-
-
- ); - } else if (data.resolvedData) { + if (data.resolvedData) { return (
diff --git a/apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx b/apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx index f4a31ffb73f..ec7751a0541 100644 --- a/apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx +++ b/apps/remix-dapp/src/components/UiTerminal/RenderKnownTransactions.tsx @@ -35,39 +35,39 @@ const RenderKnownTransactions = ({
{showTableHash.includes(tx.hash) ? showTable( - { - hash: tx.hash, - status: receipt !== null ? receipt.status : null, - isCall: tx.isCall, - contractAddress: receipt.contractAddress, - blockHash: tx.blockHash, - blockNumber: tx.blockNumber, - data: tx, - from, - to, - gas: tx.gas, - input: tx.input, - 'decoded input': resolvedData?.params - ? JSON.stringify( - typeConversion.stringify(resolvedData.params), - null, - '\t' - ) - : ' - ', - 'decoded output': resolvedData?.decodedReturnValue - ? JSON.stringify( - typeConversion.stringify(resolvedData.decodedReturnValue), - null, - '\t' - ) - : ' - ', - logs, - val: tx.value, - transactionCost: tx.transactionCost, - executionCost: tx.executionCost, - }, - showTableHash - ) + { + hash: tx.hash, + status: receipt !== null ? receipt.status : null, + isCall: tx.isCall, + contractAddress: receipt.contractAddress, + blockHash: tx.blockHash, + blockNumber: tx.blockNumber, + data: tx, + from, + to, + gas: tx.gas, + input: tx.input, + 'decoded input': resolvedData?.params + ? JSON.stringify( + typeConversion.stringify(resolvedData.params), + null, + '\t' + ) + : ' - ', + 'decoded output': resolvedData?.decodedReturnValue + ? JSON.stringify( + typeConversion.stringify(resolvedData.decodedReturnValue), + null, + '\t' + ) + : ' - ', + logs, + val: tx.value, + transactionCost: tx.transactionCost, + executionCost: tx.executionCost, + }, + showTableHash + ) : null} ); diff --git a/apps/remix-dapp/src/components/UniversalDappUI/index.tsx b/apps/remix-dapp/src/components/UniversalDappUI/index.tsx index 3d3cde33bcc..1c2a52be704 100644 --- a/apps/remix-dapp/src/components/UniversalDappUI/index.tsx +++ b/apps/remix-dapp/src/components/UniversalDappUI/index.tsx @@ -161,136 +161,61 @@ export function UniversalDappUI(props: any) { return (
- {containers - ? containers.map((id: any) => { - return ( -
- {items[id].map((funcId: any, index: any) => { - const funcABI = contractABI[funcId]; - if (funcABI.type !== 'function') return null; - const isConstant = + {containers.map((id: any) => { + return ( +
+ {items[id].map((funcId: any, index: any) => { + const funcABI = contractABI[funcId]; + if (funcABI.type !== 'function') return null; + const isConstant = funcABI.constant !== undefined ? funcABI.constant : false; - const lookupOnly = + const lookupOnly = funcABI.stateMutability === 'view' || funcABI.stateMutability === 'pure' || isConstant; - const inputs = getFuncABIInputs(funcABI); - return ( -
-
-
- {funcABI.title &&

{funcABI.title}

} - { - runTransaction( - lookupOnly, - funcABI, - valArray, - inputsValues, - funcId - ); - }} - inputs={inputs} - lookupOnly={lookupOnly} - key={funcId} - /> - {funcABI.details && ( -
- {funcABI.details} -
- )} - {lookupOnly && ( -
- - {Object.keys( - instance.decodedResponse || {} - ).map((key) => { - const funcIndex = funcId; - const response = + const inputs = getFuncABIInputs(funcABI); + return ( +
+
+
+ {funcABI.title &&

{funcABI.title}

} + { + runTransaction( + lookupOnly, + funcABI, + valArray, + inputsValues, + funcId + ); + }} + inputs={inputs} + lookupOnly={lookupOnly} + key={funcId} + /> + {funcABI.details && ( +
+ {funcABI.details} +
+ )} + {lookupOnly && ( +
+ + {Object.keys( + instance.decodedResponse || {} + ).map((key) => { + const funcIndex = funcId; + const response = instance.decodedResponse[key]; - return key === funcIndex - ? Object.keys(response || {}).map( - (innerkey, _index) => { - return renderData( - instance.decodedResponse[key][ - innerkey - ], - response, - innerkey, - innerkey - ); - } - ) - : null; - })} - -
- )} -
-
-
- ); - })} -
- ); - }) - : contractABI?.map((funcABI: any, index: any) => { - if (funcABI.type !== 'function') return null; - const isConstant = - funcABI.constant !== undefined ? funcABI.constant : false; - const lookupOnly = - funcABI.stateMutability === 'view' || - funcABI.stateMutability === 'pure' || - isConstant; - const inputs = getFuncABIInputs(funcABI); - - return ( -
-
-
-
- { - runTransaction( - lookupOnly, - funcABI, - valArray, - inputsValues, - index - ); - }} - inputs={inputs} - lookupOnly={lookupOnly} - key={index} - /> - {lookupOnly && ( -
- - {Object.keys(instance.decodedResponse || {}).map( - (key) => { - const funcIndex = index.toString(); - const response = instance.decodedResponse[key]; - return key === funcIndex ? Object.keys(response || {}).map( (innerkey, _index) => { @@ -305,17 +230,18 @@ export function UniversalDappUI(props: any) { } ) : null; - } - )} - -
- )} + })} + +
+ )} +
-
-
- ); - })} + ); + })} +
+ ); + })}
); } diff --git a/apps/remix-dapp/src/reducers/state.ts b/apps/remix-dapp/src/reducers/state.ts index bcf01f41582..7d8e436e91d 100644 --- a/apps/remix-dapp/src/reducers/state.ts +++ b/apps/remix-dapp/src/reducers/state.ts @@ -27,25 +27,25 @@ export const appInitialState: any = { export const appReducer = (state = appInitialState, action: any): any => { switch (action.type) { - case 'SET_INSTANCE': - return { - ...state, - instance: { ...state.instance, ...action.payload }, - }; + case 'SET_INSTANCE': + return { + ...state, + instance: { ...state.instance, ...action.payload }, + }; - case 'SET_SETTINGS': - return { - ...state, - settings: { ...state.settings, ...action.payload }, - }; + case 'SET_SETTINGS': + return { + ...state, + settings: { ...state.settings, ...action.payload }, + }; - case 'SET_TERMINAL': - return { - ...state, - terminal: { ...state.terminal, ...action.payload }, - }; + case 'SET_TERMINAL': + return { + ...state, + terminal: { ...state.terminal, ...action.payload }, + }; - default: - throw new Error(); + default: + throw new Error(); } }; From f72d0fc7ce0e17576dc57e16e0cafb94d8b5c487 Mon Sep 17 00:00:00 2001 From: drafish Date: Wed, 28 Aug 2024 08:20:11 +0800 Subject: [PATCH 05/10] fix lint --- apps/remix-dapp/src/contexts/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-dapp/src/contexts/index.ts b/apps/remix-dapp/src/contexts/index.ts index e1fc5812f23..c92823eee82 100644 --- a/apps/remix-dapp/src/contexts/index.ts +++ b/apps/remix-dapp/src/contexts/index.ts @@ -1,3 +1,3 @@ -import {createContext} from 'react' +import { createContext } from 'react' export const AppContext = createContext({}) From cc6f38d077587d554745db9891d561ed10ae5e93 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 25 Sep 2024 11:54:50 +0200 Subject: [PATCH 06/10] Update index.ts --- apps/quick-dapp/src/actions/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/quick-dapp/src/actions/index.ts b/apps/quick-dapp/src/actions/index.ts index 46c21505721..fbcda6ac4d5 100644 --- a/apps/quick-dapp/src/actions/index.ts +++ b/apps/quick-dapp/src/actions/index.ts @@ -10,8 +10,8 @@ const { encodeFunctionId } = execution.txHelper; const surgeClient = new SurgeClient({ // surge backend doesn't support cross-domain, that's why the proxy goes - // here is the codebase of proxy: https://github.com/drafish/vercel-proxy - proxy: 'https://vercel-proxy-bice-six.vercel.app', + // here is the codebase of proxy: https://github.com/remix-project-org/remix-wildcard/blob/master/src/hosts/common-corsproxy.ts + proxy: 'https://common-corsproxy.remixproject.org/', onError: (err: Error) => { console.log(err); }, From d18e8df2d81bcd4ccfdb8d3f26d9b30fd3e778ee Mon Sep 17 00:00:00 2001 From: mtzsky Date: Wed, 25 Sep 2024 22:34:38 +0200 Subject: [PATCH 07/10] fix broken link to i18n.config.json in the docs --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a603101311c..a29c31a0b94 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,7 +77,7 @@ const locales = [ ] ``` You can find the language's `code, name, localeName` in this link -https://github.com/ethereum/ethereum-org-website/blob/dev/i18n/config.json +https://github.com/ethereum/ethereum-org-website/blob/dev/i18n.config.json ### Whether or not to use `defaultMessage`? If you search `FormattedMessage` or `intl.formatMessage` in this project, you will notice that most of them only have a `id` prop, but a few of them have a `defaultMessage` prop. From 969f8c2c5453a0b172bf6e410b570ab7e4d4f399 Mon Sep 17 00:00:00 2001 From: Elias Rad <146735585+nnsW3@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:58:47 +0300 Subject: [PATCH 08/10] Update release-management.md --- release-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-management.md b/release-management.md index 20ac266987b..f356ff7a80b 100644 --- a/release-management.md +++ b/release-management.md @@ -19,7 +19,7 @@ Together with the team, the release manager will refine the list of issues and P More generally and a non-negligible part of the planning is to properly ensure that bugs, issues that weren't totally identified in the roadmap, and the roadmap issues are still being processed as they should. During this phase, **all the current project issues have to be assigned.** -After the release planning each one of us has to specify the effort need for each issue (1 - 2 - 3 - 5 - 8 - 13) +After the release planning each one of us has to specify the effort needed for each issue (1 - 2 - 3 - 5 - 8 - 13) ## Release planning - refinement meeting: This meeting happens a few days after the release planning meeting. From d9a4bc1bb87f91a140248ce1de5a32aa7edab0d7 Mon Sep 17 00:00:00 2001 From: Elias Rad <146735585+nnsW3@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:02:02 +0300 Subject: [PATCH 09/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9059e6318ca..166188a051a 100644 --- a/README.md +++ b/README.md @@ -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. From 2d3a46db37137bcbbf679d16e944c682f805ba97 Mon Sep 17 00:00:00 2001 From: Elias Rad <146735585+nnsW3@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:02:58 +0300 Subject: [PATCH 10/10] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a29c31a0b94..e23efaf847d 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.