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/ |
||||
rm -rf git/bare.git |
||||
rm -rf git/bare2.git |
||||
rm -rf git |
||||
mkdir -p git |
||||
cd 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: { |
||||
|
||||
} |
||||
} |