diff --git a/apps/remix-ide-e2e/src/tests/proxy.test.ts b/apps/remix-ide-e2e/src/tests/proxy.test.ts index d6d51ed57d..6707b60627 100644 --- a/apps/remix-ide-e2e/src/tests/proxy.test.ts +++ b/apps/remix-ide-e2e/src/tests/proxy.test.ts @@ -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') diff --git a/apps/remix-ide/src/app/components/preload.tsx b/apps/remix-ide/src/app/components/preload.tsx index 6674b1bcfd..f607264d63 100644 --- a/apps/remix-ide/src/app/components/preload.tsx +++ b/apps/remix-ide/src/app/components/preload.tsx @@ -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) }) } diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 4183f2ec3a..9a0d58e2e5 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -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() } } diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index 95860a0ee4..c4523551be 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -795,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) } diff --git a/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx b/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx index 3131f07020..2c473a86f3 100644 --- a/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx +++ b/apps/remix-ide/src/app/plugins/solidity-umlgen.tsx @@ -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 = () => {} @@ -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 { 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('} */ 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 } } diff --git a/apps/remix-ide/src/blockchain/blockchain.js b/apps/remix-ide/src/blockchain/blockchain.js index 785d5f05d1..0047b786f9 100644 --- a/apps/remix-ide/src/blockchain/blockchain.js +++ b/apps/remix-ide/src/blockchain/blockchain.js @@ -180,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) } @@ -236,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: { @@ -260,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)) diff --git a/libs/remix-core-plugin/src/types/contract.ts b/libs/remix-core-plugin/src/types/contract.ts index a327539a60..cccce84203 100644 --- a/libs/remix-core-plugin/src/types/contract.ts +++ b/libs/remix-core-plugin/src/types/contract.ts @@ -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 + } diff --git a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx index 61c27bce8e..04d31c4839 100644 --- a/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx +++ b/libs/remix-ui/helper/src/lib/components/custom-dropdown.tsx @@ -55,3 +55,46 @@ export const CustomMenu = React.forwardRef( ) }, ) + +export const ProxyAddressToggle = React.forwardRef(({ address, onClick, className = '', onChange }: { address: string, onClick: (e) => void, className: string, onChange: (e: React.ChangeEvent) => void }, ref: Ref) => ( +
{ + e.preventDefault() + onClick(e) + }} + className={'d-flex '+ className.replace('dropdown-toggle', '')} + data-id="toggleProxyAddressDropdown" + > + { + e.preventDefault() + onChange(e) + }} + className="udapp_input form-control" + value={address} + placeholder="Enter Proxy Address" + style={{ width: '100%' }} + data-id="ERC1967AddressInput" + /> +
+)) + +export const ProxyDropdownMenu = React.forwardRef( + ({ children, style, className, 'aria-labelledby': labeledBy }: { children: React.ReactNode, style?: React.CSSProperties, className: string, 'aria-labelledby'?: string }, ref: Ref) => { + return ( +
+
    + { + children + } +
+
+ ) + }, +) diff --git a/libs/remix-ui/helper/src/lib/helper-components.tsx b/libs/remix-ui/helper/src/lib/helper-components.tsx index ba482551c9..707538e707 100644 --- a/libs/remix-ui/helper/src/lib/helper-components.tsx +++ b/libs/remix-ui/helper/src/lib/helper-components.tsx @@ -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 = () => ( ) + +export const unavailableProxyLayoutMsg = () => ( +
+

Previous contract implementation is NOT available for upgrade comparison.
A new storage layout will be saved for future upgrades.

+
+) + +export const upgradeReportMsg = (report: LayoutCompatibilityReport) => ( +
+
+ + + +
+ The storage layout of new implementation is NOT + compatible with the previous implementation. + Your contract's storage may be partially or fully erased! +
+
+
+ { report.explain() } +
+
+) diff --git a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts index d9d0f193ce..3573f42d20 100644 --- a/libs/remix-ui/helper/src/lib/remix-ui-helper.ts +++ b/libs/remix-ui/helper/src/lib/remix-ui-helper.ts @@ -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' }) +} diff --git a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx index 936956c62a..4d74918338 100644 --- a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx +++ b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx @@ -98,7 +98,7 @@ export const ModalDialog = (props: ModalDialogProps) => { {/* todo add autofocus ^^ */} { props.okLabel && - -
-
- - } - tooltipId="proxyAddressTooltip" - placement="auto" - tooltipClasses="text-wrap" - > - - -
- { - !useLastProxy ? +
+ + + + { props.proxy.deployments.length > 0 && + + { + props.proxy.deployments.map((deployment, index) => ( + + { + switchProxyAddress(deployment.address) + }} + data-id={`proxyAddress${index}`} + > + { proxyAddress === deployment.address ? ✓ { deployment.contractName + ' ' + shortenProxyAddress(deployment.address) } : { deployment.contractName + ' ' + shortenProxyAddress(deployment.address) } } + + + )) + } + + } + +
+
- - }> - validateProxyAddress(proxyAddress) } /> - - { proxyAddressError && { proxyAddressError } } -
: - { proxyAddress || proxyAddressError } - } + { proxyAddressError && { proxyAddressError } } +
- + ) : null} ); diff --git a/libs/remix-ui/run-tab/src/lib/constants/index.ts b/libs/remix-ui/run-tab/src/lib/constants/index.ts index bfe5a76715..dbd04e9e27 100644 --- a/libs/remix-ui/run-tab/src/lib/constants/index.ts +++ b/libs/remix-ui/run-tab/src/lib/constants/index.ts @@ -44,5 +44,7 @@ export const ADD_DEPLOY_OPTION = 'ADD_DEPLOY_OPTION' export const REMOVE_DEPLOY_OPTION = 'REMOVE_DEPLOY_OPTION' export const SET_DEPLOY_OPTIONS = 'SET_DEPLOY_OPTIONS' export const SET_CURRENT_CONTRACT = 'SET_CURRENT_CONTRACT' -export const SET_PROXY_ENV_ADDRESS = 'SET_PROXY_ENV_ADDRESS' export const SET_REMIXD_ACTIVATED = 'SET_REMIXD_ACTIVATED' +export const FETCH_PROXY_DEPLOYMENTS = 'FETCH_PROXY_DEPLOYMENTS' +export const NEW_PROXY_DEPLOYMENT = 'NEW_PROXY_DEPLOYMENT' +export const RESET_PROXY_DEPLOYMENTS = 'RESET_PROXY_DEPLOYMENTS' diff --git a/libs/remix-ui/run-tab/src/lib/css/run-tab.css b/libs/remix-ui/run-tab/src/lib/css/run-tab.css index 203e315de1..5ff98e898c 100644 --- a/libs/remix-ui/run-tab/src/lib/css/run-tab.css +++ b/libs/remix-ui/run-tab/src/lib/css/run-tab.css @@ -362,8 +362,6 @@ } .udapp_contractProperty button:disabled { cursor: not-allowed; - background-color: white; - border-color: lightgray; } .udapp_contractProperty.udapp_constant button { min-width: 100px; diff --git a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts index 3dfa5a921b..0495fc479e 100644 --- a/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts +++ b/libs/remix-ui/run-tab/src/lib/reducers/runTab.ts @@ -1,108 +1,12 @@ -import { CompilerAbstract } from '@remix-project/remix-solidity' import { ContractData } from '@remix-project/core-plugin' -import { DeployOptions } from '../types' -import { ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, SET_PROXY_ENV_ADDRESS, ADD_DEPLOY_OPTION, REMOVE_DEPLOY_OPTION, SET_REMIXD_ACTIVATED } from '../constants' +import { ContractList, DeployOptions, RunTabState } from '../types' +import { ADD_INSTANCE, ADD_PROVIDER, CLEAR_INSTANCES, CLEAR_RECORDER_COUNT, DISPLAY_NOTIFICATION, DISPLAY_POPUP_MESSAGE, FETCH_ACCOUNTS_LIST_FAILED, FETCH_ACCOUNTS_LIST_REQUEST, FETCH_ACCOUNTS_LIST_SUCCESS, FETCH_CONTRACT_LIST_FAILED, FETCH_CONTRACT_LIST_REQUEST, FETCH_CONTRACT_LIST_SUCCESS, FETCH_PROVIDER_LIST_FAILED, FETCH_PROVIDER_LIST_REQUEST, FETCH_PROVIDER_LIST_SUCCESS, HIDE_NOTIFICATION, HIDE_POPUP_MESSAGE, REMOVE_INSTANCE, REMOVE_PROVIDER, RESET_STATE, SET_BASE_FEE_PER_GAS, SET_CONFIRM_SETTINGS, SET_CURRENT_CONTRACT, SET_CURRENT_FILE, SET_DECODED_RESPONSE, SET_DEPLOY_OPTIONS, SET_EXECUTION_ENVIRONMENT, SET_EXTERNAL_WEB3_ENDPOINT, SET_GAS_LIMIT, SET_GAS_PRICE, SET_GAS_PRICE_STATUS, SET_IPFS_CHECKED_STATE, SET_LOAD_TYPE, SET_MATCH_PASSPHRASE, SET_MAX_FEE, SET_MAX_PRIORITY_FEE, SET_NETWORK_NAME, SET_PASSPHRASE, SET_PATH_TO_SCENARIO, SET_PERSONAL_MODE, SET_RECORDER_COUNT, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, ADD_DEPLOY_OPTION, REMOVE_DEPLOY_OPTION, SET_REMIXD_ACTIVATED, FETCH_PROXY_DEPLOYMENTS, NEW_PROXY_DEPLOYMENT, RESET_PROXY_DEPLOYMENTS } from '../constants' declare const window: any interface Action { type: string payload: any } -export interface Contract { - name: string, - alias: string, - file: string, - compiler: CompilerAbstract, - compilerName: string -} - -export interface ContractList { - [file: string]: Contract[] -} -export interface RunTabState { - accounts: { - loadedAccounts: Record, - isRequesting: boolean, - isSuccessful: boolean, - error: string, - selectedAccount: string - }, - sendValue: string, - sendUnit: 'ether' | 'finney' | 'gwei' | 'wei', - gasLimit: number, - selectExEnv: string, - personalMode: boolean, - networkName: string, - providers: { - providerList: { - id?: string, - dataId?: string, - title?: string, - value: string, - fork?: string - content: string - }[], - isRequesting: boolean, - isSuccessful: boolean, - error: string - }, - notification: { - title: string, - message: string, - actionOk: () => void, - actionCancel: (() => void) | null, - labelOk: string, - labelCancel: string - }, - externalEndpoint: string, - popup: string, - passphrase: string, - matchPassphrase: string, - contracts: { - contractList: { - [file: string]: { - name: string, - alias: string, - file: string, - compiler: CompilerAbstract - compilerName: string - }[] - }, - deployOptions: { [file: string]: { [name: string]: DeployOptions } }, - proxyKey: string, - loadType: 'abi' | 'sol' | 'other' - currentFile: string, - compilationSource: string, - currentContract: string, - compilationCount: number, - isRequesting: boolean, - isSuccessful: boolean, - error: string - }, - ipfsChecked: boolean, - gasPriceStatus: boolean, - confirmSettings: boolean, - maxFee: string, - maxPriorityFee: string, - baseFeePerGas: string, - gasPrice: string, - instances: { - instanceList: { - contractData?: ContractData, - address: string, - balance?: number, - name: string, - decodedResponse?: Record, - abi?: any - }[], - error: string - }, - recorder: { - pathToScenario: string, - transactionCount: number - } - remixdActivated: boolean -} export const runTabInitialState: RunTabState = { accounts: { @@ -139,7 +43,6 @@ export const runTabInitialState: RunTabState = { contracts: { contractList: {}, deployOptions: {} as any, - proxyKey: '', compilationSource: '', loadType: 'other', currentFile: '', @@ -164,7 +67,10 @@ export const runTabInitialState: RunTabState = { pathToScenario: 'scenario.json', transactionCount: 0 }, - remixdActivated: false + remixdActivated: false, + proxy: { + deployments: [] + } } type AddProvider = { @@ -697,23 +603,45 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A } } - case SET_PROXY_ENV_ADDRESS: { - const payload: string = action.payload + case SET_REMIXD_ACTIVATED: { + const payload: boolean = action.payload + return { + ...state, + remixdActivated: payload + } + } + + case FETCH_PROXY_DEPLOYMENTS: { + const payload: { address: string, date: string, contractName: string }[] = action.payload return { ...state, - contracts: { - ...state.contracts, - proxyKey: payload + proxy: { + ...state.proxy, + deployments: payload } } } - case SET_REMIXD_ACTIVATED: { - const payload: boolean = action.payload + case NEW_PROXY_DEPLOYMENT: { + const payload: { address: string, date: string, contractName: string } = action.payload + return { ...state, - remixdActivated: payload + proxy: { + ...state.proxy, + deployments: [...state.proxy.deployments, payload] + } + } + } + + case RESET_PROXY_DEPLOYMENTS: { + return { + ...state, + proxy: { + ...state.proxy, + deployments: [] + } } } diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index e5fb961364..d01c86d6c4 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -27,7 +27,7 @@ import { storeNewScenario, runScenario, setScenarioPath, getFuncABIValues, setNetworkName, updateSelectedContract, - syncContracts, isValidProxyAddress + syncContracts, isValidProxyAddress, isValidProxyUpgrade } from './actions' import './css/run-tab.css' import { PublishToStorage } from '@remix-ui/publish-to-storage' @@ -81,7 +81,9 @@ export function RunTabUI (props: RunTabProps) { okLabel: modals[0].okLabel, okFn: modals[0].okFn, cancelLabel: modals[0].cancelLabel, - cancelFn: modals[0].cancelFn + cancelFn: modals[0].cancelFn, + okBtnClass: modals[0].okBtnClass, + cancelBtnClass: modals[0].cancelBtnClass } return focusModal }) @@ -120,9 +122,9 @@ export function RunTabUI (props: RunTabProps) { dispatch(setIpfsCheckedState(value)) } - const modal = (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => { + const modal = (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void, okBtnClass?: string, cancelBtnClass?: string) => { setModals(modals => { - modals.push({ message, title, okLabel, okFn, cancelLabel, cancelFn }) + modals.push({ message, title, okLabel, okFn, cancelLabel, cancelFn, okBtnClass, cancelBtnClass }) return [...modals] }) } @@ -242,6 +244,8 @@ export function RunTabUI (props: RunTabProps) { setSelectedContract={updateSelectedContract} remixdActivated={runTab.remixdActivated} isValidProxyAddress={isValidProxyAddress} + isValidProxyUpgrade={isValidProxyUpgrade} + proxy={runTab.proxy} /> { txRunner: any; networkcallid: number; networkStatus: { - name: string; - id: string; + network: { + name: string; + id: string; + }; }; setupEvents(): void; getCurrentNetworkStatus(): { diff --git a/libs/remix-ui/run-tab/src/lib/types/index.ts b/libs/remix-ui/run-tab/src/lib/types/index.ts index 86b328a61a..cbde8cd7da 100644 --- a/libs/remix-ui/run-tab/src/lib/types/index.ts +++ b/libs/remix-ui/run-tab/src/lib/types/index.ts @@ -1,12 +1,111 @@ import { Ref } from 'react' import { CompilerAbstract } from '@remix-project/remix-solidity' import { ContractData, FuncABI } from '@remix-project/core-plugin' -import { ContractList } from '../reducers/runTab' import { RunTab } from './run-tab' +import { SolcInput, SolcOutput } from '@openzeppelin/upgrades-core' +import { LayoutCompatibilityReport } from '@openzeppelin/upgrades-core/dist/storage/report' export interface RunTabProps { plugin: RunTab } +export interface Contract { + name: string, + alias: string, + file: string, + compiler: CompilerAbstract, + compilerName: string +} + +export interface ContractList { + [file: string]: Contract[] +} +export interface RunTabState { + accounts: { + loadedAccounts: Record, + isRequesting: boolean, + isSuccessful: boolean, + error: string, + selectedAccount: string + }, + sendValue: string, + sendUnit: 'ether' | 'finney' | 'gwei' | 'wei', + gasLimit: number, + selectExEnv: string, + personalMode: boolean, + networkName: string, + providers: { + providerList: { + id?: string, + dataId?: string, + title?: string, + value: string, + fork?: string + content: string + }[], + isRequesting: boolean, + isSuccessful: boolean, + error: string + }, + notification: { + title: string, + message: string, + actionOk: () => void, + actionCancel: (() => void) | null, + labelOk: string, + labelCancel: string + }, + externalEndpoint: string, + popup: string, + passphrase: string, + matchPassphrase: string, + contracts: { + contractList: { + [file: string]: { + name: string, + alias: string, + file: string, + compiler: CompilerAbstract + compilerName: string + }[] + }, + deployOptions: { [file: string]: { [name: string]: DeployOptions } }, + loadType: 'abi' | 'sol' | 'other' + currentFile: string, + compilationSource: string, + currentContract: string, + compilationCount: number, + isRequesting: boolean, + isSuccessful: boolean, + error: string + }, + ipfsChecked: boolean, + gasPriceStatus: boolean, + confirmSettings: boolean, + maxFee: string, + maxPriorityFee: string, + baseFeePerGas: string, + gasPrice: string, + instances: { + instanceList: { + contractData?: ContractData, + address: string, + balance?: number, + name: string, + decodedResponse?: Record, + abi?: any + }[], + error: string + }, + recorder: { + pathToScenario: string, + transactionCount: number + } + remixdActivated: boolean, + proxy: { + deployments: { address: string, date: string, contractName: string }[] + } +} + export interface SettingsProps { selectExEnv: string, accounts: { @@ -41,7 +140,7 @@ export interface SettingsProps { createNewBlockchainAccount: (cbMessage: JSX.Element) => void, setPassphrase: (passphrase: string) => void, setMatchPassphrase: (passphrase: string) => void, - modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, + modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void, okBtnClass?: string, cancelBtnClass?: string) => void, tooltip: (toasterMsg: string) => void, signMessageWithAddress: (account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => void, passphrase: string, @@ -85,7 +184,7 @@ export interface AccountProps { setPassphrase: (passphrase: string) => void, setMatchPassphrase: (passphrase: string) => void, tooltip: (toasterMsg: string) => void, - modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, + modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void, okBtnClass?: string, cancelBtnClass?: string) => void, signMessageWithAddress: (account: string, message: string, modalContent: (hash: string, data: string) => JSX.Element, passphrase?: string) => void, passphrase: string } @@ -129,7 +228,6 @@ export interface ContractDropdownProps { contracts: { contractList: ContractList, deployOptions: { [file: string]: { [name: string]: DeployOptions } }, - proxyKey: string, loadType: 'abi' | 'sol' | 'other', currentFile: string, compilationSource: string @@ -141,7 +239,7 @@ export interface ContractDropdownProps { }, syncContracts: () => void, getSelectedContract: (contractName: string, compiler: CompilerAbstract) => ContractData, - modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, + modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void, okBtnClass?: string, cancelBtnClass?: string) => void, passphrase: string, setPassphrase: (passphrase: string) => void, createInstance: ( @@ -166,7 +264,9 @@ export interface ContractDropdownProps { setNetworkName: (name: string) => void, setSelectedContract: (contractName: string) => void remixdActivated: boolean, - isValidProxyAddress?: (address: string) => Promise + isValidProxyAddress?: (address: string) => Promise, + isValidProxyUpgrade?: (proxyAddress: string, contractName: string, solcInput: SolcInput, solcOuput: SolcOutput) => Promise, + proxy: { deployments: { address: string, date: string, contractName: string }[] } } export interface RecorderProps { @@ -223,7 +323,9 @@ export interface Modal { okLabel: string okFn: () => void cancelLabel: string - cancelFn: () => void + cancelFn: () => void, + okBtnClass?: string, + cancelBtnClass?: string } export type DeployMode = 'Deploy with Proxy' | 'Upgrade with Proxy' @@ -261,8 +363,10 @@ export interface ContractGUIProps { isDeploy?: boolean, deployOption?: { title: DeployMode, active: boolean }[], initializerOptions?: DeployOption, - savedProxyAddress?: string, - isValidProxyAddress?: (address: string) => Promise + proxy?: { deployments: { address: string, date: string, contractName: string }[] }, + isValidProxyAddress?: (address: string) => Promise, + isValidProxyUpgrade?: (proxyAddress: string) => Promise, + modal?: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void, okBtnClass?: string, cancelBtnClass?: string) => void } export interface MainnetProps { network: Network, diff --git a/libs/remix-ui/solidity-uml-gen/src/lib/css/solidity-uml-gen.css b/libs/remix-ui/solidity-uml-gen/src/lib/css/solidity-uml-gen.css index f8be12bc6b..bc9cf7fd0e 100644 --- a/libs/remix-ui/solidity-uml-gen/src/lib/css/solidity-uml-gen.css +++ b/libs/remix-ui/solidity-uml-gen/src/lib/css/solidity-uml-gen.css @@ -1,3 +1,9 @@ .remixui_default-message { margin-top: 100px; +} + +.remixui_no-shadow { + border-width: 1px; + border-style: solid; + border-color: var(--info); } \ No newline at end of file diff --git a/libs/remix-ui/solidity-uml-gen/src/lib/solidity-uml-gen.tsx b/libs/remix-ui/solidity-uml-gen/src/lib/solidity-uml-gen.tsx index 223a281e77..38580353e9 100644 --- a/libs/remix-ui/solidity-uml-gen/src/lib/solidity-uml-gen.tsx +++ b/libs/remix-ui/solidity-uml-gen/src/lib/solidity-uml-gen.tsx @@ -1,55 +1,29 @@ -import { CustomTooltip } from '@remix-ui/helper' import React, { Fragment, useEffect, useState } from 'react' import { TransformComponent, TransformWrapper } from 'react-zoom-pan-pinch' -import { ISolidityUmlGen } from '../types' +import { ThemeSummary } from '../types' import './css/solidity-uml-gen.css' export interface RemixUiSolidityUmlGenProps { - plugin?: ISolidityUmlGen updatedSvg?: string loading?: boolean themeSelected?: string -} - -type ButtonAction = { - svgValid: () => boolean - action: () => void - buttonText: string - icon?: string - customcss?: string + themeName: string + themeCollection: ThemeSummary[] } interface ActionButtonsProps { - buttons: ButtonAction[] + actions: { + zoomIn: () => void, + zoomOut: () => void, + resetTransform: () => void + } } -const ActionButtons = ({ buttons }: ActionButtonsProps) => ( - <> - {buttons.map(btn => ( - - - - ))} - -) -export function RemixUiSolidityUmlGen ({ plugin, updatedSvg, loading, themeSelected }: RemixUiSolidityUmlGenProps) { - const [showViewer, setShowViewer] = useState(false) - const [svgPayload, setSVGPayload] = useState('') - const [validSvg, setValidSvg] = useState(false) +export function RemixUiSolidityUmlGen ({ updatedSvg, loading }: RemixUiSolidityUmlGenProps) { + const [showViewer, setShowViewer] = useState(false) + const [validSvg, setValidSvg] = useState(false) useEffect(() => { setValidSvg (updatedSvg.startsWith(' validSvg, - action: () => console.log('generated!!'), - icon: 'fa mr-1 pt-1 pb-1 fa-file' - }, - { - buttonText: 'Download as PNG', - svgValid: () => validSvg, - action: () => console.log('generated!!'), - icon: 'fa fa-picture-o' - } - ] + + const encoder = new TextEncoder() + const data = encoder.encode(updatedSvg) + const final = btoa(String.fromCharCode.apply(null, data)) + + function ActionButtons({ actions: { zoomIn, zoomOut, resetTransform }}: ActionButtonsProps) { + + return ( + <> +
+
+ + + + +
+
+ + ) + } const DefaultInfo = () => (
@@ -80,10 +84,8 @@ export function RemixUiSolidityUmlGen ({ plugin, updatedSvg, loading, themeSelec
) const Display = () => { - const invert = themeSelected === 'dark' ? 'invert(0.8)' : 'invert(0)' return ( -
-
+
{ validSvg && showViewer ? ( ( - - + + - + ) } @@ -107,7 +111,6 @@ export function RemixUiSolidityUmlGen ({ plugin, updatedSvg, loading, themeSelec
: }
-
)} return (<> { } diff --git a/libs/remix-ui/solidity-uml-gen/src/types/index.ts b/libs/remix-ui/solidity-uml-gen/src/types/index.ts index 6423075344..7f0dd41736 100644 --- a/libs/remix-ui/solidity-uml-gen/src/types/index.ts +++ b/libs/remix-ui/solidity-uml-gen/src/types/index.ts @@ -1,4 +1,5 @@ import { ViewPlugin } from '@remixproject/engine-web' +import { customAction } from '@remixproject/plugin-api' import React from 'react' export interface ISolidityUmlGen extends ViewPlugin { @@ -6,9 +7,22 @@ export interface ISolidityUmlGen extends ViewPlugin { currentFile: string svgPayload: string updatedSvg: string + currentlySelectedTheme: string + themeName: string + loading: boolean + themeCollection: ThemeSummary[] showUmlDiagram(path: string, svgPayload: string): void updateComponent(state: any): JSX.Element setDispatch(dispatch: React.Dispatch): void + mangleSvgPayload(svgPayload: string) : Promise + generateCustomAction(action: customAction): Promise + flattenContract (source: any, filePath: string, data: any): Promise + hideSpinner(): void + renderComponent (): void render(): JSX.Element -} \ No newline at end of file +} + +export type ThemeQualityType = { name: string, quality: 'light' | 'dark', url: string } + +export type ThemeSummary = { themeName: string, backgroundColor: string, actualHex: string } \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/actions/events.ts b/libs/remix-ui/workspace/src/lib/actions/events.ts index 7a9ed8a416..5c9b03141c 100644 --- a/libs/remix-ui/workspace/src/lib/actions/events.ts +++ b/libs/remix-ui/workspace/src/lib/actions/events.ts @@ -112,16 +112,22 @@ export const listenOnProviderEvents = (provider) => (reducerDispatch: React.Disp const config = plugin.registry.get('config').api const editor = plugin.registry.get('editor').api - if (config.get('currentFile') === path && editor.currentContent() !== content) { - if (provider.isReadOnly(path)) return editor.setText(content) + if (editor.getText(path) === content) return + if (provider.isReadOnly(path)) return editor.setText(path, content) + + if (config.get('currentFile') === path) { + // if it's the current file and the content is different: dispatch(displayNotification( path + ' changed', 'This file has been changed outside of Remix IDE.', 'Replace by the new content', 'Keep the content displayed in Remix', () => { - editor.setText(content) + editor.setText(path, content) } )) + } else { + // this isn't the current file, we can silently update the model + editor.setText(path, content) } }) diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index 4fd90d38fa..8b3b1f14e6 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -374,7 +374,7 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error, const editor = plugin.registry.get('editor').api if ((config.get('currentFile') === name) && (editor.currentContent() !== event.target.result)) { - editor.setText(event.target.result) + editor.setText(name, event.target.result) } } fileReader.readAsText(file) diff --git a/libs/remixd/src/services/remixdClient.ts b/libs/remixd/src/services/remixdClient.ts index b8a03ef375..a3f3d9dd8f 100644 --- a/libs/remixd/src/services/remixdClient.ts +++ b/libs/remixd/src/services/remixdClient.ts @@ -1,5 +1,5 @@ import { PluginClient } from '@remixproject/plugin' -import { SharedFolderArgs, TrackDownStreamUpdate, Filelist, ResolveDirectory, FileContent } from '../types' // eslint-disable-line +import { SharedFolderArgs, Filelist, ResolveDirectory, FileContent } from '../types' // eslint-disable-line import * as WS from 'ws' // eslint-disable-line import * as utils from '../utils' import * as chokidar from 'chokidar' @@ -8,7 +8,6 @@ import * as isbinaryfile from 'isbinaryfile' export class RemixdClient extends PluginClient { methods: Array - trackDownStreamUpdate: TrackDownStreamUpdate = {} websocket: WS currentSharedFolder: string watcher: chokidar.FSWatcher @@ -101,7 +100,6 @@ export class RemixdClient extends PluginClient { console.log('trying to write "undefined" ! stopping.') return reject(new Error('trying to write "undefined" ! stopping.')) } - this.trackDownStreamUpdate[path] = path if (!exists && args.path.indexOf('/') !== -1) { // the last element is the filename and we should remove it this.createDir({ path: args.path.substr(0, args.path.lastIndexOf('/')) }) @@ -250,7 +248,7 @@ export class RemixdClient extends PluginClient { const absPath = utils.absolutePath('./', path) if (!isRealPath(absPath)) return - this.watcher = chokidar.watch(path, { depth: 0, ignorePermissionErrors: true }) + this.watcher = chokidar.watch(path, { depth: 2, ignorePermissionErrors: true }) console.log('setup notifications for ' + path) /* we can't listen on created file / folder watcher.on('add', (f, stat) => { @@ -265,11 +263,9 @@ export class RemixdClient extends PluginClient { }) */ this.watcher.on('change', async (f: string) => { - if (this.trackDownStreamUpdate[f]) { - delete this.trackDownStreamUpdate[f] - return - } - if (this.isLoaded) { + const currentContent = await this.call('editor', 'getText' as any, f) + const newContent = fs.readFileSync(f) + if (currentContent !== newContent && this.isLoaded) { this.emit('changed', utils.relativePath(f, this.currentSharedFolder)) } }) diff --git a/libs/remixd/src/types/index.ts b/libs/remixd/src/types/index.ts index 66818dff48..48d4640de3 100644 --- a/libs/remixd/src/types/index.ts +++ b/libs/remixd/src/types/index.ts @@ -42,8 +42,6 @@ export type FileContent = { readonly: boolean } -export type TrackDownStreamUpdate = KeyPairString - export type SharedFolderArgs = FolderArgs & KeyPairString export type WS = typeof Websocket diff --git a/package.json b/package.json index 1f2574caf5..1bb34a3c05 100644 --- a/package.json +++ b/package.json @@ -130,6 +130,7 @@ "@isomorphic-git/lightning-fs": "^4.4.1", "@monaco-editor/react": "4.4.5", "@openzeppelin/contracts": "^4.7.3", + "@openzeppelin/upgrades-core": "^1.22.0", "@openzeppelin/wizard": "^0.1.1", "@remixproject/engine": "^0.3.31", "@remixproject/engine-web": "^0.3.31", diff --git a/yarn.lock b/yarn.lock index 6c1b25db3a..afada6be4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5090,6 +5090,19 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e" integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw== +"@openzeppelin/upgrades-core@^1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.22.0.tgz#41ffda6a9161845fc6b82bd945e530529feefa00" + integrity sha512-TcTabzRbYOzWJnwiToj0LRzje25d9QbDPe2dOT9eHlLDRhOMiep39FDibJjkYd5IdF3s8M9IcK+YSnf49renEg== + dependencies: + cbor "^8.0.0" + chalk "^4.1.0" + compare-versions "^5.0.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + "@openzeppelin/wizard@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@openzeppelin/wizard/-/wizard-0.1.1.tgz#8c183e2c5748869bc3a5317c0330aa36a9ad44fe" @@ -9161,6 +9174,13 @@ catering@^2.1.0, catering@^2.1.1: resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== +cbor@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== + dependencies: + nofilter "^3.1.0" + center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" @@ -9847,6 +9867,11 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" +compare-versions@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.3.tgz#a9b34fea217472650ef4a2651d905f42c28ebfd7" + integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -12299,6 +12324,17 @@ ethereumjs-util@^7.0.10, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.3: ethereum-cryptography "^0.1.3" rlp "^2.2.4" +ethereumjs-util@^7.0.3: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + ethers@^5.4.2: version "5.5.1" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.5.1.tgz#d3259a95a42557844aa543906c537106c0406fbf" @@ -19579,6 +19615,11 @@ nodent@>=2.6.12: nodent-runtime "^3.2.1" resolve "^1.5.0" +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== + "nopt@2 || 3", nopt@~3.0.1: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -21894,6 +21935,15 @@ prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proper-lockfile@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -23231,6 +23281,11 @@ retry@^0.10.0, retry@~0.10.1: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + retry@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -24107,6 +24162,11 @@ solc@0.7.4: semver "^5.5.0" tmp "0.0.33" +solidity-ast@^0.4.15: + version "0.4.44" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.44.tgz#dd6732bd65bb1d01777fc537de99cbb3d4e0089d" + integrity sha512-Ct3ppqWS0uTWNYxM2cgruUeWYzqYmeikANsCHgGBnMjAMsqONgqnYrlpifQxNFwXOPHD3vZQLmCjaYnQ+i3eQA== + solidity-comments-extractor@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19"