Merge branch 'master' of https://github.com/ethereum/remix-project into desktop-master-git
@ -0,0 +1,34 @@ |
|||||||
|
import { NightwatchBrowser } from 'nightwatch' |
||||||
|
import EventEmitter from 'events' |
||||||
|
|
||||||
|
class HideMetaMaskPopup extends EventEmitter { |
||||||
|
command(this: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.pause(5000) |
||||||
|
.isVisible({ |
||||||
|
selector: 'button[data-testid="popover-close"]', |
||||||
|
locateStrategy: 'css selector', |
||||||
|
suppressNotFoundErrors: true, |
||||||
|
timeout: 2000 |
||||||
|
}, (okVisible) => { |
||||||
|
console.log('okVisible', okVisible) |
||||||
|
if (!okVisible.value) { |
||||||
|
console.log('popover not found') |
||||||
|
} else { |
||||||
|
console.log('popover found... closing') |
||||||
|
browser.click('button[data-testid="popover-close"]') |
||||||
|
} |
||||||
|
}) |
||||||
|
.waitForElementNotPresent({ |
||||||
|
selector: 'button[data-testid="popover-close"]', |
||||||
|
locateStrategy: 'css selector', |
||||||
|
timeout: 2000 |
||||||
|
}) |
||||||
|
.perform((done) => { |
||||||
|
done() |
||||||
|
this.emit('complete') |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = HideMetaMaskPopup |
@ -0,0 +1,20 @@ |
|||||||
|
import { NightwatchBrowser } from 'nightwatch' |
||||||
|
import EventEmitter from 'events' |
||||||
|
|
||||||
|
class pinGrid extends EventEmitter { |
||||||
|
command (this: NightwatchBrowser, provider: string, status: boolean): NightwatchBrowser { |
||||||
|
this.api.useCss().waitForElementVisible('[data-id="settingsSelectEnvOptions"]') |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') |
||||||
|
.waitForElementVisible(`[data-id="dropdown-item-another-chain"]`) |
||||||
|
.click(`[data-id="dropdown-item-another-chain"]`) |
||||||
|
.waitForElementVisible(`[data-id="${provider}-${status ? 'unpinned' : 'pinned'}"]`) |
||||||
|
.click(`[data-id="${provider}-${status ? 'unpinned' : 'pinned'}"]`) |
||||||
|
.perform((done) => { |
||||||
|
done() |
||||||
|
this.emit('complete') |
||||||
|
}) |
||||||
|
return this |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = pinGrid |
@ -1,7 +1,9 @@ |
|||||||
|
|
||||||
cd /tmp/ |
cd /tmp/ |
||||||
rm -rf git/bare.git |
rm -rf git/bare.git |
||||||
|
rm -rf git/bare2.git |
||||||
rm -rf git |
rm -rf git |
||||||
mkdir -p git |
mkdir -p git |
||||||
cd git |
cd git |
||||||
git clone --bare https://github.com/ethereum/awesome-remix bare.git |
git clone --bare https://github.com/ethereum/awesome-remix bare.git |
||||||
|
git clone --bare https://github.com/ethereum/awesome-remix bare2.git |
||||||
|
@ -0,0 +1,36 @@ |
|||||||
|
'use strict' |
||||||
|
import { NightwatchBrowser } from 'nightwatch' |
||||||
|
import init from '../helpers/init' |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||||
|
init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false) |
||||||
|
}, |
||||||
|
'pin chain': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.clickLaunchIcon('udapp') |
||||||
|
.pinGrid('vm-custom-fork', true) |
||||||
|
.waitForElementVisible('[data-id="settingsSelectEnvOptions"]') |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') |
||||||
|
.waitForElementVisible(`[data-id="dropdown-item-vm-custom-fork"]`) |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') // close the dropdown
|
||||||
|
.pinGrid('vm-sepolia-fork', true) |
||||||
|
.waitForElementVisible('[data-id="settingsSelectEnvOptions"]') |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') |
||||||
|
.waitForElementVisible(`[data-id="dropdown-item-vm-sepolia-fork"]`) |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') // close the dropdown
|
||||||
|
}, |
||||||
|
'unpin chain': function (browser: NightwatchBrowser) { |
||||||
|
browser |
||||||
|
.pinGrid('vm-custom-fork', false) |
||||||
|
.waitForElementVisible('[data-id="settingsSelectEnvOptions"]') |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') |
||||||
|
.waitForElementNotPresent(`[data-id="dropdown-item-vm-custom-fork"]`) |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') // close the dropdown
|
||||||
|
.pinGrid('vm-sepolia-fork', false) |
||||||
|
.waitForElementVisible('[data-id="settingsSelectEnvOptions"]') |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') |
||||||
|
.waitForElementNotPresent(`[data-id="dropdown-item-vm-sepolia-fork"]`) |
||||||
|
.click('[data-id="settingsSelectEnvOptions"] button') // close the dropdown
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
.TSCellStyle { |
||||||
|
min-height: 8.5rem; |
||||||
|
max-width: 13rem; |
||||||
|
min-width: 13rem; |
||||||
|
max-height: 8.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
.badgeForCell { |
||||||
|
max-width: fit-content; |
||||||
|
padding-right: 0.5rem; |
||||||
|
font-size: smaller; |
||||||
|
} |
@ -0,0 +1,269 @@ |
|||||||
|
|
||||||
|
import React from 'react' |
||||||
|
import { FormattedMessage, useIntl } from 'react-intl' |
||||||
|
import { CustomTooltip } from "@remix-ui/helper" |
||||||
|
import { AppModal } from '@remix-ui/app' |
||||||
|
import { ViewPlugin } from '@remixproject/engine-web' |
||||||
|
import { PluginViewWrapper } from '@remix-ui/helper' |
||||||
|
import { RemixUIGridView } from '@remix-ui/remix-ui-grid-view' |
||||||
|
import { RemixUIGridSection } from '@remix-ui/remix-ui-grid-section' |
||||||
|
import { RemixUIGridCell } from '@remix-ui/remix-ui-grid-cell' |
||||||
|
import isElectron from 'is-electron' |
||||||
|
import type { TemplateGroup } from '@remix-ui/workspace' |
||||||
|
import './templates-selection-plugin.css' |
||||||
|
import { templates } from './templates' |
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
const _paq = (window._paq = window._paq || []) |
||||||
|
|
||||||
|
const profile = { |
||||||
|
name: 'templateSelection', |
||||||
|
displayName: 'Template Selection', |
||||||
|
description: 'templateSelection', |
||||||
|
location: 'mainPanel', |
||||||
|
methods: [], |
||||||
|
events: [], |
||||||
|
maintainedBy: 'Remix', |
||||||
|
} |
||||||
|
|
||||||
|
export class TemplatesSelectionPlugin extends ViewPlugin { |
||||||
|
templates: Array<TemplateGroup> |
||||||
|
dispatch: React.Dispatch<any> = () => { } |
||||||
|
constructor() { |
||||||
|
super(profile) |
||||||
|
} |
||||||
|
|
||||||
|
async onActivation() { |
||||||
|
this.handleThemeChange() |
||||||
|
await this.call('tabs', 'focus', 'templateSelection') |
||||||
|
this.renderComponent() |
||||||
|
_paq.push(['trackEvent', 'plugin', 'activated', 'remixGuide']) |
||||||
|
} |
||||||
|
|
||||||
|
onDeactivation(): void { |
||||||
|
} |
||||||
|
|
||||||
|
private handleThemeChange() { |
||||||
|
this.on('theme', 'themeChanged', (theme: any) => { |
||||||
|
this.renderComponent() |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
setDispatch(dispatch: React.Dispatch<any>): void { |
||||||
|
this.dispatch = dispatch |
||||||
|
this.renderComponent() |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
return ( |
||||||
|
<div className="bg-dark" id="remixGuide"> |
||||||
|
<PluginViewWrapper plugin={this} /> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
renderComponent() { |
||||||
|
this.dispatch({ |
||||||
|
...this, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
updateComponent() { |
||||||
|
/* |
||||||
|
This represents the different options available from the openzeppelin library. |
||||||
|
const opts = { |
||||||
|
// @ts-ignore: Object is possibly 'null'.
|
||||||
|
mintable: mintableCheckboxRef.current.checked, |
||||||
|
// @ts-ignore: Object is possibly 'null'.
|
||||||
|
burnable: burnableCheckboxRef.current.checked, |
||||||
|
// @ts-ignore: Object is possibly 'null'.
|
||||||
|
pausable: pausableCheckboxRef.current.checked, |
||||||
|
// @ts-ignore: Object is possibly 'null'.
|
||||||
|
upgradeable: transparentRadioRef.current.checked ? transparentRadioRef.current.value : uupsRadioRef.current.checked ? uupsRadioRef.current.value : false |
||||||
|
} |
||||||
|
*/ |
||||||
|
const createWorkspace = async (item) => { |
||||||
|
const defaultName = await this.call('filePanel', 'getAvailableWorkspaceName', item.displayName) |
||||||
|
|
||||||
|
const username = await this.call('settings', 'get', 'settings/github-user-name') |
||||||
|
const email = await this.call('settings', 'get', 'settings/github-email') |
||||||
|
const gitNotSet = !username || !email |
||||||
|
let workspaceName = defaultName |
||||||
|
let initGit = false |
||||||
|
const modal: AppModal = { |
||||||
|
id: 'TemplatesSelection', |
||||||
|
title: window._intl.formatMessage({ id: !isElectron() ? 'filePanel.workspace.create': 'filePanel.workspace.create.desktop' }), |
||||||
|
message: await createModalMessage(defaultName, gitNotSet, (value) => workspaceName = value, (value) => initGit = !!value), |
||||||
|
okLabel: window._intl.formatMessage({ id: !isElectron() ? 'filePanel.ok':'filePanel.selectFolder' }), |
||||||
|
} |
||||||
|
const modalResult = await this.call('notification', 'modal', modal) |
||||||
|
if (!modalResult) return |
||||||
|
this.emit('createWorkspaceReducerEvent', workspaceName, item.value, item.opts, false, async (e, data) => { |
||||||
|
if (e) { |
||||||
|
const modal: AppModal = { |
||||||
|
id: 'TemplatesSelection', |
||||||
|
title: window._intl.formatMessage({ id: !isElectron() ? 'filePanel.workspace.create': 'filePanel.workspace.create.desktop' }), |
||||||
|
message: e.message, |
||||||
|
okLabel: window._intl.formatMessage({ id: 'filePanel.ok' }), |
||||||
|
cancelLabel: window._intl.formatMessage({ id: 'filePanel.cancel' }) |
||||||
|
} |
||||||
|
await this.call('notification', 'modal', modal) |
||||||
|
console.error(e) |
||||||
|
} |
||||||
|
|
||||||
|
}, initGit) |
||||||
|
} |
||||||
|
|
||||||
|
const addToCurrentWorkspace = async (item) => { |
||||||
|
this.emit('addTemplateToWorkspaceReducerEvent', item.value, item.opts, false, async (e, data) => { |
||||||
|
if (e) { |
||||||
|
const modal: AppModal = { |
||||||
|
id: 'TemplatesSelection', |
||||||
|
title: window._intl.formatMessage({ id: !isElectron() ? 'filePanel.workspace.create': 'filePanel.workspace.create.desktop' }), |
||||||
|
message: e.message, |
||||||
|
okLabel: window._intl.formatMessage({ id: 'filePanel.ok' }), |
||||||
|
cancelLabel: window._intl.formatMessage({ id: 'filePanel.cancel' }) |
||||||
|
} |
||||||
|
await this.call('notification', 'modal', modal) |
||||||
|
console.error(e) |
||||||
|
} else { |
||||||
|
this.call('notification', 'toast', 'Files Added.') |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<RemixUIGridView |
||||||
|
plugin={this} |
||||||
|
styleList={""} |
||||||
|
logo='/assets/img/bgRemi.webp' |
||||||
|
enableFilter={true} |
||||||
|
showUntagged={true} |
||||||
|
showPin={false} |
||||||
|
tagList={[ |
||||||
|
['Solidity', 'danger'], |
||||||
|
['ZKP', 'warning'], |
||||||
|
['ERC20', 'success'], |
||||||
|
['ERC721', 'secondary'], |
||||||
|
['ERC1155', 'primary'], |
||||||
|
]} |
||||||
|
title='Template explorer' |
||||||
|
description="Select a template to create a workspace or to add it to current workspace" |
||||||
|
> |
||||||
|
{ |
||||||
|
templates(window._intl).map(template => { |
||||||
|
return <RemixUIGridSection |
||||||
|
plugin={this} |
||||||
|
key={template.name} |
||||||
|
title={template.name} |
||||||
|
hScrollable={false} |
||||||
|
> |
||||||
|
{template.items.map(item => { |
||||||
|
return <RemixUIGridCell |
||||||
|
plugin={this} |
||||||
|
title={item.displayName} |
||||||
|
key={item.name} |
||||||
|
id={item.name} |
||||||
|
searchKeywords={[item.displayName, item.description, template.name]} |
||||||
|
tagList={item.tagList} |
||||||
|
classList='TSCellStyle' |
||||||
|
> |
||||||
|
<div className='d-flex justify-content-between h-100 flex-column'> |
||||||
|
<div className='d-flex flex-column'> |
||||||
|
<div> |
||||||
|
{item.description && <span className='text-dark'>{item.description}</span>} |
||||||
|
</div> |
||||||
|
<div className='d-flex flex-wrap'> |
||||||
|
{(item.opts && item.opts.upgradeable && item.opts.upgradeable === 'uupds') && <span className='badgeForCell badge text-secondary'>Upgradeable-UUPS</span>} |
||||||
|
{(item.opts && item.opts.mintable) && <span className='badgeForCell text-secondary'>mintable</span>} |
||||||
|
{(item.opts && item.opts.burnable) && <span className='badgeForCell text-secondary'>burnable</span>} |
||||||
|
{(item.opts && item.opts.pausable) && <span className='badgeForCell text-secondary'>pausable</span>} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div className='align-items-center justify-content-between w-100 d-flex pt- flex-row'> |
||||||
|
{(!template.IsArtefact || !item.isArtefact) && <CustomTooltip |
||||||
|
placement="auto" |
||||||
|
tooltipId={`overlay-tooltip-new${item.name}`} |
||||||
|
tooltipText="Create a new workspace" |
||||||
|
> |
||||||
|
<span |
||||||
|
data-id={`create-${item.value}${item.opts ? JSON.stringify(item.opts) : ''}`} |
||||||
|
onClick={async () => createWorkspace(item)} |
||||||
|
className="btn btn-sm mr-2 border border-primary" |
||||||
|
> |
||||||
|
Create |
||||||
|
</span> |
||||||
|
</CustomTooltip>} |
||||||
|
<CustomTooltip |
||||||
|
placement="auto" |
||||||
|
tooltipId={`overlay-tooltip-add${item.name}`} |
||||||
|
tooltipText="Add template files to current workspace" |
||||||
|
> |
||||||
|
<span |
||||||
|
data-id={`add-${item.value}`} |
||||||
|
onClick={async () => addToCurrentWorkspace(item)} |
||||||
|
className="btn btn-sm border" |
||||||
|
> |
||||||
|
Add to current |
||||||
|
</span> |
||||||
|
</CustomTooltip> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</RemixUIGridCell> |
||||||
|
})} |
||||||
|
</RemixUIGridSection> |
||||||
|
})} |
||||||
|
</RemixUIGridView> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const createModalMessage = async ( |
||||||
|
defaultName: string, |
||||||
|
gitConfigNotSet: boolean, |
||||||
|
onChangeTemplateName: (name: string) => void, |
||||||
|
onChangeInitGit: (name: string) => void) => { |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<label id="wsName" className="form-check-label" style={{ fontWeight: 'bolder' }}> |
||||||
|
<FormattedMessage id="filePanel.workspaceName" /> |
||||||
|
</label> |
||||||
|
<input |
||||||
|
type="text" |
||||||
|
data-id="modalDialogCustomPromptTextCreate" |
||||||
|
defaultValue={defaultName} |
||||||
|
className="form-control" |
||||||
|
onChange={(e) => onChangeTemplateName(e.target.value)} |
||||||
|
onInput={(e) => onChangeTemplateName((e.target as any).value)} |
||||||
|
/> |
||||||
|
<div className="d-flex py-2 align-items-center custom-control custom-checkbox"> |
||||||
|
<input |
||||||
|
id="initGitRepository" |
||||||
|
data-id="initGitRepository" |
||||||
|
className="form-check-input custom-control-input" |
||||||
|
type="checkbox" |
||||||
|
disabled={gitConfigNotSet} |
||||||
|
onChange={(e) => onChangeInitGit(e.target.value)} |
||||||
|
onInput={(e) => onChangeInitGit((e.target as any).value)} |
||||||
|
/> |
||||||
|
<label |
||||||
|
htmlFor="initGitRepository" |
||||||
|
data-id="initGitRepositoryLabel" |
||||||
|
className="m-0 form-check-label custom-control-label udapp_checkboxAlign" |
||||||
|
title={window._intl.formatMessage({ id: 'filePanel.initGitRepoTitle' })} |
||||||
|
> |
||||||
|
<FormattedMessage id="filePanel.initGitRepositoryLabel" /> |
||||||
|
</label> |
||||||
|
</div> |
||||||
|
{gitConfigNotSet ? ( |
||||||
|
<div className="text-warning"> |
||||||
|
<FormattedMessage id="filePanel.initGitRepositoryWarning" /> |
||||||
|
</div> |
||||||
|
) : ( |
||||||
|
<></> |
||||||
|
)} |
||||||
|
</> |
||||||
|
) |
||||||
|
} |
||||||
|
|
@ -0,0 +1,357 @@ |
|||||||
|
|
||||||
|
export const templates = (intl) => { |
||||||
|
return [ |
||||||
|
{ |
||||||
|
name: "Generic", |
||||||
|
items: [ |
||||||
|
{ value: "remixDefault", tagList: ["Solidity"], displayName: intl.formatMessage({ id: 'filePanel.basic' }), description: 'A default project' }, |
||||||
|
{ value: "blank", displayName: intl.formatMessage({ id: 'filePanel.blank' }), IsArtefact: true, description: 'A blank project' } |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "OpenZeppelin", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
tagList: ["ERC20", "Solidity"], |
||||||
|
description: 'A simple ERC20 project' |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
tagList: ["ERC721", "Solidity"], |
||||||
|
description: 'A simple ERC721 (aka NFT) project' |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
tagList: ["Solidity"], |
||||||
|
displayName: "ERC1155", |
||||||
|
description: 'A simple ERC1155 (multi token) project' |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
description: "A standard interface for fungible tokens", |
||||||
|
tagList: ["Solidity"], |
||||||
|
opts: { |
||||||
|
mintable: true |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
description: "Non-fungible Token Standard", |
||||||
|
tagList: ["Solidity", "ERC721"], |
||||||
|
opts: { |
||||||
|
mintable: true |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
tagList: ["Solidity"], |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
opts: { |
||||||
|
mintable: true |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
description: "A standard interface for fungible tokens", |
||||||
|
tagList: ["Solidity", "ERC20"], |
||||||
|
opts: { |
||||||
|
mintable: true, |
||||||
|
burnable: true |
||||||
|
}, |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
description: "Non-fungible Token Standard", |
||||||
|
opts: { |
||||||
|
mintable: true, |
||||||
|
burnable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC721", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
opts: { |
||||||
|
mintable: true, |
||||||
|
burnable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC1155", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
description: "A standard interface for fungible tokens", |
||||||
|
opts: { |
||||||
|
mintable: true, |
||||||
|
pausable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC20", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
description: "Non-fungible Token Standard", |
||||||
|
opts: { |
||||||
|
mintable: true, |
||||||
|
pausable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC721", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
tagList: ["ERC20"], |
||||||
|
opts: { |
||||||
|
mintable: true, |
||||||
|
pausable: true |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "OpenZeppelin Proxy", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
description: "A standard interface for fungible tokens", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups' |
||||||
|
}, |
||||||
|
tagList: ["ERC20", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
description: "Non-fungible Token Standard", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups' |
||||||
|
}, |
||||||
|
tagList: ["ERC721", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups' |
||||||
|
}, |
||||||
|
tagList: ["ERC1155", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
description: "A standard interface for fungible tokens", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC20", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
description: "Non-fungible Token Standard", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC721", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC1155", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
description: "A standard interface for fungible tokens", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true, |
||||||
|
burnable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC20", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
description: "Non-fungible Token Standard", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true, |
||||||
|
burnable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC721", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true, |
||||||
|
burnable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC1155", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc20", |
||||||
|
displayName: "ERC20", |
||||||
|
description: "A standard interface for fungible tokens", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true, |
||||||
|
pausable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC20", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc721", |
||||||
|
displayName: "ERC721 (NFT)", |
||||||
|
description: "Non-fungible Token Standard", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true, |
||||||
|
pausable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC721", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true, |
||||||
|
pausable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC1155", "Solidity"] |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "ozerc1155", |
||||||
|
displayName: "ERC1155", |
||||||
|
description: "A standard interface for contracts that manage multiple token types", |
||||||
|
opts: { |
||||||
|
upgradeable: 'uups', |
||||||
|
mintable: true, |
||||||
|
burnable: true, |
||||||
|
pausable: true |
||||||
|
}, |
||||||
|
tagList: ["ERC1155", "Solidity"] |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "OxProject", |
||||||
|
items: [ |
||||||
|
{ value: "zeroxErc20", displayName: "ERC20", tagList: ["ERC20", "Solidity"], description: "A standard interface for fungible tokens by 0xProject" } |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "Gnosis Safe", |
||||||
|
items: [ |
||||||
|
{ value: "gnosisSafeMultisig", tagList: ["Solidity"], displayName: intl.formatMessage({ id: 'filePanel.multiSigWallet' }), description: 'Deploy or Customize the Gnosis Safe.' } |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "Circom ZKP", |
||||||
|
items: [ |
||||||
|
{ value: "semaphore", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.semaphore' }), description: 'Run a ZK Semaphore circom circuit.' }, |
||||||
|
{ value: "hashchecker", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.hashchecker' }), description: 'Run a ZK Hash checker circom circuit.' }, |
||||||
|
{ value: "rln", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.rln' }), description: 'Run a Rate Limiting Nullifier circom circuit.' } |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "Generic ZKP", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
value: "sindriScripts", |
||||||
|
tagList: ["ZKP"], |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.addscriptsindri' }), |
||||||
|
description: 'Use the Sindri API to compile and generate proof.' |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "Uniswap V4", |
||||||
|
items: [ |
||||||
|
{ value: "uniswapV4Template", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.uniswapV4Template' }), |
||||||
|
description: 'Use an Uniswap hook' |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "breakthroughLabsUniswapv4Hooks", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.breakthroughLabsUniswapv4Hooks' }), |
||||||
|
description: 'Use an Uniswap hook developed by Breakthrough Labs' |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "uniswapV4HookBookMultiSigSwapHook", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.uniswapV4HookBookMultiSigSwapHook' }), |
||||||
|
description: 'Use a MultiSigSwapHook developed by Breakthrough Labs' |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "Solidity CREATE2", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
value: "contractCreate2Factory", |
||||||
|
tagList: ["Solidity"], |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.addcreate2solidityfactory' }), |
||||||
|
description: 'Factory for deploying a Contract using the CREATE2 opcode.' |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "contractDeployerScripts", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.addscriptdeployer' }), |
||||||
|
description: 'Script for deploying a Contract using the CREATE2 opcode.' |
||||||
|
} |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: "Contract Verification", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
value: "etherscanScripts", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.addscriptetherscan' }), |
||||||
|
description: 'Script for verifying a Contract in Etherscan.' |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'Github Actions', |
||||||
|
items: [ |
||||||
|
{ value: "runJsTestAction", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.tssoltestghaction' }), |
||||||
|
description: 'A Mocha Chai Test Workflow in a GitHub CI.' |
||||||
|
}, |
||||||
|
{ value: "runSolidityUnittestingAction", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.solghaction' }), |
||||||
|
description: 'Run a Solidity Unittest Workflow in a GitHub CI.' |
||||||
|
}, |
||||||
|
{ |
||||||
|
value: "runSlitherAction", |
||||||
|
displayName: intl.formatMessage({ id: 'filePanel.slitherghaction' }), |
||||||
|
description: 'Run a Slither Security Analysis in a GitHub CI.' |
||||||
|
} |
||||||
|
], |
||||||
|
IsArtefact: true |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,207 @@ |
|||||||
|
import React from 'react' // eslint-disable-line
|
||||||
|
import { ViewPlugin } from '@remixproject/engine-web' |
||||||
|
import { PluginViewWrapper } from '@remix-ui/helper' |
||||||
|
import { RemixUIGridView } from '@remix-ui/remix-ui-grid-view' |
||||||
|
import { RemixUIGridSection } from '@remix-ui/remix-ui-grid-section' |
||||||
|
import { RemixUIGridCell } from '@remix-ui/remix-ui-grid-cell' |
||||||
|
import './style/environment-explorer.css' |
||||||
|
import type { Provider } from '../../blockchain/blockchain' |
||||||
|
|
||||||
|
import * as packageJson from '../../../../../package.json' |
||||||
|
|
||||||
|
const _paq = (window._paq = window._paq || []) |
||||||
|
|
||||||
|
const profile = { |
||||||
|
name: 'environmentExplorer', |
||||||
|
displayName: 'Environment Explorer', |
||||||
|
icon: 'assets/img/EnvironmentExplorerLogo.webp', |
||||||
|
description: 'Customize the Environments list in Deploy & Run', |
||||||
|
location: 'mainPanel', |
||||||
|
documentation: 'https://remix-ide.readthedocs.io/en/latest/run.html', |
||||||
|
version: packageJson.version, |
||||||
|
maintainedBy: 'Remix', |
||||||
|
permission: true, |
||||||
|
events: [], |
||||||
|
methods: [] |
||||||
|
} |
||||||
|
|
||||||
|
type ProvidersSection = `Injected` | 'Remix VMs' | 'Externals' |
||||||
|
|
||||||
|
export class EnvironmentExplorer extends ViewPlugin { |
||||||
|
providers: { [key in ProvidersSection]: Provider[] } |
||||||
|
providersFlat: { [key: string]: Provider } |
||||||
|
pinnedProviders: string[] |
||||||
|
dispatch: React.Dispatch<any> = () => {} |
||||||
|
|
||||||
|
constructor() { |
||||||
|
super(profile) |
||||||
|
this.providersFlat = {} |
||||||
|
this.providers = { |
||||||
|
'Injected': [], |
||||||
|
'Remix VMs': [], |
||||||
|
'Externals': [] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
async onActivation(): Promise<void> { |
||||||
|
this.providersFlat = await this.call('blockchain', 'getAllProviders') |
||||||
|
this.pinnedProviders = await this.call('blockchain', 'getPinnedProviders') |
||||||
|
this.renderComponent() |
||||||
|
} |
||||||
|
|
||||||
|
addProvider (provider: Provider) { |
||||||
|
if (provider.isInjected) { |
||||||
|
this.providers['Injected'].push(provider) |
||||||
|
} else if (provider.isVM) { |
||||||
|
this.providers['Remix VMs'].push(provider) |
||||||
|
} else { |
||||||
|
this.providers['Externals'].push(provider) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
setDispatch(dispatch: React.Dispatch<any>): void { |
||||||
|
this.dispatch = dispatch |
||||||
|
this.renderComponent() |
||||||
|
} |
||||||
|
render() { |
||||||
|
return ( |
||||||
|
<div className="bg-dark" id="environmentExplorer"> |
||||||
|
<PluginViewWrapper plugin={this} /> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
renderComponent() { |
||||||
|
this.dispatch({ |
||||||
|
...this |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
updateComponent(state: any) { |
||||||
|
this.providers = { |
||||||
|
'Injected': [], |
||||||
|
'Remix VMs': [], |
||||||
|
'Externals': [] |
||||||
|
} |
||||||
|
for (const [key, provider] of Object.entries(this.providersFlat)) { |
||||||
|
this.addProvider(provider) |
||||||
|
} |
||||||
|
return ( |
||||||
|
<RemixUIGridView |
||||||
|
plugin={this} |
||||||
|
styleList={""} |
||||||
|
logo={profile.icon} |
||||||
|
enableFilter={true} |
||||||
|
showUntagged={true} |
||||||
|
showPin={true} |
||||||
|
title={profile.description} |
||||||
|
description="Select the providers and chains to include them in the ENVIRONMENT select box of the Deploy & Run Transactions plugin." |
||||||
|
> |
||||||
|
<RemixUIGridSection |
||||||
|
plugin={this} |
||||||
|
title='Deploy using a Browser Extension.' |
||||||
|
hScrollable={false} |
||||||
|
> |
||||||
|
{this.providers['Injected'].map(provider => { |
||||||
|
return <RemixUIGridCell |
||||||
|
plugin={this} |
||||||
|
title={provider.displayName} |
||||||
|
logos={provider.logos} |
||||||
|
classList='EECellStyle' |
||||||
|
searchKeywords={['Injected', provider.name, provider.displayName, provider.title, provider.description]} |
||||||
|
pinned={this.pinnedProviders.includes(provider.name)} |
||||||
|
key={provider.name} |
||||||
|
id={provider.name} |
||||||
|
pinStateCallback={async (pinned: boolean) => { |
||||||
|
if (pinned) { |
||||||
|
this.emit('providerPinned', provider.name, provider) |
||||||
|
this.call('notification', 'toast', `"${provider.displayName}" has been added to the Environment list of the Deploy & Run Transactions plugin.`) |
||||||
|
return true |
||||||
|
} |
||||||
|
const providerName = await this.call('blockchain', 'getProvider') |
||||||
|
if (providerName !== provider.name) { |
||||||
|
this.emit('providerUnpinned', provider.name, provider) |
||||||
|
this.call('notification', 'toast', `"${provider.displayName}" has been removed from the Environment list of the Deploy & Run Transactions plugin.`) |
||||||
|
return true |
||||||
|
} else { |
||||||
|
this.call('notification', 'toast', 'Cannot unpin the current selected provider') |
||||||
|
return false |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
<div>{provider.description}</div> |
||||||
|
</RemixUIGridCell> |
||||||
|
})} |
||||||
|
</RemixUIGridSection> |
||||||
|
<RemixUIGridSection |
||||||
|
plugin={this} |
||||||
|
title='Deploy to an In-browser Virtual Machine.' |
||||||
|
hScrollable={false} |
||||||
|
>{this.providers['Remix VMs'].map(provider => { |
||||||
|
return <RemixUIGridCell |
||||||
|
plugin={this} |
||||||
|
title={provider.displayName} |
||||||
|
logos={provider.logos} |
||||||
|
classList='EECellStyle' |
||||||
|
searchKeywords={['Remix VMs', provider.name, provider.displayName, provider.title, provider.description]} |
||||||
|
pinned={this.pinnedProviders.includes(provider.name)} |
||||||
|
key={provider.name} |
||||||
|
id={provider.name} |
||||||
|
pinStateCallback={async (pinned: boolean) => { |
||||||
|
if (pinned) { |
||||||
|
this.emit('providerPinned', provider.name, provider) |
||||||
|
this.call('notification', 'toast', `"${provider.displayName}" has been added to the Environment list of the Deploy & Run Transactions plugin.`) |
||||||
|
return true |
||||||
|
} |
||||||
|
const providerName = await this.call('blockchain', 'getProvider') |
||||||
|
if (providerName !== provider.name) { |
||||||
|
this.emit('providerUnpinned', provider.name, provider) |
||||||
|
this.call('notification', 'toast', `"${provider.displayName}" has been removed from the Environment list of the Deploy & Run Transactions plugin.`) |
||||||
|
return true |
||||||
|
} else { |
||||||
|
this.call('notification', 'toast', 'Cannot unpin the current selected provider') |
||||||
|
return false |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
<div>{provider.description}</div> |
||||||
|
</RemixUIGridCell> |
||||||
|
})}</RemixUIGridSection> |
||||||
|
<RemixUIGridSection |
||||||
|
plugin={this} |
||||||
|
title='Deploy to an external Provider.' |
||||||
|
hScrollable={false} |
||||||
|
>{this.providers['Externals'].map(provider => { |
||||||
|
return <RemixUIGridCell |
||||||
|
plugin={this} |
||||||
|
title={provider.displayName} |
||||||
|
logos={provider.logos} |
||||||
|
classList='EECellStyle' |
||||||
|
searchKeywords={['Externals', provider.name, provider.displayName, provider.title, provider.description]} |
||||||
|
pinned={this.pinnedProviders.includes(provider.name)} |
||||||
|
key={provider.name} |
||||||
|
id={provider.name} |
||||||
|
pinStateCallback={async (pinned: boolean) => { |
||||||
|
if (pinned) { |
||||||
|
this.emit('providerPinned', provider.name, provider) |
||||||
|
this.call('notification', 'toast', `"${provider.displayName}" has been added to the Environment list of the Deploy & Run Transactions plugin.`) |
||||||
|
return true |
||||||
|
} |
||||||
|
const providerName = await this.call('blockchain', 'getProvider') |
||||||
|
if (providerName !== provider.name) { |
||||||
|
this.emit('providerUnpinned', provider.name, provider) |
||||||
|
this.call('notification', 'toast', `"${provider.displayName}" has been removed from the Environment list of the Deploy & Run Transactions plugin.`) |
||||||
|
return true |
||||||
|
} else { |
||||||
|
this.call('notification', 'toast', 'Cannot unpin the current selected provider') |
||||||
|
return false |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
<div>{provider.description}</div> |
||||||
|
</RemixUIGridCell> |
||||||
|
})}</RemixUIGridSection> |
||||||
|
</RemixUIGridView> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
.EECellStyle { |
||||||
|
min-height: 6rem; |
||||||
|
max-width: 12rem; |
||||||
|
min-width: 10rem; |
||||||
|
} |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 78 KiB |
@ -0,0 +1,13 @@ |
|||||||
|
import { IFilePanel } from '@remixproject/plugin-api' |
||||||
|
import { StatusEvents } from '@remixproject/plugin-utils' |
||||||
|
|
||||||
|
export interface ILayoutApi { |
||||||
|
events:{ |
||||||
|
} & StatusEvents |
||||||
|
methods: { |
||||||
|
maximisePinnedPanel: () => void |
||||||
|
maximiseSidePanel: () => void |
||||||
|
resetPinnedPanel: () => void |
||||||
|
resetSidePanel: () => void |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
import { IFilePanel } from '@remixproject/plugin-api' |
||||||
|
import { StatusEvents } from '@remixproject/plugin-utils' |
||||||
|
|
||||||
|
export interface IMatomoApi { |
||||||
|
events:{ |
||||||
|
} & StatusEvents |
||||||
|
methods: { |
||||||
|
track: (data: string[]) => void |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
import { IFilePanel } from '@remixproject/plugin-api' |
||||||
|
import { StatusEvents } from '@remixproject/plugin-utils' |
||||||
|
|
||||||
|
export interface IPinnedPanelApi { |
||||||
|
events:{ |
||||||
|
|
||||||
|
} & StatusEvents |
||||||
|
methods: { |
||||||
|
currentFocus(): Promise<string> |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
import { IFilePanel } from '@remixproject/plugin-api' |
||||||
|
import { StatusEvents } from '@remixproject/plugin-utils' |
||||||
|
|
||||||
|
export interface ISidePanelApi { |
||||||
|
events:{ |
||||||
|
focusChanged: (name: string) => void; |
||||||
|
} & StatusEvents |
||||||
|
methods: { |
||||||
|
|
||||||
|
} |
||||||
|
} |