Merge branch 'master' into contMenu

pull/3372/head
Liana Husikyan 2 years ago committed by GitHub
commit eba43b037a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  2. 6
      apps/remix-ide/src/app.js
  3. 32
      apps/remix-ide/src/app/files/fileManager.ts
  4. 6
      apps/remix-ide/src/app/plugins/contractFlattener.tsx
  5. 2
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  6. 17
      apps/remix-ide/src/app/providers/abstract-provider.tsx
  7. 80
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  8. 1
      apps/remix-ide/src/app/providers/injected-L2-provider.tsx
  9. 12
      apps/remix-ide/src/app/providers/injected-provider.tsx
  10. 29
      apps/remix-ide/src/app/providers/mainnet-vm-fork-provider.tsx
  11. 15
      apps/remix-ide/src/app/providers/vm-provider.tsx
  12. 4
      apps/remix-ide/src/app/tabs/locales/en/home.json
  13. 11
      apps/remix-ide/src/app/udapp/run-tab.js
  14. 4
      apps/remix-ide/src/blockchain/blockchain.js
  15. 8
      apps/remix-ide/src/blockchain/execution-context.js
  16. 4
      apps/remix-ide/src/blockchain/providers/vm.js
  17. 2
      apps/remix-ide/src/blockchain/providers/worker-vm.ts
  18. 2
      apps/remix-ide/src/remixEngine.js
  19. 6
      libs/remix-simulator/src/provider.ts
  20. 67
      libs/remix-simulator/src/vm-context.ts
  21. 56
      libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx
  22. 1
      libs/remix-ui/app/src/lib/remix-app/types/index.ts
  23. 54
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  24. 33
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  25. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  26. 49
      libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx
  27. 41
      libs/remix-ui/home-tab/src/lib/components/homeTabScamAlert.tsx
  28. 37
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  29. 24
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx
  30. 2
      libs/remix-ui/modal-dialog/src/lib/types/index.ts
  31. 2
      libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts
  32. 2
      libs/remix-ui/workspace/src/lib/utils/index.ts
  33. 2
      tsconfig.base.json
  34. 3
      tsconfig.json

@ -286,7 +286,15 @@ module.exports = {
.end() .end()
} }
}) })
} },
'Should connect to the mainnet fork and run web3.eth.getCode in the terminal #group9': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('udapp')
.switchEnvironment('vm-mainnet-fork')
.executeScriptInTerminal(`web3.eth.getCode('0x180587b00c8642e2c7ac3a758712d97e6f7bdcc7')`) // mainnet contract
.waitForElementContainsText('*[data-id="terminalJournal"]', '0x608060405260043610601f5760003560e01c80635c60da1b14603157602b565b36602b576029605f565b005b6029605f565b348015603c57600080fd5b5060436097565b6040516001600160a01b03909116815260200160405180910390f35b609560917f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b60d1565b565b600060c97f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b90565b3660008037600080366000845af43d6000803e80801560ef573d6000f35b3d6000fdfea2646970667358221220969dbb4b1d8aec2bb348e26488dc1a33b6bcf0190f567d161312ab7ca9193d8d64736f6c63430008110033', 120000)
},
} }

@ -28,6 +28,8 @@ import { Layout } from './app/panels/layout'
import { NotificationPlugin } from './app/plugins/notification' import { NotificationPlugin } from './app/plugins/notification'
import { Blockchain } from './blockchain/blockchain.js' import { Blockchain } from './blockchain/blockchain.js'
import { BerlinVMProvider, LondonVMProvider } from './app/providers/vm-provider' import { BerlinVMProvider, LondonVMProvider } from './app/providers/vm-provider'
import { MainnetForkVMProvider } from './app/providers/mainnet-vm-fork-provider'
import { CustomForkVMProvider } from './app/providers/custom-vm-fork-provider'
import { HardhatProvider } from './app/providers/hardhat-provider' import { HardhatProvider } from './app/providers/hardhat-provider'
import { GanacheProvider } from './app/providers/ganache-provider' import { GanacheProvider } from './app/providers/ganache-provider'
import { FoundryProvider } from './app/providers/foundry-provider' import { FoundryProvider } from './app/providers/foundry-provider'
@ -203,6 +205,8 @@ class AppComponent {
const networkModule = new NetworkModule(blockchain) const networkModule = new NetworkModule(blockchain)
// ----------------- represent the current selected web3 provider ---- // ----------------- represent the current selected web3 provider ----
const web3Provider = new Web3ProviderModule(blockchain) const web3Provider = new Web3ProviderModule(blockchain)
const vmProviderCustomFork = new CustomForkVMProvider(blockchain)
const vmProviderMainnetFork = new MainnetForkVMProvider(blockchain)
const vmProviderBerlin = new BerlinVMProvider(blockchain) const vmProviderBerlin = new BerlinVMProvider(blockchain)
const vmProviderLondon = new LondonVMProvider(blockchain) const vmProviderLondon = new LondonVMProvider(blockchain)
const hardhatProvider = new HardhatProvider(blockchain) const hardhatProvider = new HardhatProvider(blockchain)
@ -273,6 +277,8 @@ class AppComponent {
storagePlugin, storagePlugin,
vmProviderBerlin, vmProviderBerlin,
vmProviderLondon, vmProviderLondon,
vmProviderMainnetFork,
vmProviderCustomFork,
hardhatProvider, hardhatProvider,
ganacheProvider, ganacheProvider,
foundryProvider, foundryProvider,

@ -1,5 +1,6 @@
'use strict' 'use strict'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import JSZip from 'jszip'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import Registry from '../state/registry' 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) { async download(path) {
try { try {
const fileName = helper.extractNameFromKey(path) const downloadFileName = helper.extractNameFromKey(path)
path = this.normalize(path) if (await this.isDirectory(path)) {
const content: any = await this.readFile(path) const zip = new JSZip()
saveAs(new Blob([content]), fileName) 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) { } catch (e) {
throw new Error(e) throw new Error(e)
} }
@ -377,9 +397,9 @@ class FileManager extends Plugin {
/** /**
* Get the list of files in the directory * Get the list of files in the directory
* @param {string} path path of 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 { try {
path = this.normalize(path) path = this.normalize(path)
path = this.limitPluginScope(path) path = this.limitPluginScope(path)

@ -26,7 +26,7 @@ export class ContractFlattener extends Plugin {
async flattenAContract(action: customAction) { async flattenAContract(action: customAction) {
this.fileName = action.path[0] 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 ast = data.sources
const dependencyGraph = getDependencyGraph(ast, filePath) const dependencyGraph = getDependencyGraph(ast, filePath)
const sorted = dependencyGraph.isEmpty() const sorted = dependencyGraph.isEmpty()
? [filePath] ? [filePath]
: dependencyGraph.sort().reverse() : dependencyGraph.sort().reverse()
const sources = source.sources const sources = source.sources
const result = concatSourceFiles(sorted, sources) const result = concatSourceFiles(sorted, sources)
await this.call('fileManager', 'writeFile', `${filePath}_flattened.sol`, result) 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 // warn the user only if he/she is in the browser context
const mod: AppModal = { const mod: AppModal = {
id: 'remixdConnect', id: 'remixdConnect',
title: 'Connect to localhost', title: 'Access file system using remixd',
message: remixdDialog(), message: remixdDialog(),
okLabel: 'Connect', okLabel: 'Connect',
cancelLabel: 'Cancel', cancelLabel: 'Cancel',

@ -19,12 +19,20 @@ export type JsonDataResult = {
export type RejectRequest = (error: Error) => void export type RejectRequest = (error: Error) => void
export type SuccessRequest = (data: JsonDataResult) => void export type SuccessRequest = (data: JsonDataResult) => void
export abstract class AbstractProvider extends Plugin { 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 provider: ethers.providers.JsonRpcProvider
blockchain: Blockchain blockchain: Blockchain
defaultUrl: string defaultUrl: string
connected: boolean connected: boolean
nodeUrl: string nodeUrl: string
options: { [id: string] : any } = {}
constructor (profile, blockchain, defaultUrl) { constructor (profile, blockchain, defaultUrl) {
super(profile) super(profile)
@ -41,7 +49,7 @@ export abstract class AbstractProvider extends Plugin {
this.provider = null this.provider = null
} }
async init () { async init () {
this.nodeUrl = await ((): Promise<string> => { this.nodeUrl = await ((): Promise<string> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const modalContent: AppModal = { const modalContent: AppModal = {
@ -80,9 +88,12 @@ export abstract class AbstractProvider extends Plugin {
}) })
})() })()
this.provider = new ethers.providers.JsonRpcProvider(this.nodeUrl) this.provider = new ethers.providers.JsonRpcProvider(this.nodeUrl)
return {
nodeUrl: this.nodeUrl
}
} }
sendAsync (data: JsonDataRequest): Promise<any> { sendAsync (data: JsonDataRequest): Promise<JsonDataResult> {
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
if (!this.provider) return reject(new Error('provider node set')) if (!this.provider) return reject(new Error('provider node set'))

@ -0,0 +1,80 @@
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'
export class CustomForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
inputs: any
constructor (blockchain) {
super({
name: 'vm-custom-fork',
displayName: 'Custom fork - Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
this.blockchain = blockchain
this.fork = ''
this.nodeUrl = ''
this.blockNumber = 'latest'
this.inputs = {}
}
async init () {
this.inputs = {nodeUrl: '', evm: '', blockNumber: '' }
const body = () => {
return <div>
<div>
<label>Node URL</label>
<input type="text" value={this.inputs.nodeUrl} ></input>
</div>
<div>
<label>Block number (or "latest")</label>
<input type="text" placeholder='block number or "latest"' value={this.inputs.blockNumber} ></input>
</div>
<div>
<label>EVM</label>
<select value={this.inputs.evm}>
<option value="berlin" key="berlin">Berlin</option>
<option value="london" key="london" >London</option>
</select>
</div>
</div>
}
await ((): Promise<string> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
id: this.profile.name,
title: this.profile.displayName,
message: body(),
modalType: ModalTypes.default,
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)
}
}
this.call('notification', 'modal', modalContent)
})
})()
this.fork = this.inputs.evm
this.nodeUrl = this.inputs.nodeUrl
const block = this.inputs.blockNumber
this.blockNumber = block === 'latest' ? 'latest' : parseInt(block)
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
}
}
}

@ -17,6 +17,7 @@ export class InjectedL2Provider extends InjectedProvider {
if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addL2Network(this.chainName, this.chainId, this.rpcUrls) if (this.chainName && this.rpcUrls && this.rpcUrls.length > 0) await addL2Network(this.chainName, this.chainId, this.rpcUrls)
else else
throw new Error('Cannot add the L2 network to main injected provider') throw new Error('Cannot add the L2 network to main injected provider')
return {}
} }
} }

@ -1,12 +1,15 @@
/* global ethereum */ /* global ethereum */
import React from 'react' // eslint-disable-line
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider' import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider'
import Web3 from 'web3' import Web3 from 'web3'
import { IProvider } from './abstract-provider'
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).' 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 { export class InjectedProvider extends Plugin implements IProvider {
provider: any provider: any
options: { [id: string] : any } = {}
constructor (profile) { constructor (profile) {
super(profile) super(profile)
@ -23,6 +26,12 @@ export class InjectedProvider extends Plugin {
} }
} }
body (): JSX.Element {
return (
<div></div>
)
}
async init () { async init () {
const injectedProvider = (window as any).ethereum const injectedProvider = (window as any).ethereum
if (injectedProvider === undefined) { if (injectedProvider === undefined) {
@ -33,6 +42,7 @@ export class InjectedProvider extends Plugin {
} }
this.askPermission(true) this.askPermission(true)
} }
return {}
} }
sendAsync (data: JsonDataRequest): Promise<any> { sendAsync (data: JsonDataRequest): Promise<any> {

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

@ -1,17 +1,26 @@
import React from 'react' // eslint-disable-line
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider' import { JsonDataRequest, RejectRequest, SuccessRequest } from '../providers/abstract-provider'
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { IProvider } from './abstract-provider'
export class BasicVMProvider extends Plugin { export class BasicVMProvider extends Plugin implements IProvider {
blockchain blockchain
fork: string fork: string
options: { [id: string] : any } = {}
constructor (profile, blockchain) { constructor (profile, blockchain) {
super(profile) super(profile)
this.blockchain = blockchain this.blockchain = blockchain
this.fork = null this.fork = ''
} }
init () {} async init (): Promise<{ [id: string] : any }> { return {} }
body (): JSX.Element {
return (
<div></div>
)
}
sendAsync (data: JsonDataRequest): Promise<any> { sendAsync (data: JsonDataRequest): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

@ -55,7 +55,7 @@
"home.files": "Files", "home.files": "Files",
"home.newFile": "New File", "home.newFile": "New File",
"home.openFile": "Open File", "home.openFile": "Open File",
"home.connectToLocalhost": "Connect to Localhost", "home.connectToLocalhost": "Access File System",
"home.loadFrom": "LOAD FROM", "home.loadFrom": "Load from",
"home.resources": "Resources" "home.resources": "Resources"
} }

@ -103,6 +103,7 @@ export class RunTab extends ViewPlugin {
const addProvider = async (name, displayName, isInjected, isVM, fork = '', dataId = '', title = '') => { const addProvider = async (name, displayName, isInjected, isVM, fork = '', dataId = '', title = '') => {
await this.call('blockchain', 'addProvider', { await this.call('blockchain', 'addProvider', {
options: {},
dataId, dataId,
name, name,
displayName, displayName,
@ -110,7 +111,13 @@ export class RunTab extends ViewPlugin {
isInjected, isInjected,
isVM, isVM,
title, title,
init: () => { return this.call(name, 'init') }, init: async function () {
const options = await udapp.call(name, 'init')
if (options) {
this.options = options
if (options['fork']) this.fork = options['fork']
}
},
provider: { provider: {
async sendAsync (payload, callback) { async sendAsync (payload, callback) {
try { try {
@ -128,6 +135,8 @@ export class RunTab extends ViewPlugin {
const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.' const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.'
await addProvider('vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', 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-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM)
await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'london', 'settingsVMMainnetMode', titleVM)
// await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM)
// external provider // external provider
await addProvider('hardhat-provider', 'Hardhat Provider', false, false) await addProvider('hardhat-provider', 'Hardhat Provider', false, false)

@ -84,8 +84,6 @@ export class Blockchain extends Plugin {
setupProviders () { setupProviders () {
const vmProvider = new VMProvider(this.executionContext) const vmProvider = new VMProvider(this.executionContext)
this.providers = {} this.providers = {}
this.providers['vm-berlin'] = vmProvider
this.providers['vm-london'] = vmProvider
this.providers['vm'] = vmProvider this.providers['vm'] = vmProvider
this.providers.injected = new InjectedProvider(this.executionContext) this.providers.injected = new InjectedProvider(this.executionContext)
this.providers.web3 = new NodeProvider(this.executionContext, this.config) this.providers.web3 = new NodeProvider(this.executionContext, this.config)
@ -93,7 +91,7 @@ export class Blockchain extends Plugin {
getCurrentProvider () { getCurrentProvider () {
const provider = this.getProvider() const provider = this.getProvider()
if (provider && provider.startsWith('vm')) return this.providers['vm']
if (this.providers[provider]) return this.providers[provider] if (this.providers[provider]) return this.providers[provider]
return this.providers.web3 // default to the common type of provider return this.providers.web3 // default to the common type of provider
} }

@ -43,6 +43,10 @@ export class ExecutionContext {
return this.executionContext return this.executionContext
} }
getProviderObject () {
return this.customNetWorks[this.executionContext]
}
getSelectedAddress () { getSelectedAddress () {
return injectedProvider ? injectedProvider.selectedAddress : null return injectedProvider ? injectedProvider.selectedAddress : null
} }
@ -127,10 +131,10 @@ export class ExecutionContext {
if (!confirmCb) confirmCb = () => { /* Do nothing. */ } if (!confirmCb) confirmCb = () => { /* Do nothing. */ }
if (!infoCb) infoCb = () => { /* Do nothing. */ } if (!infoCb) infoCb = () => { /* Do nothing. */ }
if (this.customNetWorks[context]) { if (this.customNetWorks[context]) {
var network = this.customNetWorks[context] var network = this.customNetWorks[context]
await network.init()
this.currentFork = network.fork this.currentFork = network.fork
this.executionContext = context this.executionContext = context
await network.init()
// injected // injected
web3.setProvider(network.provider) web3.setProvider(network.provider)
await this._updateChainContext() await this._updateChainContext()

@ -22,7 +22,9 @@ class VMProvider {
if (this.worker) this.worker.terminate() if (this.worker) this.worker.terminate()
this.accounts = {} this.accounts = {}
this.worker = new Worker(new URL('./worker-vm', import.meta.url)) 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 let incr = 0
const stamps = {} const stamps = {}

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

@ -15,7 +15,7 @@ export class RemixEngine extends Engine {
if (name === 'hardhat') return { queueTimeout: 60000 * 4 } if (name === 'hardhat') return { queueTimeout: 60000 * 4 }
if (name === 'truffle') return { queueTimeout: 60000 * 4 } if (name === 'truffle') return { queueTimeout: 60000 * 4 }
if (name === 'localPlugin') 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 === 'sourcify') return { queueTimeout: 60000 * 4 }
if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 } if (name === 'fetchAndCompile') return { queueTimeout: 60000 * 4 }
if (name === 'walletconnect') return { queueTimeout: 60000 * 4 } if (name === 'walletconnect') return { queueTimeout: 60000 * 4 }

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

@ -2,14 +2,16 @@
'use strict' 'use strict'
import { hash } from '@remix-project/remix-lib' import { hash } from '@remix-project/remix-lib'
import { bufferToHex } from '@ethereumjs/util' import { bufferToHex } from '@ethereumjs/util'
import type { Address } from '@ethereumjs/util'
import { decode } from 'rlp' import { decode } from 'rlp'
import { ethers } from 'ethers'
import { execution } from '@remix-project/remix-lib' import { execution } from '@remix-project/remix-lib'
const { LogsManager } = execution const { LogsManager } = execution
import { VmProxy } from './VmProxy' import { VmProxy } from './VmProxy'
import { VM } from '@ethereumjs/vm' import { VM } from '@ethereumjs/vm'
import { Common } from '@ethereumjs/common' import { Common } from '@ethereumjs/common'
import { Trie } from '@ethereumjs/trie' 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 { StorageDump } from '@ethereumjs/statemanager/dist/interface'
import { EVM } from '@ethereumjs/evm' import { EVM } from '@ethereumjs/evm'
import { EEI } from '@ethereumjs/vm' import { EEI } from '@ethereumjs/vm'
@ -35,6 +37,44 @@ export interface DefaultStateManagerOpts {
prefixCodeHashes?: boolean 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 extend vm state manager and instanciate VM
*/ */
@ -86,7 +126,7 @@ class StateManagerCommonStorageDump extends DefaultStateManager {
export type CurrentVm = { export type CurrentVm = {
vm: VM, vm: VM,
web3vm: VmProxy, web3vm: VmProxy,
stateManager: StateManagerCommonStorageDump, stateManager: StateManager,
common: Common common: Common
} }
@ -105,12 +145,15 @@ export class VMContext {
web3vm: VmProxy web3vm: VmProxy
logsManager: any // LogsManager logsManager: any // LogsManager
exeResults: Record<string, Transaction> exeResults: Record<string, Transaction>
nodeUrl: string
blockNumber: number | 'latest'
constructor (fork?) { constructor (fork?: string, nodeUrl?: string, blockNumber?: number | 'latest') {
this.blockGasLimitDefault = 4300000 this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault this.blockGasLimit = this.blockGasLimitDefault
this.currentFork = fork || 'london' this.currentFork = fork || 'london'
this.nodeUrl = nodeUrl
this.blockNumber = blockNumber
this.blocks = {} this.blocks = {}
this.latestBlockNumber = "0x0" this.latestBlockNumber = "0x0"
this.blockByTxHash = {} this.blockByTxHash = {}
@ -124,7 +167,21 @@ export class VMContext {
} }
async createVm (hardfork) { async createVm (hardfork) {
const stateManager = new StateManagerCommonStorageDump() let stateManager: StateManager
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 common = new Common({ chain: 'mainnet', hardfork })
const blockchain = new (Blockchain as any)({ common }) const blockchain = new (Blockchain as any)({ common })
const eei = new EEI(stateManager, common, blockchain) const eei = new EEI(stateManager, common, blockchain)

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

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

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

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

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

@ -2,6 +2,7 @@
import React, { useEffect, useState, useContext } from 'react' import React, { useEffect, useState, useContext } from 'react'
import { FormattedMessage } from 'react-intl' import { FormattedMessage } from 'react-intl'
import { ThemeContext } from '../themeContext' import { ThemeContext } from '../themeContext'
import { CustomTooltip } from '@remix-ui/helper'
declare global { declare global {
interface Window { interface Window {
_paq: any _paq: any
@ -27,10 +28,6 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
const themeFilter = useContext(ThemeContext) 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') => { const startLearnEthTutorial = async (tutorial: 'basics' | 'soliditybeginner' | 'deploylibraries') => {
await plugin.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting']) await plugin.appManager.activatePlugin(['solidity', 'LearnEth', 'solidityUnitTesting'])
plugin.verticalIcons.select('LearnEth') plugin.verticalIcons.select('LearnEth')
@ -52,17 +49,25 @@ function HomeTabLearn ({plugin}: HomeTabLearnProps) {
return ( return (
<div className="d-flex px-2 pb-2 pt-2 d-flex flex-column" id="hTLearnSection"> <div className="d-flex px-2 pb-2 pt-2 d-flex flex-column" id="hTLearnSection">
<div className="d-flex justify-content-between"> <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" /> <FormattedMessage id="home.learn" />
</label> </label>
<button <CustomTooltip
onClick={ async () => { placement={'top'}
await goToLearnEthHome() tooltipId="overlay-tooltip"
}} tooltipClasses="text-nowrap"
className="h-100 px-2 pt-0 btn" 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>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<label className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Basics }})}> <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" /> <FormattedMessage id="home.learnEth1" />
</label> </label>
{(state.visibleTutorial === VisibleTutorial.Basics) && <div className="pt-2 d-flex flex-column text-left"> {(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" /> <FormattedMessage id="home.learnEth1Desc" />
</span> </span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('basics')}> <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" /> <FormattedMessage id="home.learnEth2" />
</label> </label>
{(state.visibleTutorial === VisibleTutorial.Intermediate) && <div className="pt-2 d-flex flex-column text-left"> {(state.visibleTutorial === VisibleTutorial.Intermediate) && <div className="pt-2 d-flex flex-column text-left">
<span> <span className="py-1" style={{fontSize: "0.8rem"}}>
<FormattedMessage id="home.learnEth2Desc" /></span> <FormattedMessage id="home.learnEth2Desc" />
</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('soliditybeginner')}> <button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('soliditybeginner')}>
<FormattedMessage id="home.getStarted" /> <FormattedMessage id="home.getStarted" />
</button> </button>
</div>} </div>}
</label> </label>
<label className="d-flex flex-column btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Advanced }})}> <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" /> <FormattedMessage id="home.remixAdvanced" />
</label> </label>
{(state.visibleTutorial === VisibleTutorial.Advanced) && <div className="pt-2 d-flex flex-column text-left"> {(state.visibleTutorial === VisibleTutorial.Advanced) && <div className="pt-2 d-flex flex-column text-left">
<span> <span className="py-1" style={{fontSize: "0.8rem"}}>
<FormattedMessage id="home.remixAdvancedDesc" /></span> <FormattedMessage id="home.remixAdvancedDesc" />
</span>
<button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('deploylibraries')}> <button className="btn btn-sm btn-secondary mt-2" style={{width: 'fit-content'}} onClick={() => startLearnEthTutorial('deploylibraries')}>
<FormattedMessage id="home.getStarted" /> <FormattedMessage id="home.getStarted" />
</button> </button>
</div>} </div>}
</label> </label>
</div> </div>

@ -8,27 +8,28 @@ function HomeTabScamAlert () {
return ( return (
<div className="" id="hTScamAlertSection"> <div className="" id="hTScamAlertSection">
<label className="pl-2 text-danger" style={{fontSize: "1.2rem"}}><FormattedMessage id='home.scamAlert' /></label> <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"> <div className="py-2 ml-2 mb-1 align-self-end mb-2 d-flex border border-danger">
<span className="pl-4 mt-2"> <span className="align-self-center pl-4 mt-1">
<i className="pr-2 text-danger fas fa-exclamation-triangle"></i> <i style={{fontSize: 'xxx-large', fontWeight: 'lighter'}} className="pr-2 text-danger far 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>
</span> </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>
</div> </div>
) )

@ -52,19 +52,22 @@ function HomeTabTitle() {
return ( return (
<div className="px-2 pb-2 pt-2 d-flex flex-column border-bottom" id="hTTitleSection"> <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}} > <div className="d-flex py-2 justify-content-between">
<BasicLogo classList="align-self-end remixui_home_logoImg" solid={false} /> <div className='d-flex justify-content-start'>
</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">
<span className="h-80 text-uppercase" style={{ fontSize: 'xx-large', fontFamily: "Noah, sans-serif" }}>Remix</span> <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> <span>
<CustomTooltip <CustomTooltip
placement={'top'} placement={'top'}
@ -94,7 +97,7 @@ function HomeTabTitle() {
openLink("https://twitter.com/EthereumRemix") openLink("https://twitter.com/EthereumRemix")
_paq.push(['trackEvent', 'hometab', 'socialMedia', 'twitter']) _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> </button>
</CustomTooltip> </CustomTooltip>
@ -110,7 +113,7 @@ function HomeTabTitle() {
openLink("https://www.linkedin.com/company/ethereum-remix/") openLink("https://www.linkedin.com/company/ethereum-remix/")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'linkedin']) _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> </button>
</CustomTooltip> </CustomTooltip>
@ -126,7 +129,7 @@ function HomeTabTitle() {
openLink("https://medium.com/remix-ide") openLink("https://medium.com/remix-ide")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'medium']) _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> </button>
</CustomTooltip> </CustomTooltip>
@ -142,12 +145,12 @@ function HomeTabTitle() {
openLink("https://gitter.im/ethereum/remix") openLink("https://gitter.im/ethereum/remix")
_paq.push(['trackEvent', 'hometab', 'socialmedia', 'gitter']) _paq.push(['trackEvent', 'hometab', 'socialmedia', 'gitter'])
}} }}
className="border-0 h-100 pl-2 btn fab fa-gitter"> className="border-0 h-100 p-2 btn fab fa-gitter">
</button> </button>
</CustomTooltip> </CustomTooltip>
</span> </span>
</div> </div>
<b className="pb-1 text-dark" style={{ fontStyle: 'italic' }}> <b className="py-1 text-dark" style={{ fontStyle: 'italic' }}>
<FormattedMessage id="home.nativeIDE" /> <FormattedMessage id="home.nativeIDE" />
</b> </b>
<div className="pb-1" id="hTGeneralLinks"> <div className="pb-1" id="hTGeneralLinks">

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

@ -9,7 +9,7 @@ export interface ModalDialogProps {
timestamp?: number, timestamp?: number,
title?: string | JSX.Element, title?: string | JSX.Element,
validation?: ValidationResult validation?: ValidationResult
validationFn?: (value: string) => ValidationResult validationFn?: (value: any) => ValidationResult
message?: string | JSX.Element, message?: string | JSX.Element,
okLabel?: string | JSX.Element, okLabel?: string | JSX.Element,
okFn?: (value?:any) => void, okFn?: (value?:any) => void,

@ -28,7 +28,7 @@ function _traverse(graph, visited, ast, name) {
for (const dependency of dependencies) { for (const dependency of dependencies) {
const path = resolve(name, dependency); const path = resolve(name, dependency);
if (path in visited) { if (path in visited) {
continue; // continue; // fixes wrong ordering of source in flattened file
} }
visited[path] = 1; visited[path] = 1;
graph.add(name, path); graph.add(name, path);

@ -27,7 +27,7 @@ export const contextMenuActions: MenuItems = [{
},{ },{
id: 'download', id: 'download',
name: 'Download', name: 'Download',
type: ['file'], type: ['file', 'folder'],
multiselect: false, multiselect: false,
label: '' label: ''
}, { }, {

@ -19,7 +19,7 @@
"target": "ES2015", "target": "ES2015",
"module": "ES2020", "module": "ES2020",
"typeRoots": ["node_modules/@types", "libs/**/*.d.ts"], "typeRoots": ["node_modules/@types", "libs/**/*.d.ts"],
"lib": ["es2017", "es2019", "dom"], "lib": ["es2017", "es2019", "dom", "dom.iterable"],
"skipLibCheck": true, "skipLibCheck": true,
"skipDefaultLibCheck": true, "skipDefaultLibCheck": true,
"baseUrl": "." "baseUrl": "."

@ -15,7 +15,8 @@
"module": "CommonJS", "module": "CommonJS",
"lib": [ "lib": [
"es2017", "es2017",
"dom" "dom",
"dom.iterable"
], ],
"skipLibCheck": true, "skipLibCheck": true,
"skipDefaultLibCheck": true, "skipDefaultLibCheck": true,

Loading…
Cancel
Save