Merge branch 'master' of https://github.com/ethereum/remix-project into resolve_with_package

pull/3311/head
filip mertens 2 years ago
commit b737714a21
  1. 2
      .circleci/config.yml
  2. 2
      apps/remix-ide-e2e/src/commands/connectToExternalHttpProvider.ts
  3. 2
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  4. 2
      apps/remix-ide-e2e/src/tests/etherscan_api.ts
  5. 4
      apps/remix-ide-e2e/src/tests/plugin_api.ts
  6. 8
      apps/remix-ide-e2e/src/tests/providers.test.ts
  7. 21
      apps/remix-ide-e2e/src/tests/proxy.test.ts
  8. 2
      apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
  9. 41
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  10. 1
      apps/remix-ide/ci/browser_test.sh
  11. 1
      apps/remix-ide/ci/browser_tests_plugin_api.sh
  12. 2
      apps/remix-ide/ci/makeMockCompiler.js
  13. 38
      apps/remix-ide/src/app.js
  14. 2
      apps/remix-ide/src/app/components/preload.tsx
  15. 19
      apps/remix-ide/src/app/editor/editor.js
  16. 34
      apps/remix-ide/src/app/files/fileManager.ts
  17. 6
      apps/remix-ide/src/app/plugins/contractFlattener.tsx
  18. 2
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  19. 61
      apps/remix-ide/src/app/plugins/solidity-umlgen.tsx
  20. 136
      apps/remix-ide/src/app/providers/abstract-provider.tsx
  21. 18
      apps/remix-ide/src/app/providers/basic-injected-provider.tsx
  22. 106
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  23. 2
      apps/remix-ide/src/app/providers/external-http-provider.tsx
  24. 2
      apps/remix-ide/src/app/providers/foundry-provider.tsx
  25. 6
      apps/remix-ide/src/app/providers/ganache-provider.tsx
  26. 29
      apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx
  27. 6
      apps/remix-ide/src/app/providers/hardhat-provider.tsx
  28. 55
      apps/remix-ide/src/app/providers/injected-L2-provider.tsx
  29. 11
      apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx
  30. 11
      apps/remix-ide/src/app/providers/injected-optimism-provider.tsx
  31. 75
      apps/remix-ide/src/app/providers/injected-provider.tsx
  32. 29
      apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx
  33. 29
      apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx
  34. 88
      apps/remix-ide/src/app/providers/vm-provider.tsx
  35. 157
      apps/remix-ide/src/app/tabs/abstract-provider.tsx
  36. 6
      apps/remix-ide/src/app/tabs/locales/en/home.json
  37. 2
      apps/remix-ide/src/app/tabs/locales/zh/home.json
  38. 13
      apps/remix-ide/src/app/tabs/runTab/model/recorder.js
  39. 150
      apps/remix-ide/src/app/udapp/run-tab.js
  40. 13
      apps/remix-ide/src/assets/js/loader.js
  41. 51
      apps/remix-ide/src/blockchain/blockchain.js
  42. 119
      apps/remix-ide/src/blockchain/execution-context.js
  43. 2
      apps/remix-ide/src/blockchain/providers/injected.js
  44. 2
      apps/remix-ide/src/blockchain/providers/node.js
  45. 16
      apps/remix-ide/src/blockchain/providers/vm.js
  46. 2
      apps/remix-ide/src/blockchain/providers/worker-vm.ts
  47. 6
      apps/remix-ide/src/lib/helper.js
  48. 4
      apps/remix-ide/src/remixAppManager.js
  49. 3
      apps/remix-ide/src/remixEngine.js
  50. 2
      apps/solidity-compiler/src/app/compiler.ts
  51. 6
      libs/ghaction-helper/package.json
  52. 30
      libs/ghaction-helper/src/methods.ts
  53. 8
      libs/remix-analyzer/package.json
  54. 8
      libs/remix-astwalker/package.json
  55. 2
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  56. 19
      libs/remix-core-plugin/src/types/contract.ts
  57. 10
      libs/remix-debug/package.json
  58. 2
      libs/remix-debug/src/code/disassembler.ts
  59. 3
      libs/remix-debug/src/solidity-decoder/types/ArrayType.ts
  60. 2
      libs/remix-debug/src/solidity-decoder/types/DynamicByteArray.ts
  61. 6
      libs/remix-debug/src/solidity-decoder/types/Mapping.ts
  62. 4
      libs/remix-debug/src/solidity-decoder/types/util.ts
  63. 2
      libs/remix-debug/test/decoder/stateTests/mapping.ts
  64. 13
      libs/remix-debug/test/vmCall.ts
  65. 2
      libs/remix-lib/package.json
  66. 2
      libs/remix-lib/src/execution/txFormat.ts
  67. 4
      libs/remix-lib/src/execution/txListener.ts
  68. 8
      libs/remix-lib/src/execution/txRunnerVM.ts
  69. 3
      libs/remix-lib/src/execution/typeConversion.ts
  70. 206
      libs/remix-lib/src/hash.ts
  71. 2
      libs/remix-lib/src/helpers/txResultHelper.ts
  72. 3
      libs/remix-lib/src/index.ts
  73. 6
      libs/remix-lib/src/util.ts
  74. 3
      libs/remix-lib/test/txResultHelper.ts
  75. 10
      libs/remix-simulator/package.json
  76. 10
      libs/remix-simulator/src/VmProxy.ts
  77. 7
      libs/remix-simulator/src/genesis.ts
  78. 5
      libs/remix-simulator/src/methods/accounts.ts
  79. 8
      libs/remix-simulator/src/methods/transactions.ts
  80. 6
      libs/remix-simulator/src/provider.ts
  81. 75
      libs/remix-simulator/src/vm-context.ts
  82. 8
      libs/remix-solidity/package.json
  83. 2
      libs/remix-solidity/src/compiler/types.ts
  84. 10
      libs/remix-tests/package.json
  85. 56
      libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx
  86. 1
      libs/remix-ui/app/src/lib/remix-app/types/index.ts
  87. 2
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  88. 2
      libs/remix-ui/debugger-ui/src/lib/vm-debugger/global-variables.tsx
  89. 2
      libs/remix-ui/debugger-ui/src/utils/solidityTypeFormatter.ts
  90. 2
      libs/remix-ui/editor/src/lib/remix-plugin-types.ts
  91. 44
      libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx
  92. 25
      libs/remix-ui/helper/src/lib/helper-components.tsx
  93. 14
      libs/remix-ui/helper/src/lib/remix-ui-helper.ts
  94. 54
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  95. 33
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  96. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  97. 49
      libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx
  98. 41
      libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx
  99. 41
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  100. 24
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  101. Some files were not shown because too many files have changed in this diff Show More

@ -24,7 +24,7 @@ jobs:
key: v1-deps-{{ checksum "yarn.lock" }}
paths:
- node_modules
- run: NX_BIN_URL=http://127.0.0.1:8080/assets/js NX_WASM_URL=http://127.0.0.1:8080/assets/js yarn build:production
- run: NX_BIN_URL=http://127.0.0.1:8080/assets/js NX_WASM_URL=http://127.0.0.1:8080/assets/js NPM_URL=http://localhost:9090/ yarn build:production
- run: yarn nx build remix-ide-e2e-src-local-plugin & yarn run build:libs
- run: yarn nx run debugger:build:production
- run: yarn nx run solidity-compiler:build:production

@ -15,7 +15,7 @@ class ConnectToExternalHttpProvider extends EventEmitter {
suppressNotFoundErrors: true,
timeout: 5000
})
.switchEnvironment('External Http Provider')
.switchEnvironment('basic-http-provider')
.waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(() => {
(document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus()

@ -231,7 +231,7 @@ module.exports = {
.setSolidityCompilerVersion('soljson-v0.8.7+commit.e28d00a7.js')
.addFile('useDebugNodes.sol', sources[5]['useDebugNodes.sol']) // compile contract
.clickLaunchIcon('udapp')
.switchEnvironment('External Http Provider') // select web3 provider with debug nodes URL
.switchEnvironment('basic-http-provider') // select web3 provider with debug nodes URL
.clearValue('*[data-id="modalDialogCustomPromptText"]')
.setValue('*[data-id="modalDialogCustomPromptText"]', 'https://remix-rinkeby.ethdevops.io')
.modalFooterOKClick()

@ -28,7 +28,7 @@ module.exports = {
browser
.frameParent()
.clickLaunchIcon('udapp') // switch to Goerli
.switchEnvironment('External Http Provider')
.switchEnvironment('basic-http-provider')
.waitForElementPresent('[data-id="basic-http-provider-modal-footer-ok-react"]')
.execute(() => {
(document.querySelector('*[data-id="basic-http-providerModalDialogContainer-react"] input[data-id="modalDialogCustomPromp"]') as any).focus()

@ -183,7 +183,7 @@ module.exports = {
},
'Should select another provider #group1': async function (browser: NightwatchBrowser) {
await clickAndCheckLog(browser, 'udapp:setEnvironmentMode', null, null, { context: 'vm', fork: 'berlin' })
await clickAndCheckLog(browser, 'udapp:setEnvironmentMode', null, null, { context: 'vm-berlin' })
await browser
.frameParent()
.useCss()
@ -393,7 +393,7 @@ module.exports = {
.useCss()
.clickLaunchIcon('pluginManager')
.clickLaunchIcon('udapp')
.switchEnvironment('Hardhat Provider')
.switchEnvironment('hardhat-provider')
.modalFooterOKClick('hardhat-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom') // e.g Custom (1337) network
.clickLaunchIcon('localPlugin')

@ -10,7 +10,7 @@ module.exports = {
'Should switch to ganache provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('udapp')
.switchEnvironment('Ganache Provider')
.switchEnvironment('ganache-provider')
.waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.execute(() => {
(document.querySelector('*[data-id="ganache-providerModalDialogModalBody-react"] input') as any).focus()
@ -25,7 +25,7 @@ module.exports = {
},
'Should switch to ganache provider, use the default ganache URL and succeed to connect': function (browser: NightwatchBrowser) {
browser.switchEnvironment('Ganache Provider')
browser.switchEnvironment('ganache-provider')
.waitForElementVisible('*[data-id="ganache-providerModalDialogModalBody-react"]')
.modalFooterOKClick('ganache-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (')
@ -33,7 +33,7 @@ module.exports = {
'Should switch to foundry provider, set a custom URL and fail to connect': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="remixIdeIconPanel"]', 10000)
.switchEnvironment('Foundry Provider')
.switchEnvironment('foundry-provider')
.waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]')
.execute(() => {
(document.querySelector('*[data-id="foundry-providerModalDialogModalBody-react"] input') as any).focus()
@ -48,7 +48,7 @@ module.exports = {
},
'Should switch to foundry provider, use the default foundry URL and succeed to connect': function (browser: NightwatchBrowser) {
browser.switchEnvironment('Foundry Provider')
browser.switchEnvironment('foundry-provider')
.waitForElementVisible('*[data-id="foundry-providerModalDialogModalBody-react"]')
.modalFooterOKClick('foundry-provider')
.waitForElementContainsText('*[data-id="settingsNetworkEnv"]', 'Custom (')

@ -4,6 +4,8 @@ import init from '../helpers/init'
let firstProxyAddress: string
let lastProxyAddress: string
let shortenedFirstAddress: string
let shortenedLastAddress: string
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
@ -94,6 +96,7 @@ module.exports = {
browser
.getAddressAtPosition(1, (address) => {
firstProxyAddress = address
shortenedFirstAddress = address.slice(0, 5) + '...' + address.slice(address.length - 5, address.length)
})
.clickInstance(1)
.perform((done) => {
@ -143,6 +146,7 @@ module.exports = {
browser
.getAddressAtPosition(1, (address) => {
lastProxyAddress = address
shortenedLastAddress = address.slice(0, 5) + '...' + address.slice(address.length - 5, address.length)
})
.clickInstance(1)
.perform((done) => {
@ -157,7 +161,7 @@ module.exports = {
})
},
'Should upgrade contract using last deployed proxy address (MyTokenV1 to MyTokenV2) #group1': function (browser: NightwatchBrowser) {
'Should upgrade contract by selecting a previously deployed proxy address from dropdown (MyTokenV1 to MyTokenV2) #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementPresent('[data-id="deployAndRunClearInstances"]')
.click('[data-id="deployAndRunClearInstances"]')
@ -171,10 +175,12 @@ module.exports = {
.click('select.udapp_contractNames option[value=MyTokenV2]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
.click('[data-id="contractGUIUpgradeImplementationLabel"]')
.waitForElementPresent('[data-id="contractGUIProxyAddressLabel"]')
.click('[data-id="contractGUIProxyAddressLabel"]')
.waitForElementPresent('[data-id="lastDeployedERC1967Address"]')
.assert.containsText('[data-id="lastDeployedERC1967Address"]', lastProxyAddress)
.waitForElementPresent('[data-id="toggleProxyAddressDropdown"]')
.click('[data-id="toggleProxyAddressDropdown"]')
.waitForElementVisible('[data-id="proxy-dropdown-items"]')
.assert.textContains('[data-id="proxy-dropdown-items"]', shortenedFirstAddress)
.assert.textContains('[data-id="proxy-dropdown-items"]', shortenedLastAddress)
.click('[data-id="proxyAddress1"]')
.createContract('')
.waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Update Proxy')
.waitForElementVisible('[data-id="udappNotify-modal-footer-ok-react"]')
@ -212,9 +218,8 @@ module.exports = {
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyTokenV2]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
.waitForElementPresent('[data-id="contractGUIProxyAddressLabel"]')
.click('[data-id="contractGUIProxyAddressLabel"]')
.waitForElementPresent('[data-id="ERC1967AddressInput"]')
.waitForElementPresent('[data-id="toggleProxyAddressDropdown"]')
.clearValue('[data-id="ERC1967AddressInput"]')
.setValue('[data-id="ERC1967AddressInput"]', firstProxyAddress)
.createContract('')
.waitForElementContainsText('[data-id="udappNotifyModalDialogModalTitle-react"]', 'Deploy Implementation & Update Proxy')

@ -32,7 +32,7 @@ module.exports = {
'Should sign message using account key #group2': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="settingsRemixRunSignMsg"]')
.switchEnvironment('vm-berlin')
.switchEnvironment('vm-merge')
.pause(2000)
.click('*[data-id="settingsRemixRunSignMsg"]')
.pause(2000)

File diff suppressed because one or more lines are too long

@ -8,6 +8,7 @@ BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
echo "$BUILD_ID"
TEST_EXITCODE=0
yarn run ganache-cli &
npx http-server -p 9090 --cors='*' ./node_modules &
yarn run serve:production &
echo 'sharing folder: ' $PWD '/apps/remix-ide/contracts' &

@ -7,6 +7,7 @@ echo "$BUILD_ID"
TEST_EXITCODE=0
yarn run ganache-cli &
npx http-server -p 9090 --cors='*' ./node_modules &
yarn run serve:production &
npx nx serve remix-ide-e2e-src-local-plugin &

@ -3,7 +3,7 @@
var fs = require('fs')
var compiler = require('solc')
var compilerInput = require('@remix-project/remix-solidity').CompilerInput
var defaultVersion = 'soljson-v0.8.17+commit.8df45f5f.js'
var defaultVersion = 'soljson-v0.8.18+commit.87f61d96.js'
const path = require('path')
compiler.loadRemoteVersion(defaultVersion, (error, solcSnapshot) => {

@ -27,12 +27,18 @@ import { StoragePlugin } from './app/plugins/storage'
import { Layout } from './app/panels/layout'
import { NotificationPlugin } from './app/plugins/notification'
import { Blockchain } from './blockchain/blockchain.js'
import { HardhatProvider } from './app/tabs/hardhat-provider'
import { GanacheProvider } from './app/tabs/ganache-provider'
import { FoundryProvider } from './app/tabs/foundry-provider'
import { ExternalHttpProvider } from './app/tabs/external-http-provider'
import { Injected0ptimismProvider } from './app/tabs/injected-optimism-provider'
import { InjectedArbitrumOneProvider } from './app/tabs/injected-arbitrum-one-provider'
import { MergeVMProvider, LondonVMProvider, BerlinVMProvider} from './app/providers/vm-provider'
import { MainnetForkVMProvider } from './app/providers/mainnet-vm-fork-provider'
import { SepoliaForkVMProvider } from './app/providers/sepolia-vm-fork-provider'
import { GoerliForkVMProvider } from './app/providers/goerli-vm-fork-provider'
import { CustomForkVMProvider } from './app/providers/custom-vm-fork-provider'
import { HardhatProvider } from './app/providers/hardhat-provider'
import { GanacheProvider } from './app/providers/ganache-provider'
import { FoundryProvider } from './app/providers/foundry-provider'
import { ExternalHttpProvider } from './app/providers/external-http-provider'
import { BasicInjectedProvider } from './app/providers/basic-injected-provider'
import { Injected0ptimismProvider } from './app/providers/injected-optimism-provider'
import { InjectedArbitrumOneProvider } from './app/providers/injected-arbitrum-one-provider'
import { FileDecorator } from './app/plugins/file-decorator'
import { CodeFormat } from './app/plugins/code-format'
import { SolidityUmlGen } from './app/plugins/solidity-umlgen'
@ -201,12 +207,20 @@ class AppComponent {
const networkModule = new NetworkModule(blockchain)
// ----------------- represent the current selected web3 provider ----
const web3Provider = new Web3ProviderModule(blockchain)
const vmProviderCustomFork = new CustomForkVMProvider(blockchain)
const vmProviderMainnetFork = new MainnetForkVMProvider(blockchain)
const vmProviderSepoliaFork = new SepoliaForkVMProvider(blockchain)
const vmProviderGoerliFork = new GoerliForkVMProvider(blockchain)
const vmProviderMerge = new MergeVMProvider(blockchain)
const vmProviderBerlin = new BerlinVMProvider(blockchain)
const vmProviderLondon = new LondonVMProvider(blockchain)
const hardhatProvider = new HardhatProvider(blockchain)
const ganacheProvider = new GanacheProvider(blockchain)
const foundryProvider = new FoundryProvider(blockchain)
const externalHttpProvider = new ExternalHttpProvider(blockchain)
const injected0ptimismProvider = new Injected0ptimismProvider(blockchain)
const injectedArbitrumOneProvider = new InjectedArbitrumOneProvider(blockchain)
const basicInjectedProvider = new BasicInjectedProvider()
const injected0ptimismProvider = new Injected0ptimismProvider()
const injectedArbitrumOneProvider = new InjectedArbitrumOneProvider()
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
Registry.getInstance().put({
@ -266,10 +280,18 @@ class AppComponent {
fetchAndCompile,
dGitProvider,
storagePlugin,
vmProviderMerge,
vmProviderBerlin,
vmProviderLondon,
vmProviderSepoliaFork,
vmProviderGoerliFork,
vmProviderMainnetFork,
vmProviderCustomFork,
hardhatProvider,
ganacheProvider,
foundryProvider,
externalHttpProvider,
basicInjectedProvider,
injected0ptimismProvider,
injectedArbitrumOneProvider,
this.walkthroughService,

@ -35,7 +35,7 @@ export const Preload = () => {
})
}).catch(err => {
_paq.push(['trackEvent', 'Preload', 'error', err && err.message])
console.log('Error loading Remix:', err)
console.error('Error loading Remix:', err)
setError(true)
})
}

@ -13,7 +13,7 @@ const profile = {
name: 'editor',
description: 'service - editor',
version: packageJson.version,
methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel','addErrorMarker', 'clearErrorMarkers'],
methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel','addErrorMarker', 'clearErrorMarkers', 'getText'],
}
class Editor extends Plugin {
@ -280,11 +280,22 @@ class Editor extends Plugin {
/**
* Set the text in the current session, if any.
* @param {string} url Address of the text to replace.
* @param {string} text New text to be place.
*/
setText (text) {
if (this.currentFile && this.sessions[this.currentFile]) {
this.sessions[this.currentFile].setValue(text)
setText (url, text) {
if (this.sessions[url]) {
this.sessions[url].setValue(text)
}
}
/**
* Get the text in the current session, if any.
* @param {string} url Address of the content to retrieve.
*/
getText (url) {
if (this.sessions[url]) {
return this.sessions[url].getValue()
}
}

@ -1,5 +1,6 @@
'use strict'
import { saveAs } from 'file-saver'
import JSZip from 'jszip'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import Registry from '../state/registry'
@ -344,12 +345,31 @@ class FileManager extends Plugin {
}
}
async zipDir(dirPath, zip) {
const filesAndFolders = await this.readdir(dirPath)
for(let path in filesAndFolders) {
if (filesAndFolders[path].isDirectory) await this.zipDir(path, zip)
else {
path = this.normalize(path)
const content: any = await this.readFile(path)
zip.file(path, content)
}
}
}
async download(path) {
try {
const fileName = helper.extractNameFromKey(path)
path = this.normalize(path)
const content: any = await this.readFile(path)
saveAs(new Blob([content]), fileName)
const downloadFileName = helper.extractNameFromKey(path)
if (await this.isDirectory(path)) {
const zip = new JSZip()
await this.zipDir(path, zip)
const content = await zip.generateAsync({type: 'blob'})
saveAs(content, `${downloadFileName}.zip`)
} else {
path = this.normalize(path)
const content: any = await this.readFile(path)
saveAs(new Blob([content]), downloadFileName)
}
} catch (e) {
throw new Error(e)
}
@ -377,9 +397,9 @@ class FileManager extends Plugin {
/**
* Get the list of files in the directory
* @param {string} path path of the directory
* @returns {string[]} list of the file/directory name in this directory
* @returns {Object} list of the file/directory name in this directory e.g; {contracts/1_Storage.sol:{isDirectory: false}}
*/
async readdir(path) {
async readdir(path): Promise<Record<string, Record<string, boolean>>> {
try {
path = this.normalize(path)
path = this.limitPluginScope(path)
@ -775,7 +795,7 @@ class FileManager extends Plugin {
if (provider) {
try{
const content = await provider.get(currentFile)
if(content) this.editor.setText(content)
if(content) this.editor.setText(currentFile, content)
}catch(error){
console.log(error)
}

@ -26,7 +26,7 @@ export class ContractFlattener extends Plugin {
async flattenAContract(action: customAction) {
this.fileName = action.path[0]
this.call('solidity', 'compile', this.fileName)
await this.call('solidity', 'compile', this.fileName)
}
/**
@ -39,8 +39,8 @@ export class ContractFlattener extends Plugin {
const ast = data.sources
const dependencyGraph = getDependencyGraph(ast, filePath)
const sorted = dependencyGraph.isEmpty()
? [filePath]
: dependencyGraph.sort().reverse()
? [filePath]
: dependencyGraph.sort().reverse()
const sources = source.sources
const result = concatSourceFiles(sorted, sources)
await this.call('fileManager', 'writeFile', `${filePath}_flattened.sol`, result)

@ -115,7 +115,7 @@ export class RemixdHandle extends WebsocketPlugin {
// warn the user only if he/she is in the browser context
const mod: AppModal = {
id: 'remixdConnect',
title: 'Connect to localhost',
title: 'Access file system using remixd',
message: remixdDialog(),
okLabel: 'Connect',
cancelLabel: 'Cancel',

@ -3,7 +3,7 @@ import { ViewPlugin } from '@remixproject/engine-web'
import React from 'react'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { RemixUiSolidityUmlGen } from '@remix-ui/solidity-uml-gen'
import { ISolidityUmlGen } from 'libs/remix-ui/solidity-uml-gen/src/types'
import { ISolidityUmlGen, ThemeQualityType, ThemeSummary } from 'libs/remix-ui/solidity-uml-gen/src/types'
import { RemixAppManager } from 'libs/remix-ui/plugin-manager/src/types'
import { concatSourceFiles, getDependencyGraph } from 'libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities'
import { convertUmlClasses2Dot } from 'sol2uml/lib/converterClasses2Dot'
@ -22,13 +22,33 @@ const profile = {
events: [],
}
const themeCollection = [
{ themeName: 'HackerOwl', backgroundColor: '--body-bg', actualHex: '#011628'},
{ themeName: 'Cerulean', backgroundColor: '--body-bg', actualHex: '#fff'},
{ themeName: 'Cyborg', backgroundColor: '--body-bg', actualHex: '#060606'},
{ themeName: 'Dark', backgroundColor: '--body-bg', actualHex: '#222336'},
{ themeName: 'Flatly', backgroundColor: '--body-bg', actualHex: '#fff'},
{ themeName: 'Black', backgroundColor: '--body-bg', actualHex: '#1a1a1a'},
{ themeName: 'Light', backgroundColor: '--body-bg', actualHex: '#eef1f6'},
{ themeName: 'Midcentuary', backgroundColor: '--body-bg', actualHex: '#DBE2E0'},
{ themeName: 'Spacelab', backgroundColor: '--body-bg', actualHex: '#fff'},
{ themeName: 'Candy', backgroundColor: '--body-bg', actualHex: '#d5efff'},
]
/**
* add context menu which will offer download as pdf and download png.
* add menu under the first download button to download
*/
export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
element: HTMLDivElement
currentFile: string
svgPayload: string
updatedSvg: string
currentlySelectedTheme: string
themeName: string
loading: boolean
themeCollection: ThemeSummary[]
appManager: RemixAppManager
dispatch: React.Dispatch<any> = () => {}
@ -39,14 +59,18 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
this.updatedSvg = ''
this.loading = false
this.currentlySelectedTheme = ''
this.themeName = ''
this.themeCollection = themeCollection
this.appManager = appManager
this.element = document.createElement('div')
this.element.setAttribute('id', 'sol-uml-gen')
}
onActivation(): void {
if (this.currentFile.length < 1)
this.on('solidity', 'compilationFinished', async (file, source, languageVersion, data, input, version) => {
const currentTheme: ThemeQualityType = await this.call('theme', 'currentTheme')
this.currentlySelectedTheme = currentTheme.quality
this.themeName = currentTheme.name
let result = ''
try {
if (data.sources && Object.keys(data.sources).length > 1) { // we should flatten first as there are multiple asts
@ -56,12 +80,11 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
const umlClasses = convertAST2UmlClasses(ast, this.currentFile)
const umlDot = convertUmlClasses2Dot(umlClasses)
const payload = vizRenderStringSync(umlDot)
const currentTheme = await this.call('theme', 'currentTheme')
this.currentlySelectedTheme = currentTheme.quality
this.updatedSvg = payload
this.renderComponent()
await this.call('tabs', 'focus', 'solidityumlgen')
} catch (error) {
console.log({ error })
console.log('error', error)
}
})
this.on('theme', 'themeChanged', (theme) => {
@ -72,13 +95,16 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
async mangleSvgPayload(svgPayload: string) : Promise<string> {
const parser = new DOMParser()
const themeQuality = await this.call('theme', 'currentTheme')
const themeQuality: ThemeQualityType = await this.call('theme', 'currentTheme')
const parsedDocument = parser.parseFromString(svgPayload, 'image/svg+xml')
const res = parsedDocument.documentElement
parsedDocument.bgColor = '#cccabc'
res.style.filter = themeQuality.quality === 'dark' ? 'invert(1)' : 'invert(0)'
const element = parsedDocument.getElementsByTagName('svg')
themeCollection.forEach((theme) => {
if (theme.themeName === themeQuality.name) {
parsedDocument.documentElement.setAttribute('style', `background-color: var(${themeQuality.name === theme.themeName ? theme.backgroundColor : '--body-bg'})`)
element[0].setAttribute('fill', theme.actualHex)
}
})
const stringifiedSvg = new XMLSerializer().serializeToString(parsedDocument)
console.log({ parsedDocument, themeQuality, stringifiedSvg })
return stringifiedSvg
}
@ -87,6 +113,7 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
}
generateCustomAction = async (action: customAction) => {
this.updatedSvg = this.updatedSvg.startsWith('<?xml') ? '' : this.updatedSvg
this.currentFile = action.path[0]
await this.generateUml(action.path[0])
}
@ -105,14 +132,11 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
* @returns {Promise<string>}
*/
async flattenContract (source: any, filePath: string, data: any) {
const hold = { data, source, filePath }
const ast = data.sources
const dependencyGraph = getDependencyGraph(ast, filePath)
const dependencyGraph = getDependencyGraph(data.sources, filePath)
const sorted = dependencyGraph.isEmpty()
? [filePath]
: dependencyGraph.sort().reverse()
const sources = source.sources
const result = concatSourceFiles(sorted, sources)
const result = concatSourceFiles(sorted, source.sources)
await this.call('fileManager', 'writeFile', `${filePath}_flattened.sol`, result)
return result
}
@ -143,16 +167,19 @@ export class SolidityUmlGen extends ViewPlugin implements ISolidityUmlGen {
...this,
updatedSvg: this.updatedSvg,
loading: this.loading,
themeSelected: this.currentlySelectedTheme
themeSelected: this.currentlySelectedTheme,
themeName: this.themeName,
themeCollection: this.themeCollection
})
}
updateComponent(state: any) {
return <RemixUiSolidityUmlGen
plugin={state}
updatedSvg={state.updatedSvg}
loading={state.loading}
themeSelected={state.currentlySelectedTheme}
themeName={state.themeName}
themeCollection={state.themeCollection}
/>
}
}

@ -0,0 +1,136 @@
import { Plugin } from '@remixproject/engine'
import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
export type JsonDataRequest = {
id: number,
jsonrpc: string // version
method: string,
params: Array<any>,
}
export type JsonDataResult = {
id: number,
jsonrpc: string // version
result: any
}
export type RejectRequest = (error: Error) => void
export type SuccessRequest = (data: JsonDataResult) => void
export interface IProvider {
options: { [id: string] : any }
init(): Promise<{ [id: string] : any }>
body(): JSX.Element
sendAsync (data: JsonDataRequest): Promise<JsonDataResult>
}
export abstract class AbstractProvider extends Plugin implements IProvider {
provider: ethers.providers.JsonRpcProvider
blockchain: Blockchain
defaultUrl: string
connected: boolean
nodeUrl: string
options: { [id: string] : any } = {}
constructor (profile, blockchain, defaultUrl) {
super(profile)
this.defaultUrl = defaultUrl
this.provider = null
this.connected = false
this.blockchain = blockchain
this.nodeUrl = 'http://localhost:8545'
}
abstract body(): JSX.Element
onDeactivation () {
this.provider = null
}
async init () {
this.nodeUrl = await ((): Promise<string> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
id: this.profile.name,
title: this.profile.displayName,
message: this.body(),
modalType: ModalTypes.prompt,
okLabel: 'OK',
cancelLabel: 'Cancel',
validationFn: (value) => {
if (!value) return { valid: false, message: "value is empty" }
if (value.startsWith('https://') || value.startsWith('http://')) {
return {
valid: true,
message: ''
}
} else {
return {
valid: false,
message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
}
}
},
okFn: (value: string) => {
setTimeout(() => resolve(value), 0)
},
cancelFn: () => {
setTimeout(() => reject(new Error('Canceled')), 0)
},
hideFn: () => {
setTimeout(() => reject(new Error('Hide')), 0)
},
defaultValue: this.defaultUrl
}
this.call('notification', 'modal', modalContent)
})
})()
this.provider = new ethers.providers.JsonRpcProvider(this.nodeUrl)
return {
nodeUrl: this.nodeUrl
}
}
sendAsync (data: JsonDataRequest): Promise<JsonDataResult> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
if (!this.provider) return reject(new Error('provider node set'))
this.sendAsyncInternal(data, resolve, reject)
})
}
private async switchAway (showError) {
if (!this.provider) return
this.provider = null
this.connected = false
if (showError) {
const modalContent: AlertModal = {
id: this.profile.name,
title: this.profile.displayName,
message: `Error while connecting to the provider, provider not connected`,
}
this.call('notification', 'alert', modalContent)
}
await this.call('udapp', 'setEnvironmentMode', { context: 'vm-merge'})
return
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
if (this.provider) {
try {
const result = await this.provider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result, id: data.id })
} catch (error) {
if (error && error.message && error.message.includes('net_version') && error.message.includes('SERVER_ERROR')) {
this.switchAway(true)
}
reject(error)
}
} else {
const result = data.method === 'net_listening' ? 'canceled' : []
resolve({ jsonrpc: '2.0', result: result, id: data.id })
}
}
}

@ -0,0 +1,18 @@
import * as packageJson from '../../../../../package.json'
import { InjectedProvider } from './injected-provider'
const profile = {
name: 'injected',
displayName: 'Injected Provider',
kind: 'provider',
description: 'injected Provider',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class BasicInjectedProvider extends InjectedProvider {
constructor () {
super(profile)
}
}

@ -0,0 +1,106 @@
import React, { useRef } from 'react' // eslint-disable-line
import * as packageJson from '../../../../../package.json'
import { AppModal, ModalTypes } from '@remix-ui/app'
import { BasicVMProvider } from './vm-provider'
import { Hardfork } from '@ethereumjs/common'
export class CustomForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
inputs: any
constructor (blockchain) {
super({
name: 'vm-custom-fork',
displayName: 'Custom fork - Remix VM',
kind: 'provider',
description: 'Custom fork - Remix VM',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
this.blockchain = blockchain
this.fork = ''
this.nodeUrl = ''
this.blockNumber = 'latest'
this.inputs = {}
}
async init () {
const body = () => {
return <div>
<span>Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state.</span>
<div>
<label className="mt-3 mb-1">Node URL</label>
<input data-id="CustomForkNodeUrl" name="nodeUrl" type="text" className="border form-control border-right-0" />
</div>
<div>
<label className="mt-3 mb-1">Block number (or "latest")</label>
<input data-id="CustomForkBlockNumber" name="blockNumber" type="text" defaultValue="latest" placeholder='block number or "latest"' className="border form-control border-right-0" />
</div>
<div>
<label className="mt-3 mb-1">EVM</label>
<select data-id="CustomForkEvmType" name="evmType" className="border form-control border-right-0">
{Object.keys(Hardfork).map((value, index) => {
return <option value={Hardfork[value]} key={index}>{value}</option>
})}
</select>
</div>
</div>
}
const result = await ((): Promise<any> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
id: this.profile.name,
title: this.profile.displayName,
message: body(),
validationFn: (data: any) => {
if(data.nodeUrl !== '' && !data.nodeUrl.startsWith("http")) {
return {
valid: false,
message: 'node URL should be a valid URL'
}
}
if (data.blockNumber !== 'latest' && isNaN(data.blockNumber)) {
return {
valid: false,
message: 'blockNumber should be a number or "latest"'
}
}
return {
valid: true,
message: ''
}
},
modalType: ModalTypes.form,
okLabel: 'Connect',
cancelLabel: 'Cancel',
okFn: (value: string) => {
setTimeout(() => resolve(value), 0)
},
cancelFn: () => {
setTimeout(() => reject(new Error('Canceled')), 0)
},
hideFn: () => {
setTimeout(() => reject(new Error('Hide')), 0)
}
}
return this.call('notification', 'modal', modalContent)
})
})()
this.fork = result.evmType
this.nodeUrl = result.nodeUrl
if (this.nodeUrl) {
const block = result.blockNumber
this.blockNumber = block === 'latest' ? 'latest' : parseInt(block)
} else {
this.nodeUrl = undefined
this.blockNumber = undefined
}
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
}
}
}

@ -7,7 +7,7 @@ const profile = {
displayName: 'External Http Provider',
kind: 'provider',
description: 'External Http Provider',
methods: ['sendAsync'],
methods: ['sendAsync', 'init'],
version: packageJson.version
}

@ -7,7 +7,7 @@ const profile = {
displayName: 'Foundry Provider',
kind: 'provider',
description: 'Foundry Anvil provider',
methods: ['sendAsync'],
methods: ['sendAsync', 'init'],
version: packageJson.version
}

@ -1,9 +1,5 @@
import * as packageJson from '../../../../../package.json'
import { Plugin } from '@remixproject/engine'
import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import React from 'react' // eslint-disable-line
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
import { AbstractProvider } from './abstract-provider'
const profile = {
@ -11,7 +7,7 @@ const profile = {
displayName: 'Ganache Provider',
kind: 'provider',
description: 'Truffle Ganache provider',
methods: ['sendAsync'],
methods: ['sendAsync', 'init'],
version: packageJson.version
}

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

@ -1,9 +1,5 @@
import * as packageJson from '../../../../../package.json'
import { Plugin } from '@remixproject/engine'
import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import React from 'react' // eslint-disable-line
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
import { AbstractProvider } from './abstract-provider'
const profile = {
@ -11,7 +7,7 @@ const profile = {
displayName: 'Hardhat Provider',
kind: 'provider',
description: 'Hardhat provider',
methods: ['sendAsync'],
methods: ['sendAsync', 'init'],
version: packageJson.version
}

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

@ -1,21 +1,18 @@
import * as packageJson from '../../../../../package.json'
import { InjectedProvider } from './injected-provider'
import { InjectedL2Provider } from './injected-L2-provider'
const profile = {
name: 'injected-arbitrum-one-provider',
displayName: 'Injected Arbitrum One Provider',
kind: 'provider',
description: 'injected Arbitrum One Provider',
methods: ['sendAsync'],
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class InjectedArbitrumOneProvider extends InjectedProvider {
export class InjectedArbitrumOneProvider extends InjectedL2Provider {
constructor () {
super(profile)
this.chainName = 'Arbitrum One'
this.chainId = '0xa4b1'
this.rpcUrls = ['https://arb1.arbitrum.io/rpc']
super(profile, 'Arbitrum One', '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
}
}

@ -1,21 +1,18 @@
import * as packageJson from '../../../../../package.json'
import { InjectedProvider } from './injected-provider'
import { InjectedL2Provider } from './injected-L2-provider'
const profile = {
name: 'injected-optimism-provider',
displayName: 'Injected Optimism Provider',
kind: 'provider',
description: 'injected Optimism Provider',
methods: ['sendAsync'],
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class Injected0ptimismProvider extends InjectedProvider {
export class Injected0ptimismProvider extends InjectedL2Provider {
constructor () {
super(profile)
this.chainName = 'Optimism'
this.chainId = '0xa'
this.rpcUrls = ['https://mainnet.optimism.io']
super(profile, 'Optimism', '0xa', ['https://mainnet.optimism.io'])
}
}

@ -1,13 +1,15 @@
/* global ethereum */
import React from 'react' // eslint-disable-line
import { Plugin } from '@remixproject/engine'
import { JsonDataRequest, RejectRequest, SuccessRequest } from './abstract-provider'
import { ethers } from 'ethers'
import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider'
import Web3 from 'web3'
import { IProvider } from './abstract-provider'
export class InjectedProvider extends Plugin {
const noInjectedProviderMsg = 'No injected provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).'
export class InjectedProvider extends Plugin implements IProvider {
provider: any
chainName: string
chainId: string
rpcUrls: Array<string>
options: { [id: string] : any } = {}
constructor (profile) {
super(profile)
@ -16,6 +18,33 @@ export class InjectedProvider extends Plugin {
}
}
askPermission (throwIfNoInjectedProvider) {
if ((typeof (window as any).ethereum) !== "undefined" && (typeof (window as any).ethereum.request) === "function") {
(window as any).ethereum.request({ method: "eth_requestAccounts" })
} else if (throwIfNoInjectedProvider) {
throw new Error(noInjectedProviderMsg)
}
}
body (): JSX.Element {
return (
<div></div>
)
}
async init () {
const injectedProvider = (window as any).ethereum
if (injectedProvider === undefined) {
throw new Error(noInjectedProviderMsg)
} else {
if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) {
if (!await injectedProvider._metamask.isUnlocked()) throw new Error('Please make sure the injected provider is unlocked (e.g Metamask).')
}
this.askPermission(true)
}
return {}
}
sendAsync (data: JsonDataRequest): Promise<any> {
return new Promise((resolve, reject) => {
this.sendAsyncInternal(data, resolve, reject)
@ -32,7 +61,6 @@ export class InjectedProvider extends Plugin {
try {
if ((window as any) && typeof (window as any).ethereum.request === "function") (window as any).ethereum.request({ method: "eth_requestAccounts" });
if (!await (window as any).ethereum._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
await addL2Network(this.chainName, this.chainId, this.rpcUrls)
const resultData = await this.provider.currentProvider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result: resultData.result, id: data.id })
} catch (error) {
@ -40,36 +68,3 @@ export class InjectedProvider extends Plugin {
}
}
}
export const addL2Network = async (chainName: string, chainId: string, rpcUrls: Array<string>) => {
try {
await (window as any).ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: chainId }],
});
} catch (switchError) {
// This error code indicates that the chain has not been added to MetaMask.
if (switchError.code === 4902) {
try {
await (window as any).ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: chainId,
chainName: chainName,
rpcUrls: rpcUrls,
},
],
});
await (window as any).ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: chainId }],
});
} catch (addError) {
// handle "add" error
}
}
// handle other "switch" errors
}
}

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

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

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

@ -1,157 +0,0 @@
import { Plugin } from '@remixproject/engine'
import { AppModal, AlertModal, ModalTypes } from '@remix-ui/app'
import { Blockchain } from '../../blockchain/blockchain'
import { ethers } from 'ethers'
export type JsonDataRequest = {
id: number,
jsonrpc: string // version
method: string,
params: Array<any>,
}
export type JsonDataResult = {
id: number,
jsonrpc: string // version
result: any
}
export type RejectRequest = (error: Error) => void
export type SuccessRequest = (data: JsonDataResult) => void
export abstract class AbstractProvider extends Plugin {
provider: ethers.providers.JsonRpcProvider
blocked: boolean
blockchain: Blockchain
defaultUrl: string
connected: boolean
constructor (profile, blockchain, defaultUrl) {
super(profile)
this.defaultUrl = defaultUrl
this.provider = null
this.blocked = false // used to block any call when trying to recover after a failed connection.
this.connected = false
this.blockchain = blockchain
}
abstract body(): JSX.Element
onDeactivation () {
this.provider = null
this.blocked = false
}
sendAsync (data: JsonDataRequest): Promise<any> {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
if (this.blocked) return reject(new Error('provider unable to connect'))
// If provider is not set, allow to open modal only when provider is trying to connect
if (!this.provider) {
let value: string
try {
value = await ((): Promise<string> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
id: this.profile.name,
title: this.profile.displayName,
message: this.body(),
modalType: ModalTypes.prompt,
okLabel: 'OK',
cancelLabel: 'Cancel',
validationFn: (value) => {
if (!value) return { valid: false, message: "value is empty" }
if (value.startsWith('https://') || value.startsWith('http://')) {
return {
valid: true,
message: ''
}
} else {
return {
valid: false,
message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )'
}
}
},
okFn: (value: string) => {
setTimeout(() => resolve(value), 0)
},
cancelFn: () => {
setTimeout(() => reject(new Error('Canceled')), 0)
},
hideFn: () => {
setTimeout(() => reject(new Error('Hide')), 0)
},
defaultValue: this.defaultUrl
}
this.call('notification', 'modal', modalContent)
})
})()
} catch (e) {
// the modal has been canceled/hide
const result = data.method === 'net_listening' ? 'canceled' : []
resolve({ jsonrpc: '2.0', result: result, id: data.id })
this.switchAway(false)
return
}
this.provider = new ethers.providers.JsonRpcProvider(value)
try {
setTimeout(() => {
if (!this.connected) {
this.switchAway(true)
reject('Unable to connect')
}
}, 2000)
await this.provider.detectNetwork() // this throws if the network cannot be detected
this.connected = true
} catch (e) {
this.switchAway(true)
reject('Unable to connect')
return
}
this.sendAsyncInternal(data, resolve, reject)
} else {
this.sendAsyncInternal(data, resolve, reject)
}
})
}
private async switchAway (showError) {
if (!this.provider) return
this.provider = null
this.blocked = true
this.connected = false
if (showError) {
const modalContent: AlertModal = {
id: this.profile.name,
title: this.profile.displayName,
message: `Error while connecting to the provider, provider not connected`,
}
this.call('notification', 'alert', modalContent)
}
await this.call('udapp', 'setEnvironmentMode', { context: 'vm', fork: 'london' })
setTimeout(_ => { this.blocked = false }, 1000) // we wait 1 second for letting remix to switch to vm
return
}
private async sendAsyncInternal (data: JsonDataRequest, resolve: SuccessRequest, reject: RejectRequest): Promise<void> {
if (this.provider) {
// Check the case where current environment is VM on UI and it still sends RPC requests
// This will be displayed on UI tooltip as 'cannot get account list: Environment Updated !!'
if (this.blockchain.getProvider() !== this.profile.displayName && data.method !== 'net_listening') return reject(new Error('Environment Updated !!'))
try {
const result = await this.provider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result, id: data.id })
} catch (error) {
if (error && error.message && error.message.includes('net_version') && error.message.includes('SERVER_ERROR')) {
this.switchAway(true)
}
reject(error)
}
} else {
const result = data.method === 'net_listening' ? 'canceled' : []
resolve({ jsonrpc: '2.0', result: result, id: data.id })
}
}
}

@ -45,7 +45,7 @@
"home.remixTwitterProfile": "Remix Twitter Profile",
"home.remixLinkedinProfile": "Remix Linkedin Profile",
"home.remixMediumPosts": "Remix Medium Posts",
"home.remixGitterChannel": "Remix Gitter Channel",
"home.remixGitterChannel": "Join us on Discord",
"home.nativeIDE": "The Native IDE for Web3 Development.",
"home.website": "Website",
"home.documentation": "Documentation",
@ -55,7 +55,7 @@
"home.files": "Files",
"home.newFile": "New File",
"home.openFile": "Open File",
"home.connectToLocalhost": "Connect to Localhost",
"home.loadFrom": "LOAD FROM",
"home.connectToLocalhost": "Access File System",
"home.loadFrom": "Load from",
"home.resources": "Resources"
}

@ -43,7 +43,7 @@
"home.remixTwitterProfile": "Remix 推特档案",
"home.remixLinkedinProfile": "Remix 领英档案",
"home.remixMediumPosts": "Remix Medium 文章",
"home.remixGitterChannel": "Remix Gitter 频道",
"home.remixGitterChannel": "Join us on Discord",
"home.nativeIDE": "用于 Web3 开发的原生 IDE。",
"home.website": "网站",
"home.documentation": "文档",

@ -1,12 +1,13 @@
var async = require('async')
var ethutil = require('ethereumjs-util')
var remixLib = require('@remix-project/remix-lib')
import { bufferToHex } from '@ethereumjs/util'
import { hash } from '@remix-project/remix-lib'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../.././../../package.json'
var EventManager = remixLib.EventManager
var format = remixLib.execution.txFormat
var txHelper = remixLib.execution.txHelper
const helper = require('../../../../lib/helper')
import { addressToString } from '@remix-ui/helper'
const _paq = window._paq = window._paq || [] //eslint-disable-line
@ -42,7 +43,7 @@ class Recorder extends Plugin {
}
if (!to) {
var abi = payLoad.contractABI
var keccak = ethutil.bufferToHex(ethutil.keccakFromString(JSON.stringify(abi)))
var keccak = bufferToHex(hash.keccakFromString(JSON.stringify(abi)))
record.abi = keccak
record.contractName = payLoad.contractName
record.bytecode = payLoad.contractBytecode
@ -82,7 +83,7 @@ class Recorder extends Plugin {
if (call) return
const rawAddress = txResult.receipt.contractAddress
if (!rawAddress) return // not a contract creation
const address = helper.addressToString(rawAddress)
const address = addressToString(rawAddress)
// save back created addresses for the convertion from tokens to real adresses
this.data._createdContracts[address] = timestamp
this.data._createdContractsReverse[timestamp] = address
@ -207,7 +208,7 @@ class Recorder extends Plugin {
// resolve the bytecode and ABI using the contract name, this ensure getting the last compiled one.
const data = await this.call('compilerArtefacts', 'getArtefactsByContractName', tx.record.contractName)
tx.record.bytecode = data.artefact.evm.bytecode.object
const updatedABIKeccak = ethutil.bufferToHex(ethutil.keccakFromString(JSON.stringify(data.artefact.abi)))
const updatedABIKeccak = bufferToHex(hash.keccakFromString(JSON.stringify(data.artefact.abi)))
abis[updatedABIKeccak] = data.artefact.abi
tx.record.abi = updatedABIKeccak
}
@ -277,7 +278,7 @@ class Recorder extends Plugin {
return logCallBack(err + '. Execution failed at ' + index)
}
if (rawAddress) {
const address = helper.addressToString(rawAddress)
const address = addressToString(rawAddress)
// save back created addresses for the convertion from tokens to real adresses
this.data._createdContracts[address] = tx.timestamp
this.data._createdContractsReverse[tx.timestamp] = address

@ -101,109 +101,61 @@ export class RunTab extends ViewPlugin {
async onInitDone () {
const udapp = this // eslint-disable-line
await this.call('blockchain', 'addProvider', {
name: 'Hardhat Provider',
isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('hardhat-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
const addProvider = async (name, displayName, isInjected, isVM, fork = '', dataId = '', title = '') => {
await this.call('blockchain', 'addProvider', {
options: {},
dataId,
name,
displayName,
fork,
isInjected,
isVM,
title,
init: async function () {
const options = await udapp.call(name, 'init')
if (options) {
this.options = options
if (options['fork']) this.fork = options['fork']
}
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'Ganache Provider',
isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('ganache-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'Foundry Provider',
isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('foundry-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
},
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call(name, 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'Wallet Connect',
isInjected: false,
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('walletconnect', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'External Http Provider',
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('basic-http-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
await this.call('blockchain', 'addProvider', {
name: 'Optimism Provider',
isInjected: true,
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('injected-optimism-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
})
}
await this.call('blockchain', 'addProvider', {
name: 'Arbitrum One Provider',
isInjected: true,
provider: {
async sendAsync (payload, callback) {
try {
const result = await udapp.call('injected-arbitrum-one-provider', 'sendAsync', payload)
callback(null, result)
} catch (e) {
callback(e)
}
}
}
})
// VM
const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.'
await addProvider('vm-merge', 'Remix VM (Merge)', false, true, 'merge', 'settingsVMMergeMode', titleVM)
await addProvider('vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', titleVM)
await addProvider('vm-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM)
await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'merge', 'settingsVMMainnetMode', titleVM)
await addProvider('vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'merge', 'settingsVMSepoliaMode', titleVM)
await addProvider('vm-goerli-fork', 'Remix VM - Goerli fork', false, true, 'merge', 'settingsVMGoerliMode', titleVM)
await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM)
// external provider
await addProvider('hardhat-provider', 'Hardhat Provider', false, false)
await addProvider('ganache-provider', 'Ganache Provider', false, false)
await addProvider('foundry-provider', 'Foundry Provider', false, false)
await addProvider('walletconnect', 'Wallet Connect', false, false)
await addProvider('basic-http-provider', 'External Http Provider', false, false)
// injected provider
const displayNameInjected = `Injected Provider${(window && window.ethereum && !(window.ethereum.providers && !window.ethereum.selectedProvider)) ?
window.ethereum.isCoinbaseWallet || window.ethereum.selectedProvider?.isCoinbaseWallet ? ' - Coinbase' :
window.ethereum.isBraveWallet || window.ethereum.selectedProvider?.isBraveWallet ? ' - Brave' :
window.ethereum.isMetaMask || window.ethereum.selectedProvider?.isMetaMask ? ' - MetaMask' : '' : ''}`
await addProvider('injected', displayNameInjected, true, false)
await addProvider('injected-optimism-provider', 'Optimism Provider', true, false)
await addProvider('injected-arbitrum-one-provider', 'Arbitrum One Provider', true, false)
}
writeFile (fileName, content) {

@ -32,13 +32,6 @@ if (domains[window.location.hostname]) {
})()
}
createScriptTag = function (url, type) {
var script = document.createElement('script');
script.src = url;
script.type = type;
document.getElementsByTagName('head')[0].appendChild(script);
};
function isElectron() {
// Renderer process
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
@ -63,11 +56,5 @@ fetch(versionUrl, { cache: "no-store" }).then(response => {
response.text().then(function (data) {
const version = JSON.parse(data);
console.log(`Loading Remix ${version.version}`);
createScriptTag(`polyfills.${version.version}.${version.timestamp}.js`, 'module');
if (version.mode === 'development') {
createScriptTag(`vendor.${version.version}.${version.timestamp}.js`, 'module');
createScriptTag(`runtime.${version.version}.${version.timestamp}.js`, 'module');
}
createScriptTag(`main.${version.version}.${version.timestamp}.js`, 'text/javascript');
});
});

@ -2,7 +2,7 @@
import React from 'react' // eslint-disable-line
import Web3 from 'web3'
import { Plugin } from '@remixproject/engine'
import { toBuffer, addHexPrefix } from 'ethereumjs-util'
import { toBuffer, addHexPrefix } from '@ethereumjs/util'
import { EventEmitter } from 'events'
import { format } from 'util'
import { ExecutionContext } from './execution-context'
@ -82,14 +82,16 @@ export class Blockchain extends Plugin {
}
setupProviders () {
const vmProvider = new VMProvider(this.executionContext)
this.providers = {}
this.providers.vm = new VMProvider(this.executionContext)
this.providers['vm'] = vmProvider
this.providers.injected = new InjectedProvider(this.executionContext)
this.providers.web3 = new NodeProvider(this.executionContext, this.config)
}
getCurrentProvider () {
const provider = this.getProvider()
if (provider && provider.startsWith('vm')) return this.providers['vm']
if (this.providers[provider]) return this.providers[provider]
return this.providers.web3 // default to the common type of provider
}
@ -178,9 +180,8 @@ export class Blockchain extends Plugin {
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment failed: ' + error])
return this.call('terminal', 'logHtml', log)
}
if (networkInfo.name === 'VM') this.config.set('vm/proxy', address)
else this.config.set(`${networkInfo.name}/${networkInfo.currentFork}/${networkInfo.id}/proxy`, address)
await this.saveDeployedContractStorageLayout(implementationContractObject, address, networkInfo)
this.events.emit('newProxyDeployment', address, new Date().toISOString(), implementationContractObject.contractName)
_paq.push(['trackEvent', 'blockchain', 'Deploy With Proxy', 'Proxy deployment successful'])
this.call('udapp', 'addInstance', addressToString(address), implementationContractObject.abi, implementationContractObject.name)
}
@ -234,23 +235,40 @@ export class Blockchain extends Plugin {
}
async saveDeployedContractStorageLayout (contractObject, proxyAddress, networkInfo) {
const { contractName, implementationAddress, contract } = contractObject
const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`)
const { contractName, implementationAddress } = contractObject
const networkName = networkInfo.name === 'custom' ? networkInfo.name + '-' + networkInfo.id : networkInfo.name
const hasPreviousDeploys = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`)
// TODO: make deploys folder read only.
if (hasPreviousDeploys) {
const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`)
const deployments = await this.call('fileManager', 'readFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`)
const parsedDeployments = JSON.parse(deployments)
const proxyDeployment = parsedDeployments.deployments[proxyAddress]
if (proxyDeployment) {
const oldImplementationAddress = proxyDeployment.implementationAddress
const hasPreviousBuild = await this.call('fileManager', 'exists', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`)
if (hasPreviousBuild) await this.call('fileManager', 'remove', `.deploys/upgradeable-contracts/${networkName}/solc-${oldImplementationAddress}.json`)
}
parsedDeployments.deployments[proxyAddress] = {
date: new Date().toISOString(),
contractName: contractName,
fork: networkInfo.currentFork,
implementationAddress: implementationAddress,
layout: contract.object.storageLayout
solcOutput: contractObject.compiler.data,
solcInput: contractObject.compiler.source
}
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2))
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({
solcInput: contractObject.compiler.source,
solcOutput: contractObject.compiler.data
}, null, 2))
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify(parsedDeployments, null, 2))
} else {
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkInfo.name}/UUPS.json`, JSON.stringify({
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/solc-${implementationAddress}.json`, JSON.stringify({
solcInput: contractObject.compiler.source,
solcOutput: contractObject.compiler.data
}, null, 2))
await this.call('fileManager', 'writeFile', `.deploys/upgradeable-contracts/${networkName}/UUPS.json`, JSON.stringify({
id: networkInfo.id,
network: networkInfo.name,
deployments: {
@ -258,8 +276,7 @@ export class Blockchain extends Plugin {
date: new Date().toISOString(),
contractName: contractName,
fork: networkInfo.currentFork,
implementationAddress: implementationAddress,
layout: contract.object.storageLayout
implementationAddress: implementationAddress
}
}
}, null, 2))
@ -364,10 +381,6 @@ export class Blockchain extends Plugin {
return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb)
}
setProviderFromEndpoint (target, context, cb) {
return this.executionContext.setProviderFromEndpoint(target, context, cb)
}
detectNetwork (cb) {
return this.executionContext.detectNetwork(cb)
}
@ -389,7 +402,7 @@ export class Blockchain extends Plugin {
}
isWeb3Provider () {
const isVM = this.getProvider() === 'vm'
const isVM = this.executionContext.isVM()
const isInjected = this.getProvider() === 'injected'
return (!isVM && !isInjected)
}
@ -408,7 +421,7 @@ export class Blockchain extends Plugin {
web3 () {
// @todo(https://github.com/ethereum/remix-project/issues/431)
const isVM = this.getProvider() === 'vm'
const isVM = this.executionContext.isVM()
if (isVM) {
return this.providers.vm.web3
}
@ -527,7 +540,7 @@ export class Blockchain extends Plugin {
* @param {{privateKey: string, balance: string}} newAccount The new account to create
*/
createVMAccount (newAccount) {
if (this.getProvider() !== 'vm') {
if (!this.executionContext.isVM()) {
throw new Error('plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed')
}
return this.providers.vm.createVMAccount(newAccount)

@ -14,15 +14,13 @@ if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') {
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
}
const noInjectedProviderMsg = 'No injected provider found. Make sure your provider (e.g. MetaMask) is active and running (when recently activated you may have to reload the page).'
/*
trigger contextChanged, web3EndpointChanged
*/
export class ExecutionContext {
constructor () {
this.event = new EventManager()
this.executionContext = 'vm'
this.executionContext = 'vm-merge'
this.lastBlock = null
this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault
@ -37,25 +35,18 @@ export class ExecutionContext {
init (config) {
if (config.get('settings/always-use-vm')) {
this.executionContext = 'vm'
} else {
this.executionContext = injectedProvider ? 'injected' : 'vm'
if (this.executionContext === 'injected') this.askPermission(false)
}
}
askPermission (throwIfNoInjectedProvider) {
if (typeof ethereum !== "undefined" && typeof ethereum.request === "function") {
ethereum.request({ method: "eth_requestAccounts" })
} else if (throwIfNoInjectedProvider) {
throw new Error(noInjectedProviderMsg)
this.executionContext = 'vm-merge'
}
}
}
getProvider () {
return this.executionContext
}
getProviderObject () {
return this.customNetWorks[this.executionContext]
}
getSelectedAddress () {
return injectedProvider ? injectedProvider.selectedAddress : null
}
@ -65,7 +56,7 @@ export class ExecutionContext {
}
isVM () {
return this.executionContext === 'vm'
return this.executionContext.startsWith('vm')
}
setWeb3 (context, web3) {
@ -84,12 +75,6 @@ export class ExecutionContext {
if (!web3.currentProvider) {
return callback('No provider set')
}
if (web3.currentProvider.isConnected && !web3.currentProvider.isConnected()) {
if (web3.currentProvider.isMetaMask) {
this.askPermission(false)
}
return callback('Provider not connected')
}
web3.eth.net.getId((err, id) => {
let name = null
if (err) name = 'Unknown'
@ -117,7 +102,7 @@ export class ExecutionContext {
removeProvider (name) {
if (name && this.customNetWorks[name]) {
if (this.executionContext === name) this.setContext('vm', null, null, null)
if (this.executionContext === name) this.setContext('vm-merge', null, null, null)
delete this.customNetWorks[name]
this.event.trigger('removeProvider', [name])
}
@ -144,57 +129,17 @@ export class ExecutionContext {
const context = value.context
if (!cb) cb = () => { /* Do nothing. */ }
if (!confirmCb) confirmCb = () => { /* Do nothing. */ }
if (!infoCb) infoCb = () => { /* Do nothing. */ }
if (context === 'vm') {
this.executionContext = context
this.currentFork = value.fork
this.event.trigger('contextChanged', ['vm'])
return cb()
}
if (context === 'injected') {
if (injectedProvider === undefined) {
infoCb(noInjectedProviderMsg)
return cb()
} else {
if (injectedProvider && injectedProvider._metamask && injectedProvider._metamask.isUnlocked) {
if (!await injectedProvider._metamask.isUnlocked()) infoCb('Please make sure the injected provider is unlocked (e.g Metamask).')
}
try {
this.askPermission(true)
} catch (e) {
infoCb(e.message)
return cb()
}
this.executionContext = context
web3.setProvider(injectedProvider)
await this._updateChainContext()
this.event.trigger('contextChanged', ['injected'])
return cb()
}
}
if (!infoCb) infoCb = () => { /* Do nothing. */ }
if (this.customNetWorks[context]) {
var network = this.customNetWorks[context]
if (!this.customNetWorks[context].isInjected) {
this.setProviderFromEndpoint(network.provider, { context: network.name }, (error) => {
if (error) infoCb(error)
cb()
})
} else {
// injected
try {
this.askPermission(true)
} catch (e) {
infoCb(e.message)
return cb()
}
this.executionContext = context
web3.setProvider(network.provider)
await this._updateChainContext()
this.event.trigger('contextChanged', [context])
return cb()
}
var network = this.customNetWorks[context]
await network.init()
this.currentFork = network.fork
this.executionContext = context
// injected
web3.setProvider(network.provider)
await this._updateChainContext()
this.event.trigger('contextChanged', [context])
cb()
}
}
@ -208,7 +153,7 @@ export class ExecutionContext {
}
async _updateChainContext () {
if (this.getProvider() !== 'vm') {
if (!this.isVM()) {
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
@ -234,30 +179,6 @@ export class ExecutionContext {
}, 15000)
}
// TODO: remove this when this function is moved
setProviderFromEndpoint (endpoint, value, cb) {
const oldProvider = web3.currentProvider
const context = value.context
web3.setProvider(endpoint)
web3.eth.net.isListening((err, isConnected) => {
if (!err && isConnected === true) {
this.executionContext = context
this._updateChainContext()
this.event.trigger('contextChanged', [context])
this.event.trigger('web3EndpointChanged')
cb()
} else if (isConnected === 'canceled') {
web3.setProvider(oldProvider)
cb()
} else {
web3.setProvider(oldProvider)
cb(`Not possible to connect to ${context}. Make sure the provider is running, a connection is open (via IPC or RPC) or that the provider plugin is properly configured.`)
}
})
}
txDetailsLink (network, hash) {
const transactionDetailsLinks = {
Main: 'https://www.etherscan.io/tx/',

@ -1,5 +1,5 @@
const Web3 = require('web3')
const { hashPersonalMessage } = require('ethereumjs-util')
import { hashPersonalMessage } from '@ethereumjs/util'
class InjectedProvider {
constructor (executionContext) {

@ -1,5 +1,5 @@
const Web3 = require('web3')
const { hashPersonalMessage } = require('ethereumjs-util')
import { hashPersonalMessage } from '@ethereumjs/util'
const Personal = require('web3-eth-personal')
class NodeProvider {

@ -1,10 +1,12 @@
const Web3 = require('web3')
const { BN, privateToAddress, hashPersonalMessage } = require('ethereumjs-util')
import { privateToAddress, hashPersonalMessage } from '@ethereumjs/util'
import BN from 'bn.js'
const { extend } = require('@remix-project/remix-simulator')
class VMProvider {
constructor (executionContext) {
this.executionContext = executionContext
this.worker = null
this.provider = null
}
getAccounts (cb) {
@ -20,7 +22,9 @@ class VMProvider {
if (this.worker) this.worker.terminate()
this.accounts = {}
this.worker = new Worker(new URL('./worker-vm', import.meta.url))
this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork() })
const provider = this.executionContext.getProviderObject()
this.worker.postMessage({ cmd: 'init', fork: this.executionContext.getCurrentFork(), nodeUrl: provider?.options['nodeUrl'], blockNumber: provider?.options['blockNumber']})
let incr = 0
const stamps = {}
@ -29,7 +33,7 @@ class VMProvider {
stamps[msg.data.stamp](msg.data.error, msg.data.result)
}
})
const provider = {
this.provider = {
sendAsync: (query, callback) => {
const stamp = Date.now() + incr
incr++
@ -37,10 +41,10 @@ class VMProvider {
this.worker.postMessage({ cmd: 'sendAsync', query, stamp })
}
}
this.web3 = new Web3(provider)
this.web3 = new Web3(this.provider)
extend(this.web3)
this.accounts = {}
this.executionContext.setWeb3('vm', this.web3)
this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3)
}
// TODO: is still here because of the plugin API
@ -80,7 +84,7 @@ class VMProvider {
}
getProvider () {
return 'vm'
return this.executionContext.getProvider()
}
}

@ -6,7 +6,7 @@ self.onmessage = (e: MessageEvent) => {
switch (data.cmd) {
case 'init':
{
provider = new Provider({ fork: data.fork })
provider = new Provider({ fork: data.fork, nodeUrl: data.nodeUrl, blockNumber: data.blockNumber })
if (provider) provider.init()
break
}

@ -1,7 +1,7 @@
var async = require('async')
const ethJSUtil = require('ethereumjs-util')
import { toChecksumAddress } from '@ethereumjs/util'
module.exports = {
export default {
shortenAddress: function (address, etherBalance) {
var len = address.length
return address.slice(0, 5) + '...' + address.slice(len - 5, len) + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '')
@ -14,7 +14,7 @@ module.exports = {
if (address.indexOf('0x') === -1) {
address = '0x' + address
}
return ethJSUtil.toChecksumAddress(address)
return toChecksumAddress(address)
},
shortenHexData: function (data) {
if (!data) return ''

@ -10,7 +10,7 @@ const requiredModules = [ // services + layout views + system views
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy',
'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', 'injected-arbitrum-one-provider',
'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-optimism-provider', 'injected-arbitrum-one-provider',
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener']
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
@ -26,7 +26,7 @@ export function isNative(name) {
// nativePlugin allows to bypass the permission request
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting',
'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider',
'tabs', 'injected-arbitrum-one-provider']
'tabs', 'injected-arbitrum-one-provider', 'injected']
return nativePlugins.includes(name) || requiredModules.includes(name)
}

@ -15,10 +15,11 @@ export class RemixEngine extends Engine {
if (name === 'hardhat') return { queueTimeout: 60000 * 4 }
if (name === 'truffle') return { queueTimeout: 60000 * 4 }
if (name === 'localPlugin') return { queueTimeout: 60000 * 4 }
if (name === 'notification') return { queueTimeout: 60000 * 4 }
if (name === 'notification') return { queueTimeout: 60000 * 10 }
if (name === 'sourcify') return { queueTimeout: 60000 * 4 }
if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 }
if (name === 'walletconnect') return { queueTimeout: 60000 * 4 }
if (name === 'udapp') return { queueTimeout: 60000 * 4 }
return { queueTimeout: 10000 }
}

@ -9,7 +9,7 @@ import { CompileTabLogic } from '@remix-ui/solidity-compiler'
const defaultCompilerParameters = {
runs: '200',
optimize: false,
version: 'soljson-v0.8.17+commit.8df45f5f',
version: 'soljson-v0.8.18+commit.87f61d96',
evmVersion: null, // compiler default
language: 'Solidity',
useFileConfiguration: false,

@ -27,8 +27,6 @@
"@ethereum-waffle/chai": "^3.4.4",
"chai": "^4.3.7",
"ethers": "^5.7.2",
"ganache": "^7.5.0"
},
"types": "./src/index.d.ts",
"gitHead": "69af4eddd1ba47f76a71c78077d453deb572b1a0"
"@remix-project/remix-simulator": "^0.2.21"
}
}

@ -1,14 +1,10 @@
// @ts-ignore
import { ethers } from "ethers"
//@ts-ignore
import * as ganache from "ganache"
import { Provider } from '@remix-project/remix-simulator'
import { getArtifactsByContractName } from './artifacts-helper'
import { SignerWithAddress } from './signer'
const initializeProvider = () => {
//@ts-ignore
global.ganacheProvider = ganache.provider({ logging: { quiet: true } })
}
global.remixProvider = new Provider({ fork: null })
const isFactoryOptions = (signerOrOptions: any) => {
if (!signerOrOptions || signerOrOptions === undefined || signerOrOptions instanceof ethers.Signer) return false
@ -164,16 +160,16 @@ const resultToArtifact = (result: any) => {
const getContractFactory = async (contractNameOrABI: ethers.ContractInterface, bytecode?: string, signerOrOptions = null) => {
//@ts-ignore
if (!global.ganacheProvider) initializeProvider()
if (!global.remixProvider.Transactions.txRunnerInstance) await remixProvider.init()
if (bytecode && contractNameOrABI) {
//@ts-ignore
return new ethers.ContractFactory(contractNameOrABI, bytecode, signerOrOptions || (new ethers.providers.Web3Provider(ganacheProvider)).getSigner())
return new ethers.ContractFactory(contractNameOrABI, bytecode, signerOrOptions || (new ethers.providers.Web3Provider(remixProvider)).getSigner())
} else if (typeof contractNameOrABI === 'string') {
const contract = await getArtifactsByContractName(contractNameOrABI)
if (contract) {
//@ts-ignore
return new ethers.ContractFactory(contract.abi, contract.evm.bytecode.object, signerOrOptions || (new ethers.providers.Web3Provider(ganacheProvider)).getSigner())
return new ethers.ContractFactory(contract.abi, contract.evm.bytecode.object, signerOrOptions || (new ethers.providers.Web3Provider(remixProvider)).getSigner())
} else {
throw new Error('Contract artifacts not found')
}
@ -184,9 +180,9 @@ const getContractFactory = async (contractNameOrABI: ethers.ContractInterface, b
const getContractAt = async (contractNameOrABI: ethers.ContractInterface, address: string, signer = null) => {
//@ts-ignore
if (!global.ganacheProvider) initializeProvider()
if (!global.remixProvider.Transactions.txRunnerInstance) await remixProvider.init()
//@ts-ignore
const provider = new ethers.providers.Web3Provider(ganacheProvider)
const provider = new ethers.providers.Web3Provider(remixProvider)
if(typeof contractNameOrABI === 'string') {
const result = await getArtifactsByContractName(contractNameOrABI)
@ -203,9 +199,9 @@ const getContractAt = async (contractNameOrABI: ethers.ContractInterface, addres
const getSigner = async (address: string) => {
//@ts-ignore
if (!global.ganacheProvider) initializeProvider()
if (!global.remixProvider.Transactions.txRunnerInstance) await remixProvider.init()
//@ts-ignore
const provider = new ethers.providers.Web3Provider(ganacheProvider)
const provider = new ethers.providers.Web3Provider(remixProvider)
const signer = provider.getSigner(address)
return SignerWithAddress.create(signer)
@ -213,9 +209,9 @@ const getSigner = async (address: string) => {
const getSigners = async () => {
//@ts-ignore
if (!global.ganacheProvider) initializeProvider()
if (!global.remixProvider.Transactions.txRunnerInstance) await remixProvider.init()
//@ts-ignore
const provider = new ethers.providers.Web3Provider(ganacheProvider)
const provider = new ethers.providers.Web3Provider(remixProvider)
const accounts = await provider.listAccounts()
return await Promise.all( accounts.map((account: any) => getSigner(account)))
@ -223,7 +219,7 @@ const getSigners = async () => {
const getContractFactoryFromArtifact = async (artifact: any, signerOrOptions: { signer: any, libraries: any }) => {
//@ts-ignore
if (!global.ganacheProvider) initializeProvider()
if (!global.remixProvider.Transactions.txRunnerInstance) await remixProvider.init()
let libraries = {}
let signer
@ -249,7 +245,7 @@ If you want to call a contract using ${artifact.contractName} as its interface u
const linkedBytecode = await collectLibrariesAndLink(artifact, libraries)
//@ts-ignore
return new ethers.ContractFactory(artifact.abi, linkedBytecode || artifact.bytecode, signer || (new ethers.providers.Web3Provider(ganacheProvider)).getSigner())
return new ethers.ContractFactory(artifact.abi, linkedBytecode || artifact.bytecode, signer || (new ethers.providers.Web3Provider(remixProvider)).getSigner())
}
const getContractAtFromArtifact = async (artifact: any, address: string, signerOrOptions = null) => {

@ -21,13 +21,13 @@
}
],
"dependencies": {
"@ethereumjs/block": "^4.0.0",
"@ethereumjs/tx": "^4.0.0",
"@ethereumjs/vm": "^6.0.0",
"@ethereumjs/block": "^4.1.0",
"@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-astwalker": "^0.0.53",
"@remix-project/remix-lib": "^0.5.23",
"async": "^2.6.2",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
"string-similarity": "^4.0.4",

@ -33,13 +33,13 @@
]
},
"dependencies": {
"@ethereumjs/block": "^4.0.0",
"@ethereumjs/tx": "^4.0.0",
"@ethereumjs/vm": "^6.0.0",
"@ethereumjs/block": "^4.1.0",
"@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23",
"@types/tape": "^4.2.33",
"async": "^2.6.2",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
"nyc": "^13.3.0",

@ -1,7 +1,7 @@
import { Plugin } from '@remixproject/engine'
import { compile } from '@remix-project/remix-solidity'
import { util } from '@remix-project/remix-lib'
import { toChecksumAddress } from 'ethereumjs-util'
import { toChecksumAddress } from '@ethereumjs/util'
import { fetchContractFromEtherscan } from './helpers/fetch-etherscan'
import { fetchContractFromSourcify } from './helpers/fetch-sourcify'
import { UUPSDeployedByteCode, UUPSCompilerVersion, UUPSOptimize, UUPSRuns, UUPSEvmVersion, UUPSLanguage } from './constants/uups'

@ -1,3 +1,4 @@
import { SolcInput, SolcOutput } from "@openzeppelin/upgrades-core"
export interface FuncABI {
name: string,
type: string,
@ -183,3 +184,21 @@ export interface ContractSources {
}
}
}
export interface NetworkDeploymentFile {
id: string,
network: string,
deployments: {
[proxyAddress: string]: {
date: Date,
contractName: string,
fork: string,
implementationAddress: string
}
}[]
}
export interface SolcBuildFile {
solcInput: SolcInput,
solcOutput: SolcOutput
}

@ -21,10 +21,11 @@
"test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts"
},
"dependencies": {
"@ethereumjs/block": "^4.0.0",
"@ethereumjs/common": "^3.0.0",
"@ethereumjs/tx": "^4.0.0",
"@ethereumjs/vm": "^6.0.0",
"@ethereumjs/block": "^4.1.0",
"@ethereumjs/common": "^3.0.2",
"@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-astwalker": "^0.0.53",
"@remix-project/remix-lib": "^0.5.23",
"@remix-project/remix-simulator": "^0.2.23",
@ -34,7 +35,6 @@
"color-support": "^1.1.3",
"commander": "^9.4.1",
"deep-equal": "^1.0.1",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
"express-ws": "^4.0.0",

@ -2,7 +2,7 @@
import { parseCode } from './codeUtils'
import { util } from '@remix-project/remix-lib'
import { bufferToHex } from 'ethereumjs-util'
import { bufferToHex } from '@ethereumjs/util'
function createExpressions (instructions) {
const expressions = []

@ -1,7 +1,6 @@
'use strict'
import { add, toBN, extractHexValue } from './util'
import { util } from '@remix-project/remix-lib'
import { BN } from 'ethereumjs-util'
import { RefType } from './RefType'
const sha3256 = util.sha3_256
@ -48,7 +47,7 @@ export class ArrayType extends RefType {
size = toBN('0x' + slotValue)
currentLocation.slot = sha3256(location.slot)
} else {
size = new BN(this.arraySize)
size = toBN(this.arraySize)
}
const k = toBN(0)
for (; k.lt(size) && k.ltn(300); k.iaddn(1)) {

@ -1,7 +1,7 @@
'use strict'
import { extractHexValue, readFromStorage } from './util'
import { util } from '@remix-project/remix-lib'
import { BN } from 'ethereumjs-util'
import { BN } from 'bn.js'
import { RefType } from './RefType'
const sha3256 = util.sha3_256

@ -1,7 +1,9 @@
'use strict'
import { hash } from '@remix-project/remix-lib'
import { RefType } from './RefType'
import { normalizeHex } from './util'
import { toBuffer, setLengthLeft, keccak, BN, bufferToHex, addHexPrefix } from 'ethereumjs-util'
import { toBuffer, setLengthLeft, bufferToHex, addHexPrefix } from '@ethereumjs/util'
import BN from 'bn.js'
export class Mapping extends RefType {
keyType
@ -68,7 +70,7 @@ function getMappingLocation (key, position) {
let mappingP = toBuffer(addHexPrefix(position))
mappingP = setLengthLeft(mappingP, 32)
const mappingKeyBuf = concatTypedArrays(mappingK, mappingP)
const mappingStorageLocation: Buffer = keccak(mappingKeyBuf)
const mappingStorageLocation: Buffer = hash.keccak(mappingKeyBuf)
const mappingStorageLocationinBn: BN = new BN(mappingStorageLocation, 16)
return mappingStorageLocationinBn
}

@ -1,5 +1,6 @@
'use strict'
import { BN, bufferToHex, unpadHexString } from 'ethereumjs-util'
import { bufferToHex, unpadHexString } from '@ethereumjs/util'
import BN from 'bn.js'
export function decodeIntFromHex (value, byteLength, signed) {
let bigNumber = new BN(value, 16)
@ -58,6 +59,7 @@ export function toBN (value) {
return value
} else if (value.match && value.match(/^(0x)?([a-f0-9]*)$/)) {
value = unpadHexString(value)
value = value.replace('0x', '')
value = new BN(value === '' ? '0' : value, 16)
} else if (!isNaN(value)) {
value = new BN(value)

@ -10,7 +10,7 @@ import { InternalCallTree } from '../../../src/solidity-decoder/internalCallTree
import * as vmCall from '../../vmCall'
import { StorageResolver } from '../../../src/storage/storageResolver'
import { StorageViewer } from '../../../src/storage/storageViewer'
import { Address, bufferToHex } from 'ethereumjs-util'
import { Address, bufferToHex } from '@ethereumjs/util'
module.exports = async function testMappingStorage (st, cb) {
const mappingStorage = require('../contracts/mappingStorage')

@ -1,14 +1,7 @@
'use strict'
import { Block } from '@ethereumjs/block'
import { Transaction } from '@ethereumjs/tx'
import VM from '@ethereumjs/vm'
import { rlp, keccak, bufferToHex } from 'ethereumjs-util'
import { extendWeb3 } from '../src/init'
const utileth = require('ethereumjs-util')
const Tx = require('@ethereumjs/tx').Transaction
const BN = require('ethereumjs-util').BN
const remixLib = require('@remix-project/remix-lib')
const { Provider, extend } = require('@remix-project/remix-simulator')
import { Address } from '@ethereumjs/util'
const { Provider } = require('@remix-project/remix-simulator')
const Web3 = require('web3')
@ -25,7 +18,7 @@ async function sendTx (web3, from, to, value, data, cb) {
try {
cb = cb || (() => {})
const receipt = await web3.eth.sendTransaction({
from: utileth.Address.fromPrivateKey(from.privateKey).toString('hex'),
from: Address.fromPrivateKey(from.privateKey).toString(),
to,
value,
data,

@ -18,7 +18,7 @@
},
"dependencies": {
"async": "^2.1.2",
"ethereumjs-util": "^7.0.10",
"@ethereumjs/util": "^8.0.3",
"ethers": "^4.0.40",
"ethjs-util": "^0.1.6",
"events": "^3.0.0",

@ -3,7 +3,7 @@ import { ethers } from 'ethers'
import { encodeParams as encodeParamsHelper, encodeFunctionId, makeFullTypeDefinition } from './txHelper'
import { eachOfSeries } from 'async'
import { linkBytecode as linkBytecodeSolc } from 'solc/linker'
import { isValidAddress, addHexPrefix } from 'ethereumjs-util'
import { isValidAddress, addHexPrefix } from '@ethereumjs/util'
/**
* build the transaction data

@ -1,6 +1,6 @@
'use strict'
import { ethers } from 'ethers'
import { toBuffer, addHexPrefix } from 'ethereumjs-util'
import { toBuffer, addHexPrefix } from '@ethereumjs/util'
import { EventManager } from '../eventManager'
import { compareByteCode, getinputParameters } from '../util'
import { decodeResponse } from './txFormat'
@ -139,7 +139,7 @@ export class TxListener {
startListening () {
this.init()
this._isListening = true
if (this._listenOnNetwork && this.executionContext.getProvider() !== 'vm') {
if (this._listenOnNetwork && !this.executionContext.isVM()) {
this._startListenOnNetwork()
}
}

@ -1,8 +1,9 @@
'use strict'
import { RunBlockResult, RunTxResult } from '@ethereumjs/vm'
import { ConsensusType } from '@ethereumjs/common'
import { Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
import { Block } from '@ethereumjs/block'
import { BN, bufferToHex, Address } from 'ethereumjs-util'
import { bufferToHex, Address } from '@ethereumjs/util'
import type { Account } from '@ethereumjs/util'
import { EventManager } from '../eventManager'
import { LogsManager } from './logsManager'
@ -105,13 +106,14 @@ export class TxRunnerVM {
const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e']
const difficulties = [69762765929000, 70762765929000, 71762765929000]
const difficulty = this.commonContext.consensusType() === ConsensusType.ProofOfStake ? 0 : difficulties[self.blockNumber % difficulties.length]
const block = Block.fromBlockData({
header: {
timestamp: new Date().getTime() / 1000 | 0,
number: self.blockNumber,
coinbase: coinbases[self.blockNumber % coinbases.length],
difficulty: difficulties[self.blockNumber % difficulties.length],
difficulty,
gasLimit,
baseFeePerGas: EIP1559 ? '0x1' : undefined
},

@ -1,5 +1,6 @@
'use strict'
import { BN, bufferToHex } from 'ethereumjs-util'
import { BN } from 'bn.js'
import { bufferToHex } from '@ethereumjs/util'
export function toInt (h) {
if (h.indexOf && h.indexOf('0x') === 0) {

@ -0,0 +1,206 @@
import { keccak224, keccak384, keccak256 as k256, keccak512 } from 'ethereum-cryptography/keccak'
const createHash = require('create-hash')
import { encode, Input } from 'rlp'
import { toBuffer, setLengthLeft, isHexString } from '@ethereumjs/util'
/**
* Creates Keccak hash of a Buffer input
* @param a The input data (Buffer)
* @param bits (number = 256) The Keccak width
*/
export const keccak = function(a: Buffer, bits: number = 256): Buffer {
assertIsBuffer(a)
switch (bits) {
case 224: {
return toBuffer(keccak224(a))
}
case 256: {
return toBuffer(k256(a))
}
case 384: {
return toBuffer(keccak384(a))
}
case 512: {
return toBuffer(keccak512(a))
}
default: {
throw new Error(`Invald algorithm: keccak${bits}`)
}
}
}
/**
* Creates Keccak-256 hash of the input, alias for keccak(a, 256).
* @param a The input data (Buffer)
*/
export const keccak256 = function(a: Buffer): Buffer {
return keccak(a)
}
/**
* Creates Keccak hash of a utf-8 string input
* @param a The input data (String)
* @param bits (number = 256) The Keccak width
*/
export const keccakFromString = function(a: string, bits: number = 256) {
assertIsString(a)
const buf = Buffer.from(a, 'utf8')
return keccak(buf, bits)
}
/**
* Creates Keccak hash of an 0x-prefixed string input
* @param a The input data (String)
* @param bits (number = 256) The Keccak width
*/
export const keccakFromHexString = function(a: string, bits: number = 256) {
assertIsHexString(a)
return keccak(toBuffer(a), bits)
}
/**
* Creates Keccak hash of a number array input
* @param a The input data (number[])
* @param bits (number = 256) The Keccak width
*/
export const keccakFromArray = function(a: number[], bits: number = 256) {
assertIsArray(a)
return keccak(toBuffer(a), bits)
}
/**
* Creates SHA256 hash of an input.
* @param a The input data (Buffer|Array|String)
*/
const _sha256 = function(a: any): Buffer {
a = toBuffer(a)
return createHash('sha256')
.update(a)
.digest()
}
/**
* Creates SHA256 hash of a Buffer input.
* @param a The input data (Buffer)
*/
export const sha256 = function(a: Buffer): Buffer {
assertIsBuffer(a)
return _sha256(a)
}
/**
* Creates SHA256 hash of a string input.
* @param a The input data (string)
*/
export const sha256FromString = function(a: string): Buffer {
assertIsString(a)
return _sha256(a)
}
/**
* Creates SHA256 hash of a number[] input.
* @param a The input data (number[])
*/
export const sha256FromArray = function(a: number[]): Buffer {
assertIsArray(a)
return _sha256(a)
}
/**
* Creates RIPEMD160 hash of the input.
* @param a The input data (Buffer|Array|String|Number)
* @param padded Whether it should be padded to 256 bits or not
*/
const _ripemd160 = function(a: any, padded: boolean): Buffer {
a = toBuffer(a)
const hash = createHash('rmd160')
.update(a)
.digest()
if (padded === true) {
return setLengthLeft(hash, 32)
} else {
return hash
}
}
/**
* Creates RIPEMD160 hash of a Buffer input.
* @param a The input data (Buffer)
* @param padded Whether it should be padded to 256 bits or not
*/
export const ripemd160 = function(a: Buffer, padded: boolean): Buffer {
assertIsBuffer(a)
return _ripemd160(a, padded)
}
/**
* Creates RIPEMD160 hash of a string input.
* @param a The input data (String)
* @param padded Whether it should be padded to 256 bits or not
*/
export const ripemd160FromString = function(a: string, padded: boolean): Buffer {
assertIsString(a)
return _ripemd160(a, padded)
}
/**
* Creates RIPEMD160 hash of a number[] input.
* @param a The input data (number[])
* @param padded Whether it should be padded to 256 bits or not
*/
export const ripemd160FromArray = function(a: number[], padded: boolean): Buffer {
assertIsArray(a)
return _ripemd160(a, padded)
}
/**
* Creates SHA-3 hash of the RLP encoded version of the input.
* @param a The input data
*/
export const rlphash = function(a: Input): Buffer {
return keccak(encode(a))
}
/**
* Throws if a string is not hex prefixed
* @param {string} input string to check hex prefix of
*/
export const assertIsHexString = function(input: string): void {
if (!isHexString(input)) {
const msg = `This method only supports 0x-prefixed hex strings but input was: ${input}`
throw new Error(msg)
}
}
/**
* Throws if input is not a buffer
* @param {Buffer} input value to check
*/
export const assertIsBuffer = function(input: Buffer): void {
if (!Buffer.isBuffer(input)) {
const msg = `This method only supports Buffer but input was: ${input}`
throw new Error(msg)
}
}
/**
* Throws if input is not an array
* @param {number[]} input value to check
*/
export const assertIsArray = function(input: number[]): void {
if (!Array.isArray(input)) {
const msg = `This method only supports number arrays but input was: ${input}`
throw new Error(msg)
}
}
/**
* Throws if input is not a string
* @param {string} input value to check
*/
export const assertIsString = function(input: string): void {
if (typeof input !== 'string') {
const msg = `This method only supports strings but input was: ${input}`
throw new Error(msg)
}
}

@ -1,5 +1,5 @@
'use strict'
import { bufferToHex } from 'ethereumjs-util'
import { bufferToHex } from '@ethereumjs/util'
import { isHexString } from 'ethjs-util'
function convertToPrefixedHex (input) {

@ -2,6 +2,7 @@ import { EventManager } from './eventManager'
import * as uiHelper from './helpers/uiHelper'
import * as compilerHelper from './helpers/compilerHelper'
import * as util from './util'
import * as hash from './hash'
import { Storage } from './storage'
import { EventsDecoder } from './execution/eventsDecoder'
import * as txExecution from './execution/txExecution'
@ -38,4 +39,4 @@ const execution = {
LogsManager,
forkAt
}
export { EventManager, helpers, Storage, util, execution }
export { EventManager, helpers, Storage, util, execution, hash }

@ -1,6 +1,6 @@
'use strict'
import { bufferToHex, keccak, setLengthLeft, toBuffer, addHexPrefix } from 'ethereumjs-util'
import { bigIntToHex } from '@ethereumjs/util'
import { hash } from '@remix-project/remix-lib'
import { bufferToHex, setLengthLeft, toBuffer, addHexPrefix } from '@ethereumjs/util'
import stringSimilarity from 'string-similarity'
/*
@ -153,7 +153,7 @@ export function buildCallPath (index, rootCall) {
// eslint-disable-next-line camelcase
export function sha3_256 (value) {
value = toBuffer(addHexPrefix(value))
const retInBuffer: Buffer = keccak(setLengthLeft(value, 32))
const retInBuffer: Buffer = hash.keccak(setLengthLeft(value, 32))
return bufferToHex(retInBuffer)
}

@ -1,7 +1,8 @@
'use strict'
import tape from 'tape'
import { BN, toBuffer } from 'ethereumjs-util'
import { BN } from 'bn.js'
import { toBuffer } from '@ethereumjs/util'
import { resultToRemixTx } from '../src/helpers/txResultHelper'
const TRANSACTION_HASH = '0x538ad944d09c2df403f064c1e4556fae877fe3f1b600c567622e330c2bdbbe2e'

@ -17,10 +17,11 @@
"test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/mocha test/*.ts"
},
"dependencies": {
"@ethereumjs/block": "^4.0.0",
"@ethereumjs/common": "^3.0.0",
"@ethereumjs/tx": "^4.0.0",
"@ethereumjs/vm": "^6.0.0",
"@ethereumjs/block": "^4.1.0",
"@ethereumjs/common": "^3.0.2",
"@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23",
"ansi-gray": "^0.1.1",
"async": "^3.1.0",
@ -28,7 +29,6 @@
"color-support": "^1.1.3",
"commander": "^9.4.1",
"cors": "^2.8.5",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
"express": "^4.16.3",

@ -2,8 +2,10 @@ import { util } from '@remix-project/remix-lib'
const { toHexPaddedString, formatMemory } = util
import { helpers } from '@remix-project/remix-lib'
const { normalizeHexAddress } = helpers.ui
import { ConsoleLogs } from '@remix-project/remix-lib'
import { toChecksumAddress, BN, keccak, bufferToHex, Address, toBuffer } from 'ethereumjs-util'
import { ConsoleLogs, hash } from '@remix-project/remix-lib'
import BN from 'bn.js'
import { isBigNumber } from 'web3-utils'
import { toChecksumAddress, bufferToHex, Address, toBuffer } from '@ethereumjs/util'
import utils from 'web3-utils'
import { ethers } from 'ethers'
import { VMContext } from './vm-context'
@ -276,7 +278,7 @@ export class VmProxy {
}
let consoleArgs = iface.decodeFunctionData(functionDesc, payload)
consoleArgs = consoleArgs.map((value) => {
if (utils.isBigNumber(value)) {
if (isBigNumber(value)) {
return value.toString()
}
return value
@ -352,7 +354,7 @@ export class VmProxy {
const txHash = '0x' + block.transactions[block.transactions.length - 1].hash().toString('hex')
if (this.storageCache['after_' + txHash] && this.storageCache['after_' + txHash][address]) {
const slot = '0x' + keccak(toBuffer(ethers.utils.hexZeroPad(position, 32))).toString('hex')
const slot = '0x' + hash.keccak(toBuffer(ethers.utils.hexZeroPad(position, 32))).toString('hex')
const storage = this.storageCache['after_' + txHash][address]
return cb(null, storage[slot].value)
}

@ -1,13 +1,18 @@
import { Block } from '@ethereumjs/block'
import { ConsensusType } from '@ethereumjs/common'
export function generateBlock (vmContext) {
const common = vmContext.vmObject().common
const difficulty = common.consensusType() === ConsensusType.ProofOfStake ? 0 : 69762765929000
return new Promise((resolve, reject) => {
const block: Block = Block.fromBlockData({
header: {
timestamp: (new Date().getTime() / 1000 | 0),
number: 0,
coinbase: '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a',
difficulty: 69762765929000,
difficulty,
gasLimit: 8000000
}
}, { common: vmContext.vmObject().common })

@ -1,5 +1,6 @@
import { BN, privateToAddress, toChecksumAddress, isValidPrivate, Address } from 'ethereumjs-util'
const Web3EthAccounts = require('web3-eth-accounts')
import { privateToAddress, toChecksumAddress, isValidPrivate, Address } from '@ethereumjs/util'
import BN from 'bn.js'
const Web3EthAccounts = require('web3-eth-accounts');
import * as crypto from 'crypto'
export class Web3Accounts {

@ -1,6 +1,6 @@
import { toHex, toDecimal } from 'web3-utils'
import { bigIntToHex } from '@ethereumjs/util'
import { toChecksumAddress, BN, Address } from 'ethereumjs-util'
import BN from 'bn.js'
import { toChecksumAddress, Address, bigIntToHex } from '@ethereumjs/util'
import { processTx } from './txProcess'
import { execution } from '@remix-project/remix-lib'
import { ethers } from 'ethers'
@ -156,9 +156,9 @@ export class Transactions {
payload.params[0].gas = 10000000 * 10
this.vmContext.web3().flagNextAsDoNotRecordEvmSteps()
processTx(this.txRunnerInstance, payload, true, (error, value: VMexecutionResult) => {
const result: RunTxResult = value.result
processTx(this.txRunnerInstance, payload, true, (error, value: VMexecutionResult) => {
if (error) return cb(error)
const result: RunTxResult = value.result
if ((result as any).receipt?.status === '0x0' || (result as any).receipt?.status === 0) {
try {
const msg = `0x${result.execResult.returnValue.toString('hex') || '0'}`

@ -13,17 +13,17 @@ import { generateBlock } from './genesis'
import { VMContext } from './vm-context'
export class Provider {
options: Record<string, unknown>
options: Record<string, string | number>
vmContext
Accounts
Transactions
methods
connected: boolean;
constructor (options: Record<string, unknown> = {}) {
constructor (options: Record<string, string | number> = {}) {
this.options = options
this.connected = true
this.vmContext = new VMContext(options['fork'])
this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as number)
this.Accounts = new Web3Accounts(this.vmContext)
this.Transactions = new Transactions(this.vmContext)

@ -1,13 +1,17 @@
/* global ethereum */
'use strict'
import { rlp, keccak, bufferToHex } from 'ethereumjs-util'
import { hash } from '@remix-project/remix-lib'
import { bufferToHex } from '@ethereumjs/util'
import type { Address } from '@ethereumjs/util'
import { decode } from 'rlp'
import { ethers } from 'ethers'
import { execution } from '@remix-project/remix-lib'
const { LogsManager } = execution
import { VmProxy } from './VmProxy'
import { VM } from '@ethereumjs/vm'
import { Common } from '@ethereumjs/common'
import { Trie } from '@ethereumjs/trie'
import { DefaultStateManager } from '@ethereumjs/statemanager'
import { DefaultStateManager, StateManager, EthersStateManager, EthersStateManagerOpts } from '@ethereumjs/statemanager'
import { StorageDump } from '@ethereumjs/statemanager/dist/interface'
import { EVM } from '@ethereumjs/evm'
import { EEI } from '@ethereumjs/vm'
@ -33,6 +37,44 @@ export interface DefaultStateManagerOpts {
prefixCodeHashes?: boolean
}
class CustomEthersStateManager extends EthersStateManager {
keyHashes: { [key: string]: string }
constructor (opts: EthersStateManagerOpts) {
super(opts)
this.keyHashes = {}
}
putContractStorage (address, key, value) {
this.keyHashes[bufferToHex(key).replace('0x', '')] = hash.keccak(key).toString('hex')
return super.putContractStorage(address, key, value)
}
copy(): CustomEthersStateManager {
const newState = new CustomEthersStateManager({
provider: (this as any).provider,
blockTag: BigInt((this as any).blockTag),
})
;(newState as any).contractCache = new Map((this as any).contractCache)
;(newState as any).storageCache = new Map((this as any).storageCache)
;(newState as any)._cache = this._cache
;(newState as any).keyHashes = this.keyHashes
return newState
}
async dumpStorage(address: Address): Promise<StorageDump> {
const storageDump = {}
const storage = await super.dumpStorage(address)
for (const key of Object.keys(storage)) {
const value = storage[key]
storageDump['0x' + this.keyHashes[key]] = {
key: '0x' + key,
value: value
}
}
return storageDump
}
}
/*
extend vm state manager and instanciate VM
*/
@ -44,7 +86,7 @@ class StateManagerCommonStorageDump extends DefaultStateManager {
}
putContractStorage (address, key, value) {
this.keyHashes[keccak(key).toString('hex')] = bufferToHex(key)
this.keyHashes[hash.keccak(key).toString('hex')] = bufferToHex(key)
return super.putContractStorage(address, key, value)
}
@ -64,7 +106,7 @@ class StateManagerCommonStorageDump extends DefaultStateManager {
const stream = trie.createReadStream()
stream.on('data', (val) => {
const value = rlp.decode(val.value)
const value = decode(val.value)
storage['0x' + val.key.toString('hex')] = {
key: this.keyHashes[val.key.toString('hex')],
value: '0x' + value.toString('hex')
@ -84,7 +126,7 @@ class StateManagerCommonStorageDump extends DefaultStateManager {
export type CurrentVm = {
vm: VM,
web3vm: VmProxy,
stateManager: StateManagerCommonStorageDump,
stateManager: StateManager,
common: Common
}
@ -103,12 +145,15 @@ export class VMContext {
web3vm: VmProxy
logsManager: any // LogsManager
exeResults: Record<string, Transaction>
nodeUrl: string
blockNumber: number | 'latest'
constructor (fork?) {
constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest') {
this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = fork || 'london'
this.nodeUrl = nodeUrl
this.blockNumber = blockNumber
this.blocks = {}
this.latestBlockNumber = "0x0"
this.blockByTxHash = {}
@ -122,7 +167,21 @@ export class VMContext {
}
async createVm (hardfork) {
const stateManager = new StateManagerCommonStorageDump()
let stateManager: StateManager
console.log('creating a new VM', hardfork, this.nodeUrl, this.blockNumber)
if (this.nodeUrl) {
let block = this.blockNumber
if (this.blockNumber === 'latest') {
const provider = new ethers.providers.StaticJsonRpcProvider(this.nodeUrl)
block = await provider.getBlockNumber()
}
stateManager = new CustomEthersStateManager({
provider: this.nodeUrl,
blockTag: BigInt(block)
})
} else
stateManager = new StateManagerCommonStorageDump()
const common = new Common({ chain: 'mainnet', hardfork })
const blockchain = new (Blockchain as any)({ common })
const eei = new EEI(stateManager, common, blockchain)

@ -15,13 +15,13 @@
}
],
"dependencies": {
"@ethereumjs/block": "^4.0.0",
"@ethereumjs/tx": "^4.0.0",
"@ethereumjs/vm": "^6.0.0",
"@ethereumjs/block": "^4.1.0",
"@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23",
"async": "^2.6.2",
"eslint-scope": "^5.0.0",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
"minixhr": "^3.2.2",

@ -150,7 +150,7 @@ export interface CompilerInputOptions {
language?: Language
}
export type EVMVersion = 'homestead' | 'tangerineWhistle' | 'spuriousDragon' | 'byzantium' | 'constantinople' | 'petersburg' | 'istanbul' | 'berlin' | 'london' | null
export type EVMVersion = 'homestead' | 'tangerineWhistle' | 'spuriousDragon' | 'byzantium' | 'constantinople' | 'petersburg' | 'istanbul' | 'berlin' | 'london' | 'paris' | null
export type Language = 'Solidity' | 'Yul'

@ -36,10 +36,11 @@
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-tests#readme",
"dependencies": {
"@erebos/bzz-node": "^0.13.0",
"@ethereumjs/block": "^4.0.0",
"@ethereumjs/common": "^3.0.0",
"@ethereumjs/tx": "^4.0.0",
"@ethereumjs/vm": "^6.0.0",
"@ethereumjs/block": "^4.1.0",
"@ethereumjs/common": "^3.0.2",
"@ethereumjs/tx": "^4.0.2",
"@ethereumjs/util": "^8.0.3",
"@ethereumjs/vm": "^6.3.0",
"@remix-project/remix-lib": "^0.5.23",
"@remix-project/remix-simulator": "^0.2.23",
"@remix-project/remix-solidity": "^0.5.9",
@ -52,7 +53,6 @@
"colors": "1.4.0",
"commander": "^9.4.1",
"deep-equal": "^1.0.1",
"ethereumjs-util": "^7.0.10",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
"express-ws": "^4.0.0",

@ -3,19 +3,33 @@ import { ModalDialog, ModalDialogProps, ValidationResult } from '@remix-ui/modal
import { ModalTypes } from '../../types'
interface ModalWrapperProps extends ModalDialogProps {
modalType?: ModalTypes
defaultValue?: string
modalType?: ModalTypes
defaultValue?: string
}
const ModalWrapper = (props: ModalWrapperProps) => {
const [state, setState] = useState<ModalDialogProps>()
const ref = useRef()
const formRef = useRef()
const data = useRef()
const getFormData = () => {
if (formRef.current) {
const formData = new FormData(formRef.current)
const data = {}
for (const pair of formData.entries()) {
data[pair[0]] = pair[1]
}
return data
}
}
const onFinishPrompt = async () => {
if (ref.current === undefined) {
if (ref.current === undefined && formRef.current === undefined) {
onOkFn()
} else {
} else if (formRef.current) {
(props.okFn) ? props.okFn(getFormData()) : props.resolve(getFormData())
} else if(ref.current) {
// @ts-ignore: Object is possibly 'null'.
(props.okFn) ? props.okFn(ref.current.value) : props.resolve(ref.current.value)
}
@ -43,9 +57,29 @@ const ModalWrapper = (props: ModalWrapperProps) => {
<>
{props.message}
<input onChange={onInputChanged} type={props.modalType === ModalTypes.password ? 'password' : 'text'} defaultValue={defaultValue} data-id="modalDialogCustomPromp" ref={ref} className="form-control" />
{!validation.valid && <span className='text-warning'>{validation.message}</span>}
</>
)
{validation && !validation.valid && <span className='text-warning'>{validation.message}</span>}
</>
)
}
const onFormChanged = () => {
if (props.validationFn) {
const validation = props.validationFn(getFormData())
setState(prevState => {
return { ...prevState, message: createForm(validation), validation }
})
}
}
const createForm = (validation: ValidationResult) => {
return (
<>
<form onChange={onFormChanged} ref={formRef}>
{props.message}
</form>
{validation && !validation.valid && <span className='text-warning'>{validation.message}</span>}
</>
)
}
useEffect(() => {
@ -61,6 +95,14 @@ const ModalWrapper = (props: ModalWrapperProps) => {
message: createModalMessage(props.defaultValue, { valid: true })
})
break
case ModalTypes.form:
setState({
...props,
okFn: onFinishPrompt,
cancelFn: onCancelFn,
message: createForm({ valid: true })
})
break
default:
setState({
...props,

@ -4,4 +4,5 @@ export const enum ModalTypes {
prompt = 'prompt',
password = 'password',
default = 'default',
form = 'form',
}

@ -102,7 +102,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
const providerChanged = () => {
debuggerModule.onEnvChanged((provider) => {
setState(prevState => {
const isLocalNodeUsed = provider !== 'vm' && provider !== 'injected'
const isLocalNodeUsed = !provider.startsWith('vm') && provider !== 'injected'
return { ...prevState, isLocalNodeUsed: isLocalNodeUsed }
})
})

@ -1,6 +1,6 @@
import React from 'react' // eslint-disable-line
import DropdownPanel from './dropdown-panel' // eslint-disable-line
import { BN } from 'ethereumjs-util'
import { BN } from 'bn.js'
import Web3 from 'web3'
export const GlobalVariables = ({ block, receipt, tx, className }) => {

@ -1,4 +1,4 @@
import { BN } from 'ethereumjs-util'
import { BN } from 'bn.js'
import { ExtractData } from '../types' // eslint-disable-line
export function extractData (item, parent): ExtractData {

@ -226,7 +226,7 @@ declare interface CondensedCompilationInput {
optimize: boolean
/** e.g: 0.6.8+commit.0bbfe453 */
version: string
evmVersion?: 'berlin' | 'istanbul' | 'petersburg' | 'constantinople' | 'byzantium' | 'spuriousDragon' | 'tangerineWhistle' | 'homestead'
evmVersion?: 'paris' | 'berlin' | 'istanbul' | 'petersburg' | 'constantinople' | 'byzantium' | 'spuriousDragon' | 'tangerineWhistle' | 'homestead'
}
declare interface ContentImport {

@ -37,6 +37,50 @@ export const CustomIconsToggle = React.forwardRef(({ onClick, icon, className =
// forwardRef again here!
// Dropdown needs access to the DOM of the Menu to measure it
export const CustomMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, className: string, 'aria-labelledby'?: string }, ref: Ref<HTMLDivElement>) => {
const height = window.innerHeight * 0.6
return (
<div
ref={ref}
style={style}
className={className}
aria-labelledby={labeledBy}
>
<ul className="overflow-auto list-unstyled mb-0" style={{ maxHeight: height+'px' }}>
{
children
}
</ul>
</div>
)
},
)
export const ProxyAddressToggle = React.forwardRef(({ address, onClick, className = '', onChange }: { address: string, onClick: (e) => void, className: string, onChange: (e: React.ChangeEvent<HTMLInputElement>) => void }, ref: Ref<HTMLDivElement>) => (
<div
ref={ref}
onClick={(e) => {
e.preventDefault()
onClick(e)
}}
className={'d-flex '+ className.replace('dropdown-toggle', '')}
data-id="toggleProxyAddressDropdown"
>
<input
onChange={(e) => {
e.preventDefault()
onChange(e)
}}
className="udapp_input form-control"
value={address}
placeholder="Enter Proxy Address"
style={{ width: '100%' }}
data-id="ERC1967AddressInput"
/>
</div>
))
export const ProxyDropdownMenu = React.forwardRef(
({ children, style, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, className: string, 'aria-labelledby'?: string }, ref: Ref<HTMLDivElement>) => {
return (
<div

@ -1,3 +1,4 @@
import { LayoutCompatibilityReport } from '@openzeppelin/upgrades-core/dist/storage/report'
import React from 'react'
export const fileChangedToastMsg = (from: string, path: string) => (
@ -115,3 +116,27 @@ export const upgradeWithProxyMsg = () => (
</ol>
</div>
)
export const unavailableProxyLayoutMsg = () => (
<div>
<p>Previous contract implementation is NOT available for upgrade comparison. <br /> A new storage layout will be saved for future upgrades.</p>
</div>
)
export const upgradeReportMsg = (report: LayoutCompatibilityReport) => (
<div>
<div className="py-2 ml-2 mb-1 align-self-end mb-2 d-flex">
<span className="align-self-center pl-4 mt-1">
<i className="pr-2 text-warning far fa-exclamation-triangle" aria-hidden="true" style={{ fontSize: 'xxx-large', fontWeight: 'lighter' }}></i>
</span>
<div className="d-flex flex-column">
<span className="pl-4 mt-1">The storage layout of new implementation is NOT</span>
<span className="pl-4 mt-1">compatible with the previous implementation.</span>
<span className="pl-4 mt-1">Your contract's storage may be partially or fully erased!</span>
</div>
</div>
<div className='pl-4 text-danger'>
{ report.explain() }
</div>
</div>
)

@ -1,4 +1,4 @@
import * as ethJSUtil from 'ethereumjs-util'
import * as ethJSUtil from '@ethereumjs/util'
export const extractNameFromKey = (key: string): string => {
if (!key) return
@ -128,3 +128,15 @@ export const addSlash = (file: string) => {
if (!file.startsWith('/'))file = '/' + file
return file
}
export const shortenProxyAddress = (address: string) => {
const len = address.length
return address.slice(0, 5) + '...' + address.slice(len - 5, len)
}
export const shortenDate = (dateString: string) => {
const date = new Date(dateString)
return date.toLocaleDateString(undefined, { month: "short", day: "numeric" }) + ', ' + date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })
}

@ -37,50 +37,56 @@ function HomeTabFeatured() {
dotListClass="position-relative mt-2"
>
<div className="mx-1 px-1 d-flex">
<img src={"assets/img/bgRemi_small.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
<div className="h6 w-50 p-4" style={{ flex: "1" }}>
<a href="https://remix-project.org" target="__blank">
<img src={"assets/img/bgRemi_small.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
</a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: "1" }}>
<h5><FormattedMessage id='home.jumpIntoWeb3' /></h5>
<div><FormattedMessage id='home.jumpIntoWeb3Text'/></div>
<div style={{ fontSize: '0.8rem' }} className="mb-3"><FormattedMessage id='home.jumpIntoWeb3Text'/></div>
<a
className="remixui_home_text btn btn-secondary mt-2 text-decoration-none mb-3"
className="remixui_home_text btn-sm btn-secondary mt-2 text-decoration-none mb-3"
onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'jumpIntoWeb3'])}
target="__blank"
href="https://remix-project.org"><FormattedMessage
id='home.jumpIntoWeb3More'
/></a>
href="https://remix-project.org"
>
<FormattedMessage id='home.jumpIntoWeb3More'/>
</a>
</div>
</div>
<div className="mx-1 px-1 d-flex">
<a href="https://www.youtube.com/@EthereumRemix/videos" target="__blank">
<a href="https://www.youtube.com/@EthereumRemix/videos" target="__blank">
<img src={"/assets/img/YouTubeLogo.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
</a>
<div className="h6 w-50 p-4" style={{ flex: "1" }}>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: "1" }}>
<h5><FormattedMessage id='home.remixYouTube' /></h5>
<p style={{ fontStyle: 'italic' }}><FormattedMessage id='home.remixYouTubeText1' /></p>
<div><FormattedMessage id='home.remixYouTubeText2' /></div>
<p style={{ fontStyle: 'italic', fontSize: '1rem' }}><FormattedMessage id='home.remixYouTubeText1' /></p>
<div style={{ fontSize: '0.8rem' }} className="mb-3"><FormattedMessage id='home.remixYouTubeText2' /></div>
<a
className="remixui_home_text btn btn-secondary mt-2 text-decoration-none mb-3"
className="remixui_home_text btn-sm btn-secondary mt-2 text-decoration-none mb-3"
onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'youTubeMore'])}
target="__blank"
href="https://www.youtube.com/@EthereumRemix/videos"><FormattedMessage
id='home.remixYouTubeMore'
/></a>
href="https://www.youtube.com/@EthereumRemix/videos"
>
<FormattedMessage id='home.remixYouTubeMore' />
</a>
</div>
</div>
<div className="mx-1 px-1 d-flex">
<img src={"/assets/img/remixRewardBetaTester_small.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
<div className="h6 w-50 p-4" style={{ flex: "1" }}>
<a href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform" target="__blank">
<img src={"/assets/img/remixRewardBetaTester_small.webp"} style={{ flex: "1", height: "170px", maxWidth: "170px" }} alt="" ></img>
</a>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: "1" }}>
<h5><FormattedMessage id='home.betaTesting' /></h5>
<p style={{ fontStyle: 'italic' }}><FormattedMessage id='home.betaTestingText1' /></p>
<div><FormattedMessage id='home.betaTestingText2' /></div>
<p style={{ fontStyle: 'italic', fontSize: '1rem' }}><FormattedMessage id='home.betaTestingText1' /></p>
<div style={{ fontSize: '0.8rem' }} className="mb-3"><FormattedMessage id='home.betaTestingText2' /></div>
<a
className="remixui_home_text btn btn-secondary mt-2 text-decoration-none mb-3"
className="remixui_home_text btn-sm btn-secondary mt-2 text-decoration-none mb-3"
onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'betatesting'])}
target="__blank"
href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform"><FormattedMessage
id='home.betaTestingMore'
/></a>
href="https://docs.google.com/forms/d/e/1FAIpQLSd0WsJnKbeJo-BGrnf7WijxAdmE4PnC_Z4M0IApbBfHLHZdsQ/viewform"
>
<FormattedMessage id='home.betaTestingMore' />
</a>
</div>
</div>
</Carousel>

@ -4,6 +4,7 @@ import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
const _paq = window._paq = window._paq || [] // eslint-disable-line
import { CustomTooltip } from '@remix-ui/helper';
interface HomeTabFileProps {
plugin: any
@ -153,17 +154,27 @@ function HomeTabFile ({plugin}: HomeTabFileProps) {
</div>
</ModalDialog>
<Toaster message={state.toasterMsg} />
<div className="justify-content-start mt-1 p-2 border-bottom d-flex flex-column" id="hTFileSection">
<label style={{fontSize: "1rem"}}><FormattedMessage id='home.files' /></label>
<button className="btn btn-primary p-2 border my-1" data-id="homeTabNewFile" style={{width: 'fit-content'}} onClick={() => createNewFile()}><FormattedMessage id='home.newFile' /></button>
<label className="btn p-2 border my-1" style={{width: 'fit-content'}} htmlFor="openFileInput"><FormattedMessage id='home.openFile' /></label>
<input title="open file" type="file" id="openFileInput" onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}} multiple />
<button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}><FormattedMessage id='home.connectToLocalhost' /></button>
<label className="pt-2"><FormattedMessage id='home.loadFrom' /></label>
<div className="justify-content-start mt-1 p-2 d-flex flex-column" id="hTFileSection">
<label style={{fontSize: "1.2rem"}}><FormattedMessage id='home.files' /></label>
<div className="dflex">
<button className="btn btn-primary p-2 mr-2 border my-1" data-id="homeTabNewFile" style={{width: 'fit-content'}} onClick={() => createNewFile()}><FormattedMessage id='home.newFile' /></button>
<label className="btn p-2 mr-2 border my-1" style={{width: 'fit-content', cursor: 'pointer'}} htmlFor="openFileInput"><FormattedMessage id='home.openFile' /></label>
<input title="open file" type="file" id="openFileInput" onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}} multiple />
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={"Connect to Localhost"}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}><FormattedMessage id='home.connectToLocalhost' /></button>
</CustomTooltip>
</div>
<label style={{fontSize: "0.8rem"}} className="pt-2"><FormattedMessage id='home.loadFrom' /></label>
<div className="d-flex">
<button
className="btn p-2 border mr-2"

@ -68,7 +68,7 @@ function HomeTabGetStarted ({plugin}: HomeTabGetStartedProps) {
return (
<div className="pl-2" id="hTGetStartedSection">
<label style={{fontSize: "1.2rem"}}>
<span className="mr-2" style={{fontWeight: "bold"}}>
<span className="mr-2">
<FormattedMessage id="home.getStarted" />
</span>
- <FormattedMessage id="home.projectTemplates" />

@ -2,6 +2,7 @@
import React, { useEffect, useState, useContext } from 'react'
import { FormattedMessage } from 'react-intl'
import { ThemeContext } from '../themeContext'
import { CustomTooltip } from '@remix-ui/helper'
declare global {
interface Window {
_paq: any
@ -27,10 +28,6 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
const themeFilter = useContext(ThemeContext)
const openLink = () => {
window.open("https://remix-ide.readthedocs.io/en/latest/remix_tutorials_learneth.html?highlight=learneth#learneth-tutorial-repos", '_blank')
}
const startLearnEthTutorial = async (tutorial: 'basics' | 'soliditybeginner' | 'deploylibraries') => {
await plugin.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting'])
plugin.verticalIcons.select('LearnEth')
@ -52,17 +49,25 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
return (
<div className="d-flex px-2 pb-2 pt-2 d-flex flex-column" id="hTLearnSection">
<div className="d-flex justify-content-between">
<label className="py-2 align-self-center m-0" style={{fontSize: "1.2rem"}}>
<label className="py-2 pt-3 align-self-center m-0" style={{fontSize: "1.2rem"}}>
<FormattedMessage id="home.learn" />
</label>
<button
onClick={ async () => {
await goToLearnEthHome()
}}
className="h-100 px-2 pt-0 btn"
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={"See all tutorials"}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<img className="align-self-center" src="assets/img/learnEthLogo.webp" alt="" style={ { filter: themeFilter.filter, width: "1rem", height: "1ren" } } />
</button>
<button
onClick={ async () => {
await goToLearnEthHome()
}}
className="h-100 px-2 pt-0 btn"
>
<img className="align-self-center" src="assets/img/learnEthLogo.webp" alt="" style={ { filter: themeFilter.filter, width: "1rem", height: "1ren" } } />
</button>
</CustomTooltip>
</div>
<div className="d-flex flex-column">
<label className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Basics }})}>
@ -70,7 +75,7 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
<FormattedMessage id="home.learnEth1" />
</label>
{(state.visibleTutorial === VisibleTutorial.Basics) && <div className="pt-2 d-flex flex-column text-left">
<span>
<span className="py-1" style={{fontSize: "0.8rem"}}>
<FormattedMessage id="home.learnEth1Desc" />
</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('basics')}>
@ -83,11 +88,12 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
<FormattedMessage id="home.learnEth2" />
</label>
{(state.visibleTutorial === VisibleTutorial.Intermediate) && <div className="pt-2 d-flex flex-column text-left">
<span>
<FormattedMessage id="home.learnEth2Desc" /></span>
<span className="py-1" style={{fontSize: "0.8rem"}}>
<FormattedMessage id="home.learnEth2Desc" />
</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('soliditybeginner')}>
<FormattedMessage id="home.getStarted" />
</button>
<FormattedMessage id="home.getStarted" />
</button>
</div>}
</label>
<label className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Advanced }})}>
@ -95,11 +101,12 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
<FormattedMessage id="home.remixAdvanced" />
</label>
{(state.visibleTutorial === VisibleTutorial.Advanced) && <div className="pt-2 d-flex flex-column text-left">
<span>
<FormattedMessage id="home.remixAdvancedDesc" /></span>
<span className="py-1" style={{fontSize: "0.8rem"}}>
<FormattedMessage id="home.remixAdvancedDesc" />
</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('deploylibraries')}>
<FormattedMessage id="home.getStarted" />
</button>
<FormattedMessage id="home.getStarted" />
</button>
</div>}
</label>
</div>

@ -8,27 +8,28 @@ function HomeTabScamAlert () {
return (
<div className="" id="hTScamAlertSection">
<label className="pl-2 text-danger" style={{fontSize: "1.2rem"}}><FormattedMessage id='home.scamAlert' /></label>
<div className="py-2 ml-2 mb-1 align-self-end mb-2 d-flex flex-column border border-danger">
<span className="pl-4 mt-2">
<i className="pr-2 text-danger fas fa-exclamation-triangle"></i>
<b><FormattedMessage id='home.scamAlert' />:</b>
</span>
<span className="pl-4 mt-1">
<FormattedMessage id='home.scamAlertText' />
</span>
<span className="pl-4 mt-1">
<FormattedMessage id='home.scamAlertText2' />:
<a className="pl-2 remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'learnMore'])} target="__blank" href="https://medium.com/remix-ide/remix-in-youtube-crypto-scams-71c338da32d">
<FormattedMessage id='home.learnMore' />
</a>
</span>
<span className="pl-4 mt-1">
<FormattedMessage id='home.scamAlertText3' />
: &nbsp;
<a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'safetyTips'])} target="__blank" href="https://remix-ide.readthedocs.io/en/latest/security.html">
<FormattedMessage id='home.here' />
</a>
<div className="py-2 ml-2 mb-1 align-self-end mb-2 d-flex border border-danger">
<span className="align-self-center pl-4 mt-1">
<i style={{fontSize: 'xxx-large', fontWeight: 'lighter'}} className="pr-2 text-danger far fa-exclamation-triangle"></i>
</span>
<div className='d-flex flex-column'>
<span className="pl-4 mt-1">
<FormattedMessage id='home.scamAlertText' />
</span>
<span className="pl-4 mt-1">
<FormattedMessage id='home.scamAlertText2' />:
<a className="pl-2 remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'learnMore'])} target="__blank" href="https://medium.com/remix-ide/remix-in-youtube-crypto-scams-71c338da32d">
<FormattedMessage id='home.learnMore' />
</a>
</span>
<span className="pl-4 mt-1">
<FormattedMessage id='home.scamAlertText3' />
: &nbsp;
<a className="remixui_home_text" onClick={() => _paq.push(['trackEvent', 'hometab', 'scamAlert', 'safetyTips'])} target="__blank" href="https://remix-ide.readthedocs.io/en/latest/security.html">
<FormattedMessage id='home.here' />
</a>
</span>
</div>
</div>
</div>
)

@ -52,19 +52,22 @@ function HomeTabTitle() {
return (
<div className="px-2 pb-2 pt-2 d-flex flex-column border-bottom" id="hTTitleSection">
<div className="mr-4 d-flex">
<div onClick={() => playRemi()} style={{ filter: themeFilter.filter}} >
<BasicLogo classList="align-self-end remixui_home_logoImg" solid={false} />
</div>
<audio
id="remiAudio"
muted={false}
src="assets/audio/remiGuitar-single-power-chord-A-minor.mp3"
ref={remiAudioEl}
></audio>
</div>
<div className="d-flex justify-content-between">
<div className="d-flex py-2 justify-content-between">
<div className='d-flex justify-content-start'>
<span className="h-80 text-uppercase" style={{ fontSize: 'xx-large', fontFamily: "Noah, sans-serif" }}>Remix</span>
<div className="ml-2 d-flex">
<div onClick={() => playRemi()} >
<img className="" src="assets/img/guitarRemiCroped.webp" style={{height: "3rem"}} alt=""></img>
</div>
<audio
id="remiAudio"
muted={false}
src="assets/audio/remiGuitar-single-power-chord-A-minor.mp3"
ref={remiAudioEl}
></audio>
</div>
</div>
<span>
<CustomTooltip
placement={'top'}
@ -94,7 +97,7 @@ function HomeTabTitle() {
openLink("https://twitter.com/EthereumRemix")
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'twitter'])
}}
className="border-0 h-100 pl-2 btn fab fa-twitter">
className="border-0 p-2 h-100 pl-2 btn fab fa-twitter">
</button>
</CustomTooltip>
@ -110,7 +113,7 @@ function HomeTabTitle() {
openLink("https://www.linkedin.com/company/ethereum-remix/")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'linkedin'])
}}
className="border-0 h-100 pl-2 btn fa fa-linkedin">
className="border-0 p-2 h-100 pl-2 btn fa fa-linkedin">
</button>
</CustomTooltip>
@ -126,7 +129,7 @@ function HomeTabTitle() {
openLink("https://medium.com/remix-ide")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'medium'])
}}
className="border-0 h-100 pl-2 btn fab fa-medium">
className="border-0 p-2 h-100 pl-2 btn fab fa-medium">
</button>
</CustomTooltip>
@ -139,15 +142,15 @@ function HomeTabTitle() {
>
<button
onClick={() => {
openLink("https://gitter.im/ethereum/remix")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'gitter'])
openLink("https://discord.gg/mh9hFCKkEq")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'discord'])
}}
className="border-0 h-100 pl-2 btn fab fa-gitter">
className="border-0 h-100 p-2 btn fab fa-discord">
</button>
</CustomTooltip>
</span>
</div>
<b className="pb-1 text-dark" style={{ fontStyle: 'italic' }}>
<b className="py-1 text-dark" style={{ fontStyle: 'italic' }}>
<FormattedMessage id="home.nativeIDE" />
</b>
<div className="pb-1" id="hTGeneralLinks">

@ -45,18 +45,20 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
}, [])
return (
<div className="d-flex flex-row w-100" data-id="remixUIHTAll">
<div className="d-flex flex-column w-100" data-id="remixUIHTAll">
<ThemeContext.Provider value={ state.themeQuality }>
<div className="px-2 pl-3 justify-content-start d-flex border-right flex-column" id="remixUIHTLeft" style={{flex: 2, minWidth: "35%"}}>
<HomeTabTitle />
<HomeTabFile plugin={plugin} />
<HomeTabLearn plugin={plugin} />
</div>
<div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{width: "65%"}} id="remixUIHTRight">
<HomeTabFeatured></HomeTabFeatured>
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
<HomeTabFeaturedPlugins plugin={plugin}></HomeTabFeaturedPlugins>
<HomeTabScamAlert></HomeTabScamAlert>
<div className='d-flex flex-row w-100'>
<div className="px-2 pl-3 justify-content-start d-flex border-right flex-column" id="remixUIHTLeft" style={{ width: 'inherit' }}>
<HomeTabTitle />
<HomeTabFile plugin={plugin} />
<HomeTabLearn plugin={plugin} />
</div>
<div className="pl-2 pr-3 justify-content-start d-flex flex-column" style={{width: "65%"}} id="remixUIHTRight">
<HomeTabFeatured></HomeTabFeatured>
<HomeTabGetStarted plugin={plugin}></HomeTabGetStarted>
<HomeTabFeaturedPlugins plugin={plugin}></HomeTabFeaturedPlugins>
<HomeTabScamAlert></HomeTabScamAlert>
</div>
</div>
</ThemeContext.Provider>
</div>

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

Loading…
Cancel
Save