Merge branch 'master' into pluginupdate

pull/3370/head
bunsenstraat 2 years ago committed by GitHub
commit 0efef58167
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 221
      apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx
  2. 2
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  3. 3
      apps/remix-ide/src/app/providers/injected-provider.tsx
  4. 2
      apps/remix-ide/src/app/tabs/locales/en/settings.json
  5. 27
      apps/remix-ide/src/app/udapp/run-tab.js
  6. 2
      apps/remix-ide/src/remixAppManager.js
  7. 31
      libs/remix-core-plugin/src/lib/compiler-artefacts.ts
  8. 37
      libs/remix-core-plugin/src/lib/compiler-fetch-and-compile.ts
  9. 10
      libs/remix-lib/src/util.ts
  10. 8
      libs/remix-lib/test/util.ts
  11. 2
      libs/remix-simulator/src/provider.ts
  12. 4
      libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx
  13. 2
      libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx

@ -6,131 +6,130 @@ import { PermissionHandlerDialog, PermissionHandlerValue } from '@remix-ui/permi
import { Profile } from '@remixproject/plugin-utils'
const profile = {
name: 'permissionhandler',
displayName: 'permissionhandler',
description: 'Plugin to handle permissions',
methods: ['askPermission']
name: 'permissionhandler',
displayName: 'permissionhandler',
description: 'Plugin to handle permissions',
methods: ['askPermission']
}
export class PermissionHandlerPlugin extends Plugin {
permissions: any
currentVersion: number
fallbackMemory: boolean
constructor() {
super(profile)
this.fallbackMemory = false
this.permissions = this._getFromLocal()
this.currentVersion = 1
// here we remove the old permissions saved before adding 'permissionVersion'
// since with v1 the structure has been changed because of new engine ^0.2.0-alpha.6 changes
if (!localStorage.getItem('permissionVersion')) {
localStorage.setItem('plugins/permissions', '')
localStorage.setItem('permissionVersion', this.currentVersion.toString())
}
permissions: any
currentVersion: number
fallbackMemory: boolean
constructor() {
super(profile)
this.fallbackMemory = false
this.permissions = this._getFromLocal()
this.currentVersion = 1
// here we remove the old permissions saved before adding 'permissionVersion'
// since with v1 the structure has been changed because of new engine ^0.2.0-alpha.6 changes
if (!localStorage.getItem('permissionVersion')) {
localStorage.setItem('plugins/permissions', '')
localStorage.setItem('permissionVersion', this.currentVersion.toString())
}
}
_getFromLocal() {
if (this.fallbackMemory) return this.permissions
const permission = localStorage.getItem('plugins/permissions')
return permission ? JSON.parse(permission) : {}
}
_getFromLocal() {
if (this.fallbackMemory) return this.permissions
const permission = localStorage.getItem('plugins/permissions')
return permission ? JSON.parse(permission) : {}
}
persistPermissions() {
const permissions = JSON.stringify(this.permissions)
try {
localStorage.setItem('plugins/permissions', permissions)
} catch (e) {
this.fallbackMemory = true
console.log(e)
}
persistPermissions() {
const permissions = JSON.stringify(this.permissions)
try {
localStorage.setItem('plugins/permissions', permissions)
} catch (e) {
this.fallbackMemory = true
console.log(e)
}
}
switchMode (from: Profile, to: Profile, method: string, set: boolean) {
set
? this.permissions[to.name][method][from.name] = {}
: delete this.permissions[to.name][method][from.name]
}
switchMode (from: Profile, to: Profile, method: string, set: boolean) {
set
? this.permissions[to.name][method][from.name] = {}
: delete this.permissions[to.name][method][from.name]
}
clear() {
localStorage.removeItem('plugins/permissions')
}
clear() {
localStorage.removeItem('plugins/permissions')
}
notAllowWarning(from: Profile, to: Profile, method: string) {
return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.`
}
notAllowWarning(from: Profile, to: Profile, method: string) {
return `${from.displayName || from.name} is not allowed to call ${method} method of ${to.displayName || to.name}.`
}
async getTheme() {
return (await this.call('theme', 'currentTheme')).quality
}
async getTheme() {
return (await this.call('theme', 'currentTheme')).quality
}
/**
* Check if a plugin has the permission to call another plugin and askPermission if needed
* @param {PluginProfile} from the profile of the plugin that make the call
* @param {ModuleProfile} to The profile of the module that receive the call
* @param {string} method The name of the function to be called
* @param {string} message from the caller plugin to add more details if needed
* @returns {Promise<boolean>}
*/
async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
try {
this.permissions = this._getFromLocal()
if (!this.permissions[to.name]) this.permissions[to.name] = {}
if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {}
if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall)
/**
* Check if a plugin has the permission to call another plugin and askPermission if needed
* @param {PluginProfile} from the profile of the plugin that make the call
* @param {ModuleProfile} to The profile of the module that receive the call
* @param {string} method The name of the function to be called
* @param {string} message from the caller plugin to add more details if needed
* @returns {Promise<boolean>}
*/
async askPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
try {
this.permissions = this._getFromLocal()
if (!this.permissions[to.name]) this.permissions[to.name] = {}
if (!this.permissions[to.name][method]) this.permissions[to.name][method] = {}
if (!this.permissions[to.name][method][from.name]) return this.openPermission(from, to, method, message, sensitiveCall)
const { allow, hash } = this.permissions[to.name][method][from.name]
if (!allow) {
const warning = this.notAllowWarning(from, to, method)
this.call('notification', 'toast', warning)
return false
}
return hash === from.hash
? true // Allow
: await this.openPermission(from, to, method, message, sensitiveCall)
} catch (err) {
throw new Error(err)
}
const { allow, hash } = this.permissions[to.name][method][from.name]
if (!allow) {
const warning = this.notAllowWarning(from, to, method)
this.call('notification', 'toast', warning)
return false
}
return hash === from.hash
? true // Allow
: await this.openPermission(from, to, method, message, sensitiveCall)
} catch (err) {
throw new Error(err)
}
}
async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
const remember = this.permissions[to.name][method][from.name]
const value: PermissionHandlerValue = {
from,
to,
method,
message,
remember,
sensitiveCall
async openPermission(from: Profile, to: Profile, method: string, message: string, sensitiveCall: boolean) {
const remember = this.permissions[to.name][method][from.name]
const value: PermissionHandlerValue = {
from,
to,
method,
message,
remember,
sensitiveCall
}
const modal: AppModal = {
id: 'PermissionHandler',
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />,
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>,
okLabel: <FormattedMessage id='permissionHandler.accept' />,
cancelLabel: <FormattedMessage id='permissionHandler.decline' />
}
const result = await this.call('notification', 'modal', modal)
return new Promise((resolve, reject) => {
if (result) {
if (this.permissions[to.name][method][from.name]) {
this.permissions[to.name][method][from.name] = {
allow: true,
hash: from.hash
}
this.persistPermissions()
}
const modal: AppModal = {
id: 'PermissionHandler',
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />,
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>,
okLabel: <FormattedMessage id='permissionHandler.accept' />,
cancelLabel: <FormattedMessage id='permissionHandler.decline' />
resolve(true)
} else {
if (this.permissions[to.name][method][from.name]) {
this.permissions[to.name][method][from.name] = {
allow: false,
hash: from.hash
}
this.persistPermissions()
}
const result = await this.call('notification', 'modal', modal)
return new Promise((resolve, reject) => {
if (result) {
if (this.permissions[to.name][method][from.name]) {
this.permissions[to.name][method][from.name] = {
allow: true,
hash: from.hash
}
this.persistPermissions()
}
resolve(true)
} else {
if (this.permissions[to.name][method][from.name]) {
this.permissions[to.name][method][from.name] = {
allow: false,
hash: from.hash
}
this.persistPermissions()
}
reject(this.notAllowWarning(from, to, method))
}
})
}
reject(this.notAllowWarning(from, to, method))
}
})
}
}

@ -39,7 +39,7 @@ export class CustomForkVMProvider extends BasicVMProvider {
</div>
<div>
<label className="mt-3 mb-1">EVM</label>
<select data-id="CustomForkEvmType" name="evmType" className="border form-control border-right-0">
<select data-id="CustomForkEvmType" name="evmType" defaultValue="merge" className="border form-control border-right-0">
{Object.keys(Hardfork).map((value, index) => {
return <option value={Hardfork[value]} key={index}>{value}</option>
})}

@ -35,10 +35,11 @@ export class InjectedProvider extends Plugin implements IProvider {
async init () {
const injectedProvider = (window as any).ethereum
if (injectedProvider === undefined) {
this.call('notification', 'toast', noInjectedProviderMsg)
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).')
if (!await injectedProvider._metamask.isUnlocked()) this.call('notification', 'toast', 'Please make sure the injected provider is unlocked (e.g Metamask).')
}
this.askPermission(true)
}

@ -3,7 +3,7 @@
"settings.reset": "Reset to Default settings",
"settings.general": "General settings",
"settings.generateContractMetadataText": "Generate contract metadata. Generate a JSON file in the contract folder. Allows to specify library addresses the contract depends on. If nothing is specified, Remix deploys libraries automatically.",
"settings.ethereunVMText": "Always use Javascript VM at load",
"settings.ethereunVMText": "Always use the Remix VM at load",
"settings.wordWrapText": "Word wrap in editor",
"settings.useAutoCompleteText": "Enable code completion in editor.",
"settings.useShowGasInEditorText": "Display gas estimates in editor.",

@ -131,6 +131,12 @@ export class RunTab extends ViewPlugin {
})
}
// basic injected
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)
// 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)
@ -142,20 +148,17 @@ export class RunTab extends ViewPlugin {
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('basic-http-provider', 'Custom - External Http Provider', false, false)
await addProvider('hardhat-provider', 'Dev - Hardhat Provider', false, false)
await addProvider('ganache-provider', 'Dev - Ganache Provider', false, false)
await addProvider('foundry-provider', 'Dev - Foundry Provider', false, false)
// injected provider
await addProvider('injected-optimism-provider', 'L2 - Optimism Provider', true, false)
await addProvider('injected-arbitrum-one-provider', 'L2 - Arbitrum One Provider', true, 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) {

@ -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', 'injected-optimism-provider', 'injected-arbitrum-one-provider',
'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin',
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener']
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)

@ -5,7 +5,7 @@ import { CompilerAbstract } from '@remix-project/remix-solidity'
const profile = {
name: 'compilerArtefacts',
methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName', 'getContractDataFromAddress'],
methods: ['get', 'addResolvedContract', 'getCompilerAbstract', 'getAllContractDatas', 'getLastCompilationResult', 'getArtefactsByContractName', 'getContractDataFromAddress', 'getContractDataFromByteCode'],
events: [],
version: '0.0.1'
}
@ -98,16 +98,17 @@ export class CompilerArtefacts extends Plugin {
filterAllContractDatas (filter) {
const contractsData = {}
Object.keys(this.compilersArtefactsPerFile).map((targetFile) => {
const contracts = this.compilersArtefactsPerFile[targetFile].getContracts()
const artefact = this.compilersArtefactsPerFile[targetFile]
const contracts = artefact.getContracts()
Object.keys(contracts).map((file) => {
if (filter(file, contracts[file])) contractsData[file] = contracts[file]
if (filter(file, contracts[file], artefact)) contractsData[file] = contracts[file]
})
})
// making sure we save last compilation result in there
if (this.compilersArtefacts.__last) {
const contracts = this.compilersArtefacts.__last.getContracts()
Object.keys(contracts).map((file) => {
if (filter(file, contracts[file])) contractsData[file] = contracts[file]
if (filter(file, contracts[file], this.compilersArtefacts.__last)) contractsData[file] = contracts[file]
})
}
return contractsData
@ -194,8 +195,20 @@ export class CompilerArtefacts extends Plugin {
}
}
getCompilerAbstract (file) {
return this.compilersArtefactsPerFile[file]
async getCompilerAbstract (file) {
if (!file) return null
if (this.compilersArtefactsPerFile[file]) return this.compilersArtefactsPerFile[file]
const path = await this.call('fileManager', 'getPathFromUrl', file)
if (path && path.file && this.compilersArtefactsPerFile[path.file]) return this.compilersArtefactsPerFile[path.file]
let artefact = null
this.filterAllContractDatas((localFile, data, parentArtefact) => {
if (localFile === file || (path && path.file && localFile === path.file)) {
artefact = parentArtefact
}
})
return artefact
}
addResolvedContract (address: string, compilerData: CompilerAbstract) {
@ -212,12 +225,16 @@ export class CompilerArtefacts extends Plugin {
async getContractDataFromAddress (address) {
const code = await this.call('blockchain', 'getCode', address)
return this.getContractDataFromByteCode(code)
}
async getContractDataFromByteCode (code) {
let found
this.filterAllContractDatas((file, contractsData) => {
for (const name of Object.keys(contractsData)) {
const contract = contractsData[name]
if (util.compareByteCode(code, '0x' + contract.evm.deployedBytecode.object)) {
found = { name, contract }
found = { name, contract, file }
return true
}
}

@ -43,7 +43,14 @@ export class FetchAndCompile extends Plugin {
async resolve (contractAddress, codeAtAddress, targetPath) {
contractAddress = toChecksumAddress(contractAddress)
const localCompilation = async () => await this.call('compilerArtefacts', 'get', contractAddress) ? await this.call('compilerArtefacts', 'get', contractAddress) : await this.call('compilerArtefacts', 'get', '__last') ? await this.call('compilerArtefacts', 'get', '__last') : null
const localCompilation = async () => {
const contractData = await this.call('compilerArtefacts', 'getContractDataFromByteCode', codeAtAddress)
if (contractData) {
return await this.call('compilerArtefacts', 'getCompilerAbstract', contractData.file)
}
else
return await this.call('compilerArtefacts', 'get', '__last')
}
const resolved = await this.call('compilerArtefacts', 'get', contractAddress)
if (resolved) return resolved
@ -87,20 +94,20 @@ export class FetchAndCompile extends Plugin {
return localCompilation()
}
if (!network) return localCompilation()
if (!this.sourceVerifierNetWork.includes(network.name)) return localCompilation()
// check if the contract if part of the local compilation result
const compilation = await localCompilation()
if (compilation) {
let found = false
compilation.visitContracts((contract) => {
found = util.compareByteCode('0x' + contract.object.evm.deployedBytecode.object, codeAtAddress)
return found
})
if (found) {
await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation)
setTimeout(_ => this.emit('usingLocalCompilation', contractAddress), 0)
return compilation
if (!this.sourceVerifierNetWork.includes(network.name)) {
// check if the contract if part of the local compilation result
const compilation = await localCompilation()
if (compilation) {
let found = false
compilation.visitContracts((contract) => {
found = util.compareByteCode('0x' + contract.object.evm.deployedBytecode.object, codeAtAddress)
return found
})
if (found) {
await this.call('compilerArtefacts', 'addResolvedContract', contractAddress, compilation)
setTimeout(_ => this.emit('usingLocalCompilation', contractAddress), 0)
return compilation
}
}
}

@ -258,6 +258,12 @@ export function compareByteCode (code1, code2) {
code2 = extractcborMetadata(code2)
if (code1 && code2) {
if (code1.length !== code2.length) {
// if the length isn't the same, we have an issue with extracting the metadata hash.
const minLength = code1.length > code2.length ? code2.length: code1.length
code1 = code1.substr(0, minLength - 10)
code2 = code2.substr(0, minLength - 10)
}
const compare = stringSimilarity.compareTwoStrings(code1, code2)
return compare == 1
}
@ -294,12 +300,12 @@ function removeByIndex (code, index, length, emptyRef) {
function removeImmutableReference (code1, code2) {
try {
const refOccurence = code2.match(/7f000000000000000000000000000000000000000000000000000000000000000073/g)
const refOccurence = code2.match(/7f0000000000000000000000000000000000000000000000000000000000000000/g)
if (!refOccurence) return code1
let offset = 0
refOccurence.map((value) => {
offset = code2.indexOf(value, offset)
code1 = removeByIndex(code1, offset, value.length, '7f000000000000000000000000000000000000000000000000000000000000000073')
code1 = removeByIndex(code1, offset, value.length, '7f0000000000000000000000000000000000000000000000000000000000000000')
offset = offset + 1
})
} catch (e) {

File diff suppressed because one or more lines are too long

@ -23,7 +23,7 @@ export class Provider {
constructor (options: Record<string, string | number> = {}) {
this.options = options
this.connected = true
this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as number)
this.vmContext = new VMContext(options['fork'] as string, options['nodeUrl'] as string, options['blockNumber'] as (number | 'latest'))
this.Accounts = new Web3Accounts(this.vmContext)
this.Transactions = new Transactions(this.vmContext)

@ -98,7 +98,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
{/* todo add autofocus ^^ */}
{ props.okLabel && <button
data-id={`${props.id}-modal-footer-ok-react`}
className={'modal-ok btn btn-sm ' + (props.okBtnClass ? props.okBtnClass : state.toggleBtn ? 'btn-dark' : 'btn-light')}
className={'modal-ok btn btn-sm ' + (props.okBtnClass ? props.okBtnClass : state.toggleBtn ? 'border-primary' : 'border-secondary')}
disabled={props.validation && !props.validation.valid}
onClick={() => {
if (props.validation && !props.validation.valid) return
@ -111,7 +111,7 @@ export const ModalDialog = (props: ModalDialogProps) => {
}
{ props.cancelLabel && <button
data-id={`${props.id}-modal-footer-cancel-react`}
className={'modal-cancel btn btn-sm ' + (props.cancelBtnClass ? props.cancelBtnClass : state.toggleBtn ? 'btn-light' : 'btn-dark')}
className={'modal-cancel btn btn-sm ' + (props.cancelBtnClass ? props.cancelBtnClass : state.toggleBtn ? 'border-secondary' : 'border-primary')}
data-dismiss="modal"
onClick={() => {
if (props.cancelFn) props.cancelFn()

@ -62,7 +62,7 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
<label htmlFor='remember' className="form-check-label" data-id="permissionHandlerRememberChoice"><FormattedMessage id='permissionHandler.rememberThisChoice' /></label>
</div>
}
<button className="btn btn-sm" onClick={reset}><FormattedMessage id='permissionHandler.resetAllPermissions' /></button>
<button className="btn-secondary btn-sm" onClick={reset}><FormattedMessage id='permissionHandler.resetAllPermissions' /></button>
</article>
<div>{feedback}</div>
</section>)

Loading…
Cancel
Save