Merge branch 'master' into fix/swarm-publish

pull/5297/head
Cafe137 1 month ago committed by GitHub
commit f45d66a0db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 14
      .github/workflows/pr-reminder.yml
  2. 4
      apps/contract-verification/src/app/App.css
  3. 7
      apps/contract-verification/src/app/components/ContractDropdown.tsx
  4. 10
      apps/contract-verification/src/app/components/NavMenu.tsx
  5. 2
      apps/contract-verification/src/app/layouts/Default.tsx
  6. 23
      apps/contract-verification/src/app/views/LookupView.tsx
  7. 75
      apps/contract-verification/src/app/views/VerifyView.tsx
  8. 9
      apps/etherscan/.babelrc
  9. 16
      apps/etherscan/.browserslistrc
  10. 3
      apps/etherscan/.eslintrc
  11. 34
      apps/etherscan/.eslintrc.json
  12. 69
      apps/etherscan/project.json
  13. 7
      apps/etherscan/src/app/App.css
  14. 25
      apps/etherscan/src/app/AppContext.tsx
  15. 70
      apps/etherscan/src/app/EtherscanPluginClient.ts
  16. 136
      apps/etherscan/src/app/app.tsx
  17. 81
      apps/etherscan/src/app/components/HeaderWithSettings.tsx
  18. 34
      apps/etherscan/src/app/components/SubmitButton.tsx
  19. 2
      apps/etherscan/src/app/components/index.ts
  20. 36
      apps/etherscan/src/app/hooks/useLocalStorage.tsx
  21. 17
      apps/etherscan/src/app/layouts/Default.tsx
  22. 1
      apps/etherscan/src/app/layouts/index.ts
  23. 37
      apps/etherscan/src/app/routes.tsx
  24. 9
      apps/etherscan/src/app/types/Receipt.ts
  25. 1
      apps/etherscan/src/app/types/ThemeType.ts
  26. 2
      apps/etherscan/src/app/types/index.ts
  27. 1
      apps/etherscan/src/app/utils/index.ts
  28. 46
      apps/etherscan/src/app/utils/networks.ts
  29. 30
      apps/etherscan/src/app/utils/scripts.ts
  30. 69
      apps/etherscan/src/app/utils/utilities.ts
  31. 206
      apps/etherscan/src/app/utils/verify.ts
  32. 63
      apps/etherscan/src/app/views/CaptureKeyView.tsx
  33. 16
      apps/etherscan/src/app/views/ErrorView.tsx
  34. 31
      apps/etherscan/src/app/views/HomeView.tsx
  35. 170
      apps/etherscan/src/app/views/ReceiptsView.tsx
  36. 235
      apps/etherscan/src/app/views/VerifyView.tsx
  37. 4
      apps/etherscan/src/app/views/index.ts
  38. 0
      apps/etherscan/src/assets/.gitkeep
  39. 3
      apps/etherscan/src/environments/environment.prod.ts
  40. 6
      apps/etherscan/src/environments/environment.ts
  41. BIN
      apps/etherscan/src/favicon.ico
  42. 17
      apps/etherscan/src/index.html
  43. 14
      apps/etherscan/src/main.tsx
  44. 7
      apps/etherscan/src/polyfills.ts
  45. 16
      apps/etherscan/src/profile.json
  46. 1
      apps/etherscan/src/styles.css
  47. 22
      apps/etherscan/tsconfig.app.json
  48. 16
      apps/etherscan/tsconfig.json
  49. 90
      apps/etherscan/webpack.config.js
  50. 2
      apps/remix-ide-e2e/src/tests/etherscan_api.test.ts
  51. 2
      apps/remix-ide/project.json
  52. 3
      apps/remix-ide/src/app/components/plugin-manager-component.js
  53. 2
      apps/remix-ide/src/app/plugins/matomo.ts
  54. 6
      apps/remix-ide/src/app/plugins/remixAIPlugin.tsx
  55. 10
      apps/remix-ide/src/app/plugins/remixGuide.tsx
  56. 17
      apps/remix-ide/src/app/providers/injected-arbitrum-one-provider.tsx
  57. 10
      apps/remix-ide/src/app/providers/injected-custom-provider.tsx
  58. 40
      apps/remix-ide/src/app/providers/injected-ephemery-testnet-provider.tsx
  59. 17
      apps/remix-ide/src/app/providers/injected-optimism-provider.tsx
  60. 26
      apps/remix-ide/src/app/providers/injected-provider-trustwallet.tsx
  61. 27
      apps/remix-ide/src/app/providers/injected-skale-chaos-testnet-provider.tsx
  62. 14
      apps/remix-ide/src/app/udapp/run-tab.tsx
  63. 14
      apps/remix-ide/src/assets/list.json
  64. 8
      apps/remix-ide/src/remixAppManager.js
  65. 8
      libs/ghaction-helper/package.json
  66. 6
      libs/remix-ai-core/src/inferencers/remote/remoteInference.ts
  67. 8
      libs/remix-analyzer/package.json
  68. 6
      libs/remix-astwalker/package.json
  69. 12
      libs/remix-debug/package.json
  70. 4
      libs/remix-lib/package.json
  71. 6
      libs/remix-simulator/package.json
  72. 6
      libs/remix-solidity/package.json
  73. 10
      libs/remix-tests/package.json
  74. 13
      libs/remix-ui/editor/src/lib/providers/inlineCompletionProvider.ts
  75. 22
      libs/remix-ui/home-tab/src/lib/components/homeTabFeatured.tsx
  76. 10
      libs/remix-ui/home-tab/src/lib/components/homeTabFeaturedPlugins.tsx
  77. 18
      libs/remix-ui/home-tab/src/lib/components/pluginButton.tsx
  78. 18
      libs/remix-ui/panel/src/lib/plugins/panel-header.tsx
  79. 25
      libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx
  80. 27
      libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx
  81. 7
      libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCardContainer.tsx
  82. 22
      libs/remix-ui/plugin-manager/src/types.d.ts
  83. 4
      libs/remix-url-resolver/package.json
  84. 4
      libs/remix-ws-templates/package.json
  85. 2
      libs/remixd/package.json
  86. 2
      package.json
  87. 12
      releaseDetails.json

@ -3,15 +3,25 @@ name: PRs reviews reminder
on:
schedule:
- cron: "0 8 * * 1-5"
- cron: '0 9 * * 1-5'
workflow_dispatch:
jobs:
pr-reviews-reminder:
runs-on: ubuntu-latest
steps:
- uses: Aniket-Engg/pr-reviews-reminder-action@master
- name: Reminder about PRs pending to review
if: github.event.schedule == '0 8 * * 1-5'
uses: Aniket-Engg/pr-reviews-reminder-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
freeze-date: '2024-11-04T18:00:00Z'
- name: Reminder for standup
if: github.event.schedule == '0 9 * * 1-5'
uses: Aniket-Engg/pr-reviews-reminder-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
freeze-date: '2024-10-21T18:00:00Z'

@ -6,5 +6,9 @@ body {
margin: 0;
}
a:focus {
background-color: var(bg-light) !important;
}
.fa-arrow-up-right-from-square::before { content: "\f08e"; }
.fa-xmark::before { content: "\f00d"; }

@ -43,7 +43,12 @@ export const ContractDropdown: React.FC<ContractDropdownProps> = ({ label, id, s
return (
<div className="form-group">
<label htmlFor={id}>{label}</label>
<select value={selectedContract ? JSON.stringify(selectedContract) : ''} className={`form-control custom-select pr-4 ${!hasContracts ? 'disabled-cursor' : ''} ${!hasContracts ? 'text-muted' : ''}`} id={id} disabled={!hasContracts} onChange={handleSelectContract}>
<select value={selectedContract ? JSON.stringify(selectedContract) : ''}
className={`form-control custom-select pr-4 ${!hasContracts ? 'disabled-cursor text-warning' : ''}`}
id={id}
disabled={!hasContracts}
onChange={handleSelectContract}
>
{hasContracts ? (
Object.keys(compilationOutput).map((compilationTriggerFileName) => (
<optgroup key={compilationTriggerFileName} label={`Compilation trigger: ${compilationTriggerFileName}`}>

@ -9,8 +9,11 @@ interface NavItemProps {
const NavItem: React.FC<NavItemProps> = ({ to, icon, title }) => {
return (
<NavLink to={to} className={({ isActive }) => 'text-decoration-none d-flex flex-column justify-content-center py-2 px-1 small ' + (isActive ? 'bg-light' : 'bg-transparent')}>
<span>
<NavLink
to={to}
className={({ isActive }) => 'text-decoration-none d-flex px-1 py-1 flex-column justify-content-center small ' + (isActive ? "bg-light border-top border-left border-right" : "border-0 bg-transparent")}
>
<span className=''>
<span>{icon}</span>
<span className="ml-2">{title}</span>
</span>
@ -20,12 +23,11 @@ const NavItem: React.FC<NavItemProps> = ({ to, icon, title }) => {
export const NavMenu = () => {
return (
<nav className="d-flex flex-row justify-start w-100">
<nav className="d-flex medium flex-row w-100" style={{backgroundColor: 'var(--body-bg)!important'}}>
<NavItem to="/" icon={<i className="fas fa-home"></i>} title="Verify" />
<NavItem to="/receipts" icon={<i className="fas fa-receipt"></i>} title="Receipts" />
<NavItem to="/lookup" icon={<i className="fas fa-search"></i>} title="Lookup" />
<NavItem to="/settings" icon={<i className="fas fa-cog"></i>} title="Settings" />
<div className="flex-grow-1"></div>
</nav>
)
}

@ -12,7 +12,7 @@ export const DefaultLayout = ({ children, title, description }: PropsWithChildre
return (
<div className="d-flex flex-column h-100">
<NavMenu />
<div className="py-4 px-3 flex-grow-1" style={{ overflowY: 'auto' }}>
<div className="py-4 px-3 flex-grow-1 bg-light" style={{ overflowY: 'auto' }}>
<div>
<p className="text-center" style={{ fontSize: '0.8rem' }}>
{description}

@ -60,6 +60,10 @@ export const LookupView = () => {
}
}
const sendToMatomo = async (eventAction: string, eventName: string) => {
await clientInstance.call('matomo' as any, 'track', ['trackEvent', 'ContractVerification', eventAction, eventName]);
}
const handleOpenInRemix = async (lookupResponse: LookupResponse) => {
for (const source of lookupResponse.sourceFiles ?? []) {
try {
@ -70,6 +74,7 @@ export const LookupView = () => {
}
try {
await clientInstance.call('fileManager', 'open', lookupResponse.targetFilePath)
await sendToMatomo('lookup', "openInRemix On: " + selectedChain)
} catch (err) {
console.error(`Error focusing file ${lookupResponse.targetFilePath}: ${err.message}`)
}
@ -79,15 +84,20 @@ export const LookupView = () => {
<>
<form onSubmit={handleLookup}>
<SearchableChainDropdown label="Chain" id="network-dropdown" selectedChain={selectedChain} setSelectedChain={setSelectedChain} />
<ContractAddressInput label="Contract Address" id="contract-address" contractAddress={contractAddress} setContractAddress={setContractAddress} contractAddressError={contractAddressError} setContractAddressError={setContractAddressError} />
<button type="submit" className="btn btn-primary" disabled={submitDisabled}>
<ContractAddressInput
label="Contract Address"
id="contract-address"
contractAddress={contractAddress}
setContractAddress={setContractAddress}
contractAddressError={contractAddressError}
setContractAddressError={setContractAddressError}
/>
<button type="submit" className="btn w-100 btn-primary" disabled={submitDisabled}>
Lookup
</button>
</form>
<div className="pt-3">
{chainSettings &&
{ chainSettings &&
VERIFIERS.map((verifierId) => {
if (!validConfiguration(chainSettings, verifierId)) {
return (
@ -149,7 +159,8 @@ export const LookupView = () => {
)}
</div>
)
})}
})
}
</div>
</>
)

@ -13,7 +13,7 @@ import { VerifyFormContext } from '../VerifyFormContext'
import { useSourcifySupported } from '../hooks/useSourcifySupported'
export const VerifyView = () => {
const { compilationOutput, setSubmittedContracts, settings } = useContext(AppContext)
const { compilationOutput, setSubmittedContracts, settings, clientInstance } = useContext(AppContext)
const { selectedChain, setSelectedChain, contractAddress, setContractAddress, contractAddressError, setContractAddressError, selectedContract, setSelectedContract, proxyAddress, setProxyAddress, proxyAddressError, setProxyAddressError, abiEncodedConstructorArgs, setAbiEncodedConstructorArgs, abiEncodingError, setAbiEncodingError } = useContext(VerifyFormContext)
const [enabledVerifiers, setEnabledVerifiers] = useState<Partial<Record<VerifierIdentifier, boolean>>>({})
const [hasProxy, setHasProxy] = useState(!!proxyAddress)
@ -41,6 +41,11 @@ export const VerifyView = () => {
setEnabledVerifiers({ ...enabledVerifiers, [verifierId]: checked })
}
const sendToMatomo = async (eventAction: string, eventName: string) => {
await clientInstance.call("matomo" as any, 'track', ['trackEvent', 'ContractVerification', eventAction, eventName]);
}
const handleVerify = async (e) => {
e.preventDefault()
@ -63,6 +68,9 @@ export const VerifyView = () => {
name: verifierId as VerifierIdentifier,
}
receipts.push({ verifierInfo, status: 'pending', contractId, isProxyReceipt: false, failedChecks: 0 })
if (enabledVerifiers.Blockscout) await sendToMatomo('verify', "verifyWith: Blockscout On: " + selectedChain + " IsProxy: " + (hasProxy && !proxyAddress))
if (enabledVerifiers.Etherscan) await sendToMatomo('verify', "verifyWithEtherscan On: " + selectedChain + " IsProxy: " + (hasProxy && !proxyAddress))
if (enabledVerifiers.Sourcify) await sendToMatomo('verify', "verifyWithSourcify On: " + selectedChain + " IsProxy: " + (hasProxy && !proxyAddress))
}
const newSubmittedContract: SubmittedContract = {
@ -173,13 +181,24 @@ export const VerifyView = () => {
return (
<form onSubmit={handleVerify}>
<SearchableChainDropdown label="Chain" id="network-dropdown" selectedChain={selectedChain} setSelectedChain={setSelectedChain} />
<ContractAddressInput label="Contract Address" id="contract-address" contractAddress={contractAddress} setContractAddress={setContractAddress} contractAddressError={contractAddressError} setContractAddressError={setContractAddressError} />
<ContractDropdown label="Contract Name" id="contract-dropdown-1" selectedContract={selectedContract} setSelectedContract={setSelectedContract} />
{selectedContract && <ConstructorArguments abiEncodedConstructorArgs={abiEncodedConstructorArgs} setAbiEncodedConstructorArgs={setAbiEncodedConstructorArgs} selectedContract={selectedContract} abiEncodingError={abiEncodingError} setAbiEncodingError={setAbiEncodingError} />}
<ContractAddressInput
label="Contract Address"
id="contract-address"
contractAddress={contractAddress}
setContractAddress={setContractAddress}
contractAddressError={contractAddressError}
setContractAddressError={setContractAddressError}
/>
<CustomTooltip tooltipText="Please compile and select the solidity contract you need to verify.">
<ContractDropdown label="Contract Name" id="contract-dropdown-1" selectedContract={selectedContract} setSelectedContract={setSelectedContract} />
</CustomTooltip>
{selectedContract && <ConstructorArguments
abiEncodedConstructorArgs={abiEncodedConstructorArgs}
setAbiEncodedConstructorArgs={setAbiEncodedConstructorArgs}
selectedContract={selectedContract}
abiEncodingError={abiEncodingError}
setAbiEncodingError={setAbiEncodingError}
/>}
<div className="pt-3">
<div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input id="has-proxy" className="form-check-input custom-control-input" type="checkbox" checked={!!hasProxy} onChange={(e) => setHasProxy(e.target.checked)} />
@ -187,7 +206,14 @@ export const VerifyView = () => {
The deployed contract is behind a proxy
</label>
</div>
{hasProxy && <ContractAddressInput label="Proxy Address" id="proxy-address" contractAddress={proxyAddress} setContractAddress={setProxyAddress} contractAddressError={proxyAddressError} setContractAddressError={setProxyAddressError} />}
{hasProxy && <ContractAddressInput
label="Proxy Address"
id="proxy-address"
contractAddress={proxyAddress}
setContractAddress={setProxyAddress}
contractAddressError={proxyAddressError}
setContractAddressError={setProxyAddressError}
/>}
</div>
<div className="pt-3">
@ -198,9 +224,19 @@ export const VerifyView = () => {
return (
<div key={verifierId} className="pt-2">
<div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input className="form-check-input custom-control-input" type="checkbox" id={`verifier-${verifierId}`} checked={!!enabledVerifiers[verifierId]} onChange={(e) => handleVerifierCheckboxClick(verifierId, e.target.checked)} disabled={disabledVerifier} />
<label htmlFor={`verifier-${verifierId}`} className={`m-0 form-check-label custom-control-label large font-weight-bold${!disabledVerifier ? '' : ' text-secondary'}`} style={{ fontSize: '1rem', lineHeight: '1.5', color: 'var(--text)' }}>
<input
className="form-check-input custom-control-input"
type="checkbox"
id={`verifier-${verifierId}`}
checked={!!enabledVerifiers[verifierId]}
onChange={(e) => handleVerifierCheckboxClick(verifierId, e.target.checked)}
disabled={disabledVerifier}
/>
<label
htmlFor={`verifier-${verifierId}`}
className={`m-0 form-check-label custom-control-label large font-weight-bold${!disabledVerifier ? '' : ' text-secondary'}`}
style={{ fontSize: '1rem', lineHeight: '1.5', color: 'var(--text)' }}
>
{verifierId}
</label>
</div>
@ -227,10 +263,17 @@ export const VerifyView = () => {
)
})}
</div>
<button type="submit" className="btn btn-primary mt-3" disabled={submitDisabled}>
Verify
</button>
<CustomTooltip tooltipText={submitDisabled ? (
(!!contractAddressError || !contractAddress) ? "Please provide a valid contract address." :
!selectedChain ? "Please select the chain." :
!selectedContract ? "Please select the contract (compile if needed)." :
((hasProxy && !!proxyAddressError) || (hasProxy && !proxyAddress)) ? "Please provide a valid proxy contract address." :
"Please provide all necessary data to verify") // Is not expected to be a case
: "Verify with selected tools"}>
<button type="submit" className="w-100 btn btn-primary mt-3" disabled={submitDisabled}>
Verify
</button>
</CustomTooltip>
</form>
)
}

@ -1,9 +0,0 @@
{
"presets": ["@babel/preset-env", ["@babel/preset-react",
{"runtime": "automatic"}
]],
"plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-transform-runtime", "@babel/plugin-proposal-nullish-coalescing-operator"],
"ignore": [
"**/node_modules/**"
]
}

@ -1,16 +0,0 @@
# This file is used by:
# 1. autoprefixer to adjust CSS to support the below specified browsers
# 2. babel preset-env to adjust included polyfills
#
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# If you need to support different browsers in production, you may tweak the list below.
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major version
last 2 iOS major versions
Firefox ESR
not IE 9-11 # For IE 9-11 support, remove 'not'.

@ -1,3 +0,0 @@
{
"extends": "../../.eslintrc.json",
}

@ -1,34 +0,0 @@
{
"extends": [
"plugin:@nrwl/nx/react",
"../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts",
"*.tsx",
"*.js",
"*.jsx"
],
"rules": {}
},
{
"files": [
"*.ts",
"*.tsx"
],
"rules": {}
},
{
"files": [
"*.js",
"*.jsx"
],
"rules": {}
}
]
}

@ -1,69 +0,0 @@
{
"name": "etherscan",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/etherscan/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "development",
"options": {
"compiler": "babel",
"outputPath": "dist/apps/etherscan",
"index": "apps/etherscan/src/index.html",
"baseHref": "./",
"main": "apps/etherscan/src/main.tsx",
"polyfills": "apps/etherscan/src/polyfills.ts",
"tsConfig": "apps/etherscan/tsconfig.app.json",
"assets": [
"apps/etherscan/src/favicon.ico",
"apps/etherscan/src/assets",
"apps/etherscan/src/profile.json"
],
"styles": ["apps/etherscan/src/styles.css"],
"scripts": [],
"webpackConfig": "apps/etherscan/webpack.config.js"
},
"configurations": {
"development": {
},
"production": {
"fileReplacements": [
{
"replace": "apps/etherscan/src/environments/environment.ts",
"with": "apps/etherscan/src/environments/environment.prod.ts"
}
]
}
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/etherscan/**/*.ts"],
"eslintConfig": "apps/etherscan/.eslintrc"
}
},
"serve": {
"executor": "@nrwl/webpack:dev-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "etherscan:build",
"hmr": true,
"baseHref": "/"
},
"configurations": {
"development": {
"buildTarget": "etherscan:build:development",
"port": 5003
},
"production": {
"buildTarget": "etherscan:build:production"
}
}
}
},
"tags": []
}

@ -1,7 +0,0 @@
body {
margin: 0;
}
#root {
padding: 8px 14px;
}

@ -1,25 +0,0 @@
import React from 'react'
import {PluginClient} from '@remixproject/plugin'
import {Receipt, ThemeType} from './types'
export const AppContext = React.createContext({
apiKey: '',
setAPIKey: (value: string) => {
console.log('Set API Key from Context')
},
clientInstance: {} as PluginClient,
receipts: [] as Receipt[],
setReceipts: (receipts: Receipt[]) => {
console.log('Calling Set Receipts')
},
contracts: [] as string[],
setContracts: (contracts: string[]) => {
console.log('Calling Set Contract Names')
},
themeType: 'dark' as ThemeType,
setThemeType: (themeType: ThemeType) => {
console.log('Calling Set Theme Type')
},
networkName: ''
})

@ -1,70 +0,0 @@
import { PluginClient } from '@remixproject/plugin'
import { createClient } from '@remixproject/plugin-webview'
import { verify, EtherScanReturn } from './utils/verify'
import { getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus } from './utils'
import EventManager from 'events'
export class EtherscanPluginClient extends PluginClient {
public internalEvents: EventManager
constructor() {
super()
this.internalEvents = new EventManager()
createClient(this)
this.onload()
}
onActivation(): void {
this.internalEvents.emit('etherscan_activated')
}
async verify(
apiKey: string,
contractAddress: string,
contractArguments: string,
contractName: string,
compilationResultParam: any,
chainRef?: number | string,
isProxyContract?: boolean,
expectedImplAddress?: string
) {
const result = await verify(
apiKey,
contractAddress,
contractArguments,
contractName,
compilationResultParam,
chainRef,
isProxyContract,
expectedImplAddress,
this,
(value: EtherScanReturn) => {},
(value: string) => {}
)
return result
}
async receiptStatus(receiptGuid: string, apiKey: string, isProxyContract: boolean) {
try {
const { network, networkId } = await getNetworkName(this)
if (network === 'vm') {
throw new Error('Cannot check the receipt status in the selected network')
}
const etherscanApi = getEtherScanApi(networkId)
let receiptStatus
if (isProxyContract) receiptStatus = await getProxyContractReceiptStatus(receiptGuid, apiKey, etherscanApi)
else receiptStatus = await getReceiptStatus(receiptGuid, apiKey, etherscanApi)
return {
message: receiptStatus.result,
succeed: receiptStatus.status === '0' ? false : true
}
} catch (e: any) {
return {
status: 'error',
message: e.message,
succeed: false
}
}
}
}

@ -1,136 +0,0 @@
import React, {useState, useEffect, useRef} from 'react'
import {CompilationFileSources, CompilationResult} from '@remixproject/plugin-api'
import { EtherscanPluginClient } from './EtherscanPluginClient'
import {AppContext} from './AppContext'
import {DisplayRoutes} from './routes'
import {useLocalStorage} from './hooks/useLocalStorage'
import {getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus} from './utils'
import {Receipt, ThemeType} from './types'
import './App.css'
export const getNewContractNames = (compilationResult: CompilationResult) => {
const compiledContracts = compilationResult.contracts
let result: string[] = []
for (const file of Object.keys(compiledContracts)) {
const newContractNames = Object.keys(compiledContracts[file])
result = [...result, ...newContractNames]
}
return result
}
const plugin = new EtherscanPluginClient()
const App = () => {
const [apiKey, setAPIKey] = useLocalStorage('apiKey', '')
const [receipts, setReceipts] = useLocalStorage('receipts', [])
const [contracts, setContracts] = useState<string[]>([])
const [themeType, setThemeType] = useState<ThemeType>('dark')
const [networkName, setNetworkName] = useState('Loading...')
const timer = useRef(null)
const contractsRef = useRef(contracts)
contractsRef.current = contracts
const setListeners = () => {
plugin.on('solidity', 'compilationFinished', (fileName: string, source: CompilationFileSources, languageVersion: string, data: CompilationResult) => {
const newContractsNames = getNewContractNames(data)
const newContractsToSave: string[] = [...contractsRef.current, ...newContractsNames]
const uniqueContracts: string[] = [...new Set(newContractsToSave)]
setContracts(uniqueContracts)
})
plugin.on('blockchain' as any, 'networkStatus', (result) => {
setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`)
})
// @ts-ignore
plugin.call('blockchain', 'getCurrentNetworkStatus').then((result: any) => setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`))
}
useEffect(() => {
plugin.onload(() => {
setListeners()
})
}, [])
useEffect(() => {
let receiptsNotVerified: Receipt[] = receipts.filter((item: Receipt) => item.status === 'Pending in queue' || item.status === 'Max rate limit reached')
if (receiptsNotVerified.length > 0) {
if (timer.current) {
clearInterval(timer.current)
timer.current = null
}
timer.current = setInterval(async () => {
const {network, networkId} = await getNetworkName(plugin)
if (!plugin) return
if (network === 'vm') return
let newReceipts = receipts
for (const item of receiptsNotVerified) {
await new Promise((r) => setTimeout(r, 500)) // avoid api rate limit exceed.
let status
if (item.isProxyContract) {
status = await getProxyContractReceiptStatus(item.guid, apiKey, getEtherScanApi(networkId))
if (status.status === '1') {
status.message = status.result
status.result = 'Successfully Updated'
}
} else status = await getReceiptStatus(item.guid, apiKey, getEtherScanApi(networkId))
if (status.result === 'Pass - Verified' || status.result === 'Already Verified' || status.result === 'Successfully Updated') {
newReceipts = newReceipts.map((currentReceipt: Receipt) => {
if (currentReceipt.guid === item.guid) {
const res = {
...currentReceipt,
status: status.result
}
if (currentReceipt.isProxyContract) res.message = status.message
return res
}
return currentReceipt
})
}
}
receiptsNotVerified = newReceipts.filter((item: Receipt) => item.status === 'Pending in queue' || item.status === 'Max rate limit reached')
if (timer.current && receiptsNotVerified.length === 0) {
clearInterval(timer.current)
timer.current = null
}
setReceipts(newReceipts)
}, 10000)
}
}, [receipts])
return (
<AppContext.Provider
value={{
apiKey,
setAPIKey,
clientInstance: plugin,
receipts,
setReceipts,
contracts,
setContracts,
themeType,
setThemeType,
networkName
}}
>
{ plugin && <DisplayRoutes /> }
</AppContext.Provider>
)
}
export default App

@ -1,81 +0,0 @@
import React from 'react'
import {NavLink} from 'react-router-dom'
import {CustomTooltip} from '@remix-ui/helper'
import {AppContext} from '../AppContext'
interface Props {
title?: string
from: string
}
interface IconProps {
from: string
}
const HomeIcon = ({from}: IconProps) => {
return (
<NavLink
data-id="home"
to={{
pathname: '/'
}}
className={({isActive}) => (isActive ? 'border border-secondary shadow-none btn p-1 m-0' : 'border-0 shadow-none btn p-1 m-0')}
style={({isActive}) => (!isActive ? {width: '1.8rem', filter: 'contrast(0.5)'} : {width: '1.8rem'})}
state={from}
>
<CustomTooltip tooltipText="Home" tooltipId="etherscan-nav-home" placement="bottom">
<i className="fas fa-home"></i>
</CustomTooltip>
</NavLink>
)
}
const ReceiptsIcon = ({from}: IconProps) => {
return (
<NavLink
data-id="receipts"
to={{
pathname: '/receipts'
}}
className={({isActive}) => (isActive ? 'border border-secondary shadow-none btn p-1 m-0' : 'border-0 shadow-none btn p-1 m-0')}
style={({isActive}) => (!isActive ? {width: '1.8rem', filter: 'contrast(0.5)'} : {width: '1.8rem'})}
state={from}
>
<CustomTooltip tooltipText="Receipts" tooltipId="etherscan-nav-receipts" placement="bottom">
<i className="fas fa-receipt"></i>
</CustomTooltip>
</NavLink>
)
}
const SettingsIcon = ({from}: IconProps) => {
return (
<NavLink
data-id="settings"
to={{
pathname: '/settings'
}}
className={({isActive}) => (isActive ? 'border border-secondary shadow-none btn p-1 m-0' : 'border-0 shadow-none btn p-1 m-0')}
style={({isActive}) => (!isActive ? {width: '1.8rem', filter: 'contrast(0.5)'} : {width: '1.8rem'})}
state={from}
>
<CustomTooltip tooltipText="Settings" tooltipId="etherscan-nav-settings" placement="bottom">
<i className="fas fa-cog"></i>
</CustomTooltip>
</NavLink>
)
}
export const HeaderWithSettings = ({title = '', from}) => {
return (
<div className="d-flex justify-content-between">
<h6 className="d-inline">{title}</h6>
<div className="nav">
<HomeIcon from={from} />
<ReceiptsIcon from={from} />
<SettingsIcon from={from} />
</div>
</div>
)
}

@ -1,34 +0,0 @@
import React from 'react'
import {CustomTooltip} from '@remix-ui/helper'
interface Props {
text: string
isSubmitting?: boolean
dataId?: string
disable?: boolean
}
export const SubmitButton = ({text, dataId, isSubmitting = false, disable = true}) => {
return (
<div>
<button data-id={dataId} type="submit" className="btn btn-primary btn-block p-1 text-decoration-none" disabled={disable}>
<CustomTooltip
tooltipText={disable ? 'Fill in the valid value(s) and select a supported network' : 'Click to proceed'}
tooltipId={'etherscan-submit-button-' + dataId}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
placement="bottom"
>
<div>
{!isSubmitting && text}
{isSubmitting && (
<div>
<span className="spinner-border spinner-border-sm mr-1" role="status" aria-hidden="true" />
Verifying... Please wait
</div>
)}
</div>
</CustomTooltip>
</button>
</div>
)
}

@ -1,2 +0,0 @@
export { HeaderWithSettings } from "./HeaderWithSettings"
export { SubmitButton } from "./SubmitButton"

@ -1,36 +0,0 @@
import {useState} from 'react'
export function useLocalStorage(key: string, initialValue: any) {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
// Get from local storage by key
const item = window.localStorage.getItem(key)
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue
} catch (error) {
// If error also return initialValue
console.error(error)
return initialValue
}
})
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value: any) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore = value instanceof Function ? value(storedValue) : value
// Save state
setStoredValue(valueToStore)
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore))
} catch (error) {
// A more advanced implementation would handle the error case
console.error(error)
}
}
return [storedValue, setValue]
}

@ -1,17 +0,0 @@
import React, {PropsWithChildren} from 'react'
import {HeaderWithSettings} from '../components'
interface Props {
from: string
title?: string
}
export const DefaultLayout = ({children, from, title}) => {
return (
<div>
<HeaderWithSettings from={from} title={title} />
{children}
</div>
)
}

@ -1 +0,0 @@
export { DefaultLayout } from "./Default"

@ -1,37 +0,0 @@
import React from 'react'
import {HashRouter as Router, Route, Routes, RouteProps} from 'react-router-dom'
import {ErrorView, HomeView, ReceiptsView, CaptureKeyView} from './views'
import {DefaultLayout} from './layouts'
export const DisplayRoutes = () => (
<Router>
<Routes>
<Route
path="/"
element={
<DefaultLayout from="/" title="Verify Smart Contracts">
<HomeView />
</DefaultLayout>
}
/>
<Route path="/error" element={<ErrorView />} />
<Route
path="/receipts"
element={
<DefaultLayout from="/receipts" title="Check Receipt GUID Status">
<ReceiptsView />
</DefaultLayout>
}
/>
<Route
path="/settings"
element={
<DefaultLayout from="/settings" title="Set Explorer API Key">
<CaptureKeyView />
</DefaultLayout>
}
/>
</Routes>
</Router>
)

@ -1,9 +0,0 @@
export type ReceiptStatus = "Pending in queue" | "Pass - Verified" | "Already Verified" | "Max rate limit reached" | "Successfully Updated"
export interface Receipt {
guid: string
status: ReceiptStatus
isProxyContract: boolean
message?: string
succeed?: boolean
}

@ -1 +0,0 @@
export type ThemeType = "dark" | "light"

@ -1,2 +0,0 @@
export * from "./Receipt"
export * from "./ThemeType"

@ -1 +0,0 @@
export * from "./utilities"

@ -1,46 +0,0 @@
export const scanAPIurls = {
// all mainnet
1: 'https://api.etherscan.io/api',
56: 'https://api.bscscan.com/api',
137: 'https://api.polygonscan.com/api',
250: 'https://api.ftmscan.com/api',
42161: 'https://api.arbiscan.io/api',
43114: 'https://api.snowtrace.io/api',
1285: 'https://api-moonriver.moonscan.io/api',
1284: 'https://api-moonbeam.moonscan.io/api',
25: 'https://api.cronoscan.com/api',
199: 'https://api.bttcscan.com/api',
10: 'https://api-optimistic.etherscan.io/api',
42220: 'https://api.celoscan.io/api',
288: 'https://api.bobascan.com/api',
100: 'https://api.gnosisscan.io/api',
1101: 'https://api-zkevm.polygonscan.com/api',
59144: 'https://api.lineascan.build/api',
8453: 'https://api.basescan.org/api',
534352: 'https://api.scrollscan.com/api',
1116: 'https://openapi.coredao.org/api',
// all testnet
17000: 'https://api-holesky.etherscan.io/api',
11155111: 'https://api-sepolia.etherscan.io/api',
97: 'https://api-testnet.bscscan.com/api',
80001: 'https://api-testnet.polygonscan.com/api',
80002: 'https://api-amoy.polygonscan.com/api',
4002: 'https://api-testnet.ftmscan.com/api',
421611: 'https://api-testnet.arbiscan.io/api',
42170: 'https://api-nova.arbiscan.io/api',
43113: 'https://api-testnet.snowtrace.io/api',
1287: 'https://api-moonbase.moonscan.io/api',
338: 'https://api-testnet.cronoscan.com/api',
1028: 'https://api-testnet.bttcscan.com/api',
420: 'https://api-goerli-optimistic.etherscan.io/api',
44787: 'https://api-alfajores.celoscan.io/api',
2888: 'https://api-testnet.bobascan.com/api',
84531: 'https://api-goerli.basescan.org/api',
84532: "https://api-sepolia.basescan.org/api",
1442: 'https://api-testnet-zkevm.polygonscan.com/api',
2442: 'https://api-cardona-zkevm.polygonscan.com/api',
59140: 'https://api-testnet.lineascan.build/api',
534351: 'https://api-sepolia.scrollscan.com/api',
1115: 'https://api.test.btcs.network/api',
}

@ -1,30 +0,0 @@
export const verifyScript = `
/**
* @param {string} apikey - etherscan api key
* @param {string} contractAddress - Address of the contract to verify
* @param {string} contractArguments - Parameters used in the contract constructor during the initial deployment. It should be the hex encoded value
* @param {string} contractName - Name of the contract
* @param {string} contractFile - File where the contract is located
* @param {number | string} chainRef - Network chain id or API URL (optional)
* @param {boolean} isProxyContract - true, if contract is a proxy contract (optional)
* @param {string} expectedImplAddress - Implementation contract address, in case of proxy contract verification (optional)
* @returns {{ guid, status, message, succeed }} verification result
*/
export const verify = async (apikey: string, contractAddress: string, contractArguments: string, contractName: string, contractFile: string, chainRef?: number | string, isProxyContract?: boolean, expectedImplAddress?: string) => {
const compilationResultParam = await remix.call('compilerArtefacts' as any, 'getCompilerAbstract', contractFile)
console.log('verifying.. ' + contractName)
// update apiKey and chainRef to verify contract on multiple networks
return await remix.call('etherscan' as any, 'verify', apikey, contractAddress, contractArguments, contractName, compilationResultParam, chainRef, isProxyContract, expectedImplAddress)
}`
export const receiptGuidScript = `
/**
* @param {string} apikey - etherscan api key
* @param {string} guid - receipt id
* @param {boolean} isProxyContract - true, if contract is a proxy contract (optional)
* @returns {{ status, message, succeed }} receiptStatus
*/
export const receiptStatus = async (apikey: string, guid: string, isProxyContract?: boolean) => {
return await remix.call('etherscan' as any, 'receiptStatus', guid, apikey, isProxyContract)
}
`

@ -1,69 +0,0 @@
import { PluginClient } from "@remixproject/plugin"
import axios from 'axios'
import { scanAPIurls } from "./networks"
type RemixClient = PluginClient
/*
status: 0=Error, 1=Pass
message: OK, NOTOK
result: explanation
*/
export type receiptStatus = {
result: string
message: string
status: string
}
export const getEtherScanApi = (networkId: any) => {
if (!(networkId in scanAPIurls)) {
throw new Error("no known network to verify against")
}
const apiUrl = (scanAPIurls as any)[networkId]
return apiUrl
}
export const getNetworkName = async (client: RemixClient) => {
const network = await client.call("network", "detectNetwork")
if (!network) {
throw new Error("no known network to verify against")
}
return { network: network.name!.toLowerCase(), networkId: network.id }
}
export const getReceiptStatus = async (
receiptGuid: string,
apiKey: string,
etherscanApi: string
): Promise<receiptStatus> => {
const params = `guid=${receiptGuid}&module=contract&action=checkverifystatus&apiKey=${apiKey}`
try {
const response = await axios.get(`${etherscanApi}?${params}`)
const { result, message, status } = response.data
return {
result,
message,
status,
}
} catch (error) {
console.error(error)
}
}
export const getProxyContractReceiptStatus = async (
receiptGuid: string,
apiKey: string,
etherscanApi: string
): Promise<receiptStatus> => {
const params = `guid=${receiptGuid}&module=contract&action=checkproxyverification&apiKey=${apiKey}`
try {
const response = await axios.get(`${etherscanApi}?${params}`)
const { result, message, status } = response.data
return {
result,
message,
status,
}
} catch (error) {
console.error(error)
}
}

@ -1,206 +0,0 @@
import { getNetworkName, getEtherScanApi, getReceiptStatus, getProxyContractReceiptStatus } from "../utils"
import { CompilationResult } from "@remixproject/plugin-api"
import { CompilerAbstract } from '@remix-project/remix-solidity'
import axios from 'axios'
import { PluginClient } from "@remixproject/plugin"
const resetAfter10Seconds = (client: PluginClient, setResults: (value: string) => void) => {
setTimeout(() => {
client.emit("statusChanged", { key: "none" })
setResults("")
}, 10000)
}
export type EtherScanReturn = {
guid: any,
status: any,
}
export const verify = async (
apiKeyParam: string,
contractAddress: string,
contractArgumentsParam: string,
contractName: string,
compilationResultParam: CompilerAbstract,
chainRef: number | string,
isProxyContract: boolean,
expectedImplAddress: string,
client: PluginClient,
onVerifiedContract: (value: EtherScanReturn) => void,
setResults: (value: string) => void
) => {
let networkChainId
let etherscanApi
if (chainRef) {
if (typeof chainRef === 'number') {
networkChainId = chainRef
etherscanApi = getEtherScanApi(networkChainId)
} else if (typeof chainRef === 'string') etherscanApi = chainRef
} else {
const { network, networkId } = await getNetworkName(client)
if (network === "vm") {
return {
succeed: false,
message: "Cannot verify in the selected network"
}
} else {
networkChainId = networkId
etherscanApi = getEtherScanApi(networkChainId)
}
}
try {
const contractMetadata = getContractMetadata(
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
compilationResultParam.data as unknown as CompilationResult,
contractName
)
if (!contractMetadata) {
return {
succeed: false,
message: "Please recompile contract"
}
}
const contractMetadataParsed = JSON.parse(contractMetadata)
const fileName = getContractFileName(
// cast from the remix-plugin interface to the solidity one. Should be fixed when remix-plugin move to the remix-project repository
compilationResultParam.data as unknown as CompilationResult,
contractName
)
const jsonInput = {
language: 'Solidity',
sources: compilationResultParam.source.sources,
settings: {
optimizer: {
enabled: contractMetadataParsed.settings.optimizer.enabled,
runs: contractMetadataParsed.settings.optimizer.runs
}
}
}
const data: { [key: string]: string | any } = {
apikey: apiKeyParam, // A valid API-Key is required
module: "contract", // Do not change
action: "verifysourcecode", // Do not change
codeformat: "solidity-standard-json-input",
sourceCode: JSON.stringify(jsonInput),
contractname: fileName + ':' + contractName,
compilerversion: `v${contractMetadataParsed.compiler.version}`, // see http://etherscan.io/solcversions for list of support versions
constructorArguements: contractArgumentsParam ? contractArgumentsParam.replace('0x', '') : '', // if applicable
}
if (isProxyContract) {
data.action = "verifyproxycontract"
data.expectedimplementation = expectedImplAddress
data.address = contractAddress
} else {
data.contractaddress = contractAddress
}
const body = new FormData()
Object.keys(data).forEach((key) => body.append(key, data[key]))
client.emit("statusChanged", {
key: "loading",
type: "info",
title: "Verifying ...",
})
const response = await axios.post(etherscanApi, body)
const { message, result, status } = await response.data
if (message === "OK" && status === "1") {
resetAfter10Seconds(client, setResults)
let receiptStatus
if (isProxyContract) {
receiptStatus = await getProxyContractReceiptStatus(
result,
apiKeyParam,
etherscanApi
)
if (receiptStatus.status === '1') {
receiptStatus.message = receiptStatus.result
receiptStatus.result = 'Successfully Updated'
}
} else receiptStatus = await getReceiptStatus(
result,
apiKeyParam,
etherscanApi
)
const returnValue = {
guid: result,
status: receiptStatus.result,
message: `Verification request submitted successfully. Use this receipt GUID ${result} to track the status of your submission`,
succeed: true,
isProxyContract
}
onVerifiedContract(returnValue)
return returnValue
} else if (message === "NOTOK") {
client.emit("statusChanged", {
key: "failed",
type: "error",
title: result,
})
const returnValue = {
message: result,
succeed: false,
isProxyContract
}
resetAfter10Seconds(client, setResults)
return returnValue
}
return {
message: 'unknown reason ' + result,
succeed: false
}
} catch (error: any) {
console.error(error)
setResults("Something wrong happened, try again")
return {
message: error.message,
succeed: false
}
}
}
export const getContractFileName = (
compilationResult: CompilationResult,
contractName: string
) => {
const compiledContracts = compilationResult.contracts
let fileName = ""
for (const file of Object.keys(compiledContracts)) {
for (const contract of Object.keys(compiledContracts[file])) {
if (contract === contractName) {
fileName = file
break
}
}
}
return fileName
}
export const getContractMetadata = (
compilationResult: CompilationResult,
contractName: string
) => {
const compiledContracts = compilationResult.contracts
let contractMetadata = ""
for (const file of Object.keys(compiledContracts)) {
for (const contract of Object.keys(compiledContracts[file])) {
if (contract === contractName) {
contractMetadata = compiledContracts[file][contract].metadata
if (contractMetadata) {
break
}
}
}
}
return contractMetadata
}

@ -1,63 +0,0 @@
import React, {useState, useEffect} from 'react'
import {Formik, ErrorMessage, Field} from 'formik'
import {useNavigate, useLocation} from 'react-router-dom'
import {AppContext} from '../AppContext'
import {SubmitButton} from '../components'
export const CaptureKeyView = () => {
const location = useLocation()
const navigate = useNavigate()
const [msg, setMsg] = useState('')
const context = React.useContext(AppContext)
useEffect(() => {
if (!context.apiKey) setMsg('Please provide a 34 or 32 character API key to continue')
}, [context.apiKey])
return (
<div>
<Formik
initialValues={{apiKey: context.apiKey}}
validate={(values) => {
const errors = {} as any
if (!values.apiKey) {
errors.apiKey = 'Required'
} else if (values.apiKey.length !== 34 && values.apiKey.length !== 32) {
errors.apiKey = 'API key should be 34 or 32 characters long'
}
return errors
}}
onSubmit={(values) => {
const apiKey = values.apiKey
if (apiKey.length === 34 || apiKey.length === 32) {
context.setAPIKey(values.apiKey)
navigate(location && location.state ? location.state : '/')
}
}}
>
{({errors, touched, handleSubmit}) => (
<form onSubmit={handleSubmit}>
<div className="form-group mb-2">
<label htmlFor="apikey">API Key</label>
<Field
className={errors.apiKey && touched.apiKey ? 'form-control form-control-sm is-invalid' : 'form-control form-control-sm'}
type="password"
name="apiKey"
placeholder="e.g. GM1T20XY6JGSAPWKDCYZ7B2FJXKTJRFVGZ"
/>
<ErrorMessage className="invalid-feedback" name="apiKey" component="div" />
</div>
<div>
<SubmitButton text="Save" dataId="save-api-key" disable={errors && errors.apiKey ? true : false} />
</div>
</form>
)}
</Formik>
<div data-id="api-key-result" className="text-primary mt-4 text-center" style={{fontSize: '0.8em'}} dangerouslySetInnerHTML={{__html: msg}} />
</div>
)
}

@ -1,16 +0,0 @@
import React from 'react'
export const ErrorView = () => {
return (
<div className="d-flex w-100 flex-column align-items-center">
<img className="pb-4" width="250" src="https://res.cloudinary.com/key-solutions/image/upload/v1580400635/solid/error-png.png" alt="Error page" />
<h5>Sorry, something unexpected happened.</h5>
<h5>
Please raise an issue:{' '}
<a className="text-danger" href="https://github.com/ethereum/remix-project/issues">
Here
</a>
</h5>
</div>
)
}

@ -1,31 +0,0 @@
import React from 'react'
import {Navigate} from 'react-router-dom'
import {AppContext} from '../AppContext'
import {Receipt} from '../types'
import {VerifyView} from './VerifyView'
export const HomeView = () => {
const context = React.useContext(AppContext)
return !context.apiKey ? (
<Navigate
to={{
pathname: '/settings'
}}
/>
) : (
<VerifyView
contracts={context.contracts}
client={context.clientInstance}
apiKey={context.apiKey}
onVerifiedContract={(receipt: Receipt) => {
const newReceipts = [...context.receipts, receipt]
context.setReceipts(newReceipts)
}}
networkName={context.networkName}
/>
)
}

@ -1,170 +0,0 @@
import React, {useState} from 'react'
import {Formik, ErrorMessage, Field} from 'formik'
import {getEtherScanApi, getNetworkName, getReceiptStatus, getProxyContractReceiptStatus} from '../utils'
import {Receipt} from '../types'
import {AppContext} from '../AppContext'
import {SubmitButton} from '../components'
import {Navigate} from 'react-router-dom'
import {Button} from 'react-bootstrap'
import {CustomTooltip} from '@remix-ui/helper'
interface FormValues {
receiptGuid: string
}
export const ReceiptsView = () => {
const [results, setResults] = useState({succeed: false, message: ''})
const [isProxyContractReceipt, setIsProxyContractReceipt] = useState(false)
const context = React.useContext(AppContext)
const onGetReceiptStatus = async (values: FormValues, clientInstance: any, apiKey: string) => {
try {
const {network, networkId} = await getNetworkName(clientInstance)
if (network === 'vm') {
setResults({
succeed: false,
message: 'Cannot verify in the selected network'
})
return
}
const etherscanApi = getEtherScanApi(networkId)
let result
if (isProxyContractReceipt) {
result = await getProxyContractReceiptStatus(values.receiptGuid, apiKey, etherscanApi)
if (result.status === '1') {
result.message = result.result
result.result = 'Successfully Updated'
}
} else result = await getReceiptStatus(values.receiptGuid, apiKey, etherscanApi)
setResults({
succeed: result.status === '1' ? true : false,
message: result.result || (result.status === '0' ? 'Verification failed' : result.message)
})
} catch (error: any) {
setResults({
succeed: false,
message: error.message
})
}
}
return !context.apiKey ? (
<Navigate
to={{
pathname: '/settings'
}}
/>
) : (
<div>
<Formik
initialValues={{receiptGuid: ''}}
validate={(values) => {
const errors = {} as any
if (!values.receiptGuid) {
errors.receiptGuid = 'Required'
}
return errors
}}
onSubmit={(values) => onGetReceiptStatus(values, context.clientInstance, context.apiKey)}
>
{({errors, touched, handleSubmit, handleChange}) => (
<form onSubmit={handleSubmit}>
<div className="form-group mb-2">
<label htmlFor="receiptGuid">Receipt GUID</label>
<Field
className={errors.receiptGuid && touched.receiptGuid ? 'form-control form-control-sm is-invalid' : 'form-control form-control-sm'}
type="text"
name="receiptGuid"
/>
<ErrorMessage className="invalid-feedback" name="receiptGuid" component="div" />
</div>
<div className="d-flex mb-2 custom-control custom-checkbox">
<Field
className="custom-control-input"
type="checkbox"
name="isProxyReceipt"
id="isProxyReceipt"
onChange={async (e) => {
handleChange(e)
if (e.target.checked) setIsProxyContractReceipt(true)
else setIsProxyContractReceipt(false)
}}
/>
<label className="form-check-label custom-control-label" htmlFor="isProxyReceipt">
It's a proxy contract GUID
</label>
</div>
<SubmitButton dataId={null} text="Check" disable={!touched.receiptGuid || (touched.receiptGuid && errors.receiptGuid) ? true : false} />
</form>
)}
</Formik>
<div
className={results['succeed'] ? 'text-success mt-3 text-center' : 'text-danger mt-3 text-center'}
dangerouslySetInnerHTML={{
__html: results.message ? results.message : ''
}}
/>
<ReceiptsTable receipts={context.receipts} />
<br />
<CustomTooltip tooltipText="Clear the list of receipts" tooltipId="etherscan-clear-receipts" placement="bottom">
<Button
className="btn-sm"
onClick={() => {
context.setReceipts([])
}}
>
Clear
</Button>
</CustomTooltip>
</div>
)
}
const ReceiptsTable = ({receipts}) => {
return (
<div className="table-responsive">
<h6>Receipts</h6>
<table className="table h6 table-sm">
<thead>
<tr>
<th scope="col">Status</th>
<th scope="col">GUID</th>
</tr>
</thead>
<tbody>
{receipts &&
receipts.length > 0 &&
receipts.map((item: Receipt, index) => {
return (
<tr key={item.guid}>
<td
className={
item.status === 'Pass - Verified' || item.status === 'Successfully Updated'
? 'text-success'
: item.status === 'Pending in queue'
? 'text-warning'
: item.status === 'Already Verified'
? 'text-info'
: 'text-secondary'
}
>
{item.status}
{item.status === 'Successfully Updated' && (
<CustomTooltip placement={'bottom'} tooltipClasses="text-wrap" tooltipId="etherscan-receipt-proxy-status" tooltipText={item.message}>
<i style={{fontSize: 'small'}} className={'ml-1 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
</CustomTooltip>
)}
</td>
<td>{item.guid}</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}

@ -1,235 +0,0 @@
import React, {useEffect, useRef, useState} from 'react'
import { Web3 } from 'web3'
import {PluginClient} from '@remixproject/plugin'
import {CustomTooltip} from '@remix-ui/helper'
import {Formik, ErrorMessage, Field} from 'formik'
import {SubmitButton} from '../components'
import {Receipt} from '../types'
import {verify} from '../utils/verify'
import {etherscanScripts} from '@remix-project/remix-ws-templates'
interface Props {
client: PluginClient
apiKey: string
onVerifiedContract: (receipt: Receipt) => void
contracts: string[],
networkName: string
}
interface FormValues {
contractName: string
contractAddress: string
expectedImplAddress?: string
}
export const VerifyView = ({apiKey, client, contracts, onVerifiedContract, networkName}) => {
const [results, setResults] = useState('')
const [selectedContract, setSelectedContract] = useState('')
const [showConstructorArgs, setShowConstructorArgs] = useState(false)
const [isProxyContract, setIsProxyContract] = useState(false)
const [constructorInputs, setConstructorInputs] = useState([])
const verificationResult = useRef({})
useEffect(() => {
if (contracts.includes(selectedContract)) updateConsFields(selectedContract)
}, [contracts])
const updateConsFields = (contractName) => {
client.call('compilerArtefacts' as any, 'getArtefactsByContractName', contractName).then((result) => {
const {artefact} = result
if (artefact && artefact.abi && artefact.abi[0] && artefact.abi[0].type && artefact.abi[0].type === 'constructor' && artefact.abi[0].inputs.length > 0) {
setConstructorInputs(artefact.abi[0].inputs)
setShowConstructorArgs(true)
} else {
setConstructorInputs([])
setShowConstructorArgs(false)
}
})
}
const onVerifyContract = async (values: FormValues) => {
const compilationResult = (await client.call('solidity', 'getCompilationResult')) as any
if (!compilationResult) {
throw new Error('no compilation result available')
}
const constructorValues = []
for (const key in values) {
if (key.startsWith('contractArgValue')) constructorValues.push(values[key])
}
const web3 = new Web3()
const constructorTypes = constructorInputs.map((e) => e.type)
let contractArguments = web3.eth.abi.encodeParameters(constructorTypes, constructorValues)
contractArguments = contractArguments.replace('0x', '')
verificationResult.current = await verify(
apiKey,
values.contractAddress,
contractArguments,
values.contractName,
compilationResult,
null,
isProxyContract,
values.expectedImplAddress,
client,
onVerifiedContract,
setResults
)
setResults(verificationResult.current['message'])
}
return (
<div>
<Formik
initialValues={{
contractName: '',
contractAddress: ''
}}
validate={(values) => {
const errors = {} as any
if (!values.contractName) {
errors.contractName = 'Required'
}
if (!values.contractAddress) {
errors.contractAddress = 'Required'
}
if (values.contractAddress.trim() === '' || !values.contractAddress.startsWith('0x') || values.contractAddress.length !== 42) {
errors.contractAddress = 'Please enter a valid contract address'
}
return errors
}}
onSubmit={(values) => onVerifyContract(values)}
>
{({errors, touched, handleSubmit, handleChange, isSubmitting}) => {
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="network">Selected Network</label>
<CustomTooltip
tooltipText="Network is fetched from 'Deploy and Run Transactions' plugin's ENVIRONMENT field"
tooltipId="etherscan-impl-address2"
placement="bottom"
>
<Field className="form-control" type="text" name="network" value={networkName} disabled={true} />
</CustomTooltip>
</div>
<div className="form-group">
<label htmlFor="contractName">Contract Name</label>
<Field
as="select"
className={errors.contractName && touched.contractName && contracts.length ? 'form-control is-invalid' : 'form-control'}
name="contractName"
onChange={async (e) => {
handleChange(e)
setSelectedContract(e.target.value)
updateConsFields(e.target.value)
}}
>
<option disabled={true} value="">
{contracts.length ? 'Select a contract' : `--- No compiled contracts ---`}
</option>
{contracts.map((item) => (
<option key={item} value={item}>
{item}
</option>
))}
</Field>
<ErrorMessage className="invalid-feedback" name="contractName" component="div" />
</div>
<div className={showConstructorArgs ? 'form-group d-block' : 'form-group d-none'}>
<label>Constructor Arguments</label>
{constructorInputs.map((item, index) => {
return (
<div className="d-flex">
<Field className="form-control m-1" type="text" key={`contractArgName${index}`} name={`contractArgName${index}`} value={item.name} disabled={true} />
<CustomTooltip tooltipText={`value of ${item.name}`} tooltipId={`etherscan-constructor-value${index}`} placement="top">
<Field className="form-control m-1" type="text" key={`contractArgValue${index}`} name={`contractArgValue${index}`} placeholder={item.type} />
</CustomTooltip>
</div>
)
})}
</div>
<div className="form-group">
<label htmlFor="contractAddress">Contract Address</label>
<Field
className={errors.contractAddress && touched.contractAddress ? 'form-control is-invalid' : 'form-control'}
type="text"
name="contractAddress"
placeholder="e.g. 0x11b79afc03baf25c631dd70169bb6a3160b2706e"
/>
<ErrorMessage className="invalid-feedback" name="contractAddress" component="div" />
<div className="d-flex mb-2 custom-control custom-checkbox">
<Field
className="custom-control-input"
type="checkbox"
name="isProxy"
id="isProxy"
onChange={async (e) => {
handleChange(e)
if (e.target.checked) setIsProxyContract(true)
else setIsProxyContract(false)
}}
/>
<label className="form-check-label custom-control-label" htmlFor="isProxy">
It's a proxy contract address
</label>
</div>
</div>
<div className={isProxyContract ? 'form-group d-block' : 'form-group d-none'}>
<label htmlFor="expectedImplAddress">Expected Implementation Address</label>
<CustomTooltip
tooltipText="Providing expected implementation address enforces a check to ensure the returned implementation contract address is same as address picked up by the verifier"
tooltipId="etherscan-impl-address"
placement="bottom"
>
<Field className="form-control" type="text" name="expectedImplAddress" placeholder="verified implementation contract address" />
</CustomTooltip>
<i style={{fontSize: 'x-small'}} className={'ml-1 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
<label> &nbsp;Make sure contract is already verified on Etherscan</label>
</div>
<SubmitButton
dataId="verify-contract"
text="Verify"
isSubmitting={isSubmitting}
disable={
!contracts.length ||
!touched.contractName ||
!touched.contractAddress ||
(touched.contractName && errors.contractName) ||
(touched.contractAddress && errors.contractAddress) ||
networkName === 'VM (Not supported)'
? true
: false
}
/>
<br />
<CustomTooltip tooltipText="Generate the required TS scripts to verify a contract on Etherscan" tooltipId="etherscan-generate-scripts" placement="bottom">
<button
type="button"
className="mr-2 mb-2 py-1 px-2 btn btn-secondary btn-block"
onClick={async () => {
etherscanScripts({}, client)
}}
>
Generate Verification Scripts
</button>
</CustomTooltip>
</form>
)
}}
</Formik>
<div
data-id="verify-result"
className={verificationResult.current['succeed'] ? 'text-success mt-4 text-center' : 'text-danger mt-4 text-center'}
style={{fontSize: '0.8em'}}
dangerouslySetInnerHTML={{__html: results}}
/>
{/* <div style={{ display: "block", textAlign: "center", marginTop: "1em" }}>
<Link to="/receipts">View Receipts</Link>
</div> */}
</div>
)
}

@ -1,4 +0,0 @@
export { HomeView } from "./HomeView"
export { ErrorView } from "./ErrorView"
export { ReceiptsView } from "./ReceiptsView"
export { CaptureKeyView } from "./CaptureKeyView"

@ -1,3 +0,0 @@
export const environment = {
production: true
};

@ -1,6 +0,0 @@
// This file can be replaced during build by using the `fileReplacements` array.
// When building for production, this file is replaced with `environment.prod.ts`.
export const environment = {
production: false
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Etherscan</title>
<base href="./" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" integrity="ha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
crossorigin="anonymous" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css">
</head>
<body>
<div id="root"></div>
<script src="https://kit.fontawesome.com/41dd021e94.js" crossorigin="anonymous"></script>
</body>
</html>

@ -1,14 +0,0 @@
import React from 'react'
import * as ReactDOM from 'react-dom'
import { createRoot } from 'react-dom/client';
import App from './app/app'
const container = document.getElementById('root');
if (container) {
createRoot(container).render(
<App />
);
}

@ -1,7 +0,0 @@
/**
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
*
* See: https://github.com/zloirock/core-js#babel
*/
import 'core-js/stable';
import 'regenerator-runtime/runtime';

@ -1,16 +0,0 @@
{
"name": "etherscan",
"displayName": "Contract verification - Etherscan",
"description": "Verify Solidity contract code using Etherscan, BscScan, PolygonScan etc. APIs",
"version": "0.1.0",
"events": [],
"methods": ["verify", "receiptStatus"],
"kind": "none",
"icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHsAAAB7CAYAAABUx/9/AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAHBklEQVR42u2dy3WbTBTH/+ikgFEFwZ8K8HQAm6yjDkIHdiogHWBXgDqADgQVaLLJToZ1NtAB38JC0QNJIPGYx73nsIgcHVvz033OnblWVVXQQcqyhBACQgiUZYk0TfHnz5/q79+/rd7PGPsA8I1z/gEAjuOAMQbOOVzX1WKNLFVhCyGQJAnSNEUcx0N/CMu2bXDO4TgOXNcF51y9RauqSomnKApEUQTG2BZANeXDGNsyxuB5HqIoUmYNpf8DoyjC1HBbPEqAl/KPyrJMCg2+V+N930eWZQT72rNer6Ea4FvavtlsCLbGkM+gu66L9XptNuwsy3SG3KjpU5r3ySJrgyCfQfd93wzYmpvs1sBt2x7dtJM2S2DatYJN2nxby8eI2of/BQSzNfQgCNSETWb7PuDL5VIt2IalVL0D55wPwmXW98bKfD7fPj096bFvOtHeVJ7n2/l8DiGEvLteKtazZa+z9xm4EWiDgBNog4ATaIOAE2iDgFMebVBadnfDoWVZlF5N1CTKOcdms+n8xhmBVi8PF0Lg58+fw+fZZErlMeldGxwpIFM8YOvS+UJarUF/W++waWGbNSuKoof6ynpom269NUrme0SfOYQydTHnpNUPwO6zU3QMc06gH1hgyTp6blqam0UVyqkvL02fJ2B7WGfLtu26caR7UYVAK9f0gLe3t+7fzvl8vi3L8j9aQ2U0u75QYLHbt2hfQSOfrJzPvnni5OK3k0y4epp9y3fPCLSevnu1WrX7dhJspTX74jbojEDruw162oo8o3XRV97f3y+bIkq3tDHjzWmYhukWPM976Oxzy50oFQ5AIgzD5to4DG/I6whACdiHBwVnhyZcB9uq5M2DAwljbJskyXmApouv/vr1K1E+YFqWJeI4pmjcFEnTlGCbInmeAwC+UCFFa5/9AaC+//UTNom2PntxWDIlM65x0ScMw6PshGBrCtr3fXied/QiwdYQtOd5+PXr19kPZhSc6QXadV2EYdj4wykCNItzDs555wLI+/s7bdTcWNfdbZIXRam7Om9sUPR64y/UqY232hMY3Wf/+PHjofcXRbEgJT7X6DaH80eHrcuMLNVAUzRuEGiCbRBogm0Q6Le3N6qNmwB6V0uxSLMVK5jcCZrMuEqg6ybKLnLaakawFQDt+/7FEug10KfVRvLZkkrd9x0EwdnuVRtpKisTbIlBr9fru7plL21uEWwJzXY9+umuN1/ZxSSfLRno19fXQUCTZkskVVVZcRxjuVwOApo0WzIZEjTB1sHud+g0mlVVZdGSaQ/a4pyTZpug0cDncFeCbQBoAHh+fibYJoAGPjuECLYikuf5Q2fyOOefeXZVVRb1j8srD951Y3HOwRgjzdYc9N6EA1RB09I/n4rjOJ95dv1CfZaXRB/QjLEPxti+MreHTc33+ml0WZaLwz598tl3sPA8z7p2Y/9UEXeTfP/+/d8/Rp7yM/S5qCHPesHzvF6HwAy59vVUoKIo9r/jKEArimJBKdixzwOweHl5aTzvLJvZPjXhnueBMbZ/jaLxKzJGHDOkcp0eojzz2bQLNo4kSTIkaItzfnaIkjRb4Wj7mry8vJy91hiNk3YPI0NE203fJdu2G9uPSbNHkjHvcr904cGXa5Eo3V+ijtk+yB7w+vraDbZqaVj9QW3bNlKb63QrCIKjdOvoSzfyLM5eRi3UR1Bt2wbnHI7jwHVdae4az/McT09PYyvKzVmcSvrsLMss2bR4Km0+lCAIrv+HsUcHDlFqlOHZHaedbFxGL/Oze67bagd5ZzYnm4tS18Db1OxbmfGiKBZxHO/nRDHG8Pz8fC1YOvKftm1LaXJVibJvBWW+77da315nU1HOPP73zXXd1jcy0H52B1mtVrAsq5IBdN2F0ulGBl0Dpj6f3YJKN8PscEBbq0CbYCoHuaqrZF0/D/lsuX1yo5++5y408tknVa/5fL6VxSdfq5J1vSKLfLb8proxn95sNvf31JkIuOUUXa1AGwd7VztWanJwX6CNgK2KmR4atJawsyxTUoOHBq0N7CiK6u6MSodnCNDKwtYNLhom+Qxx8kT65oUkSSCEwO/fv7FarXSvAFnL5RJhGF5sLXpEpICd5znyPIcQAmVZIk1TJEliWmnPCoLgYrOgUrCTJEGSJAD+TWo3EOjFqlgYhsOPwRrT12rqYx+eYHh40nLQ9TesV0sayHWNe1RlmyqiNhm07/ujabM0qdfUzXpTmOyhDvMrk2fv9ma1hVz3iU29ztSWO7Am910F066ClmUZfN9XdofK9/1JzbWy5dIoilSAjuVyiSiKpF5L5WriMjQd1BrseR6iKJoksr7nUbbhUAiBJEmQpiniOB7lNgMZT4x2+hA6dZfWmyZ1fV0I0bpLtL4Gq4boOM7+GFN9q6/q8j8QmqQtM04gOgAAAABJRU5ErkJggg==",
"location": "sidePanel",
"url": "https://ipfs-cluster.ethdevops.io/ipfs/QmQsZbBSYCVBVpz2mVRbPRVTrcz59oJEpuuoxiT9otu3mh",
"repo": "https://github.com/ethereum/remix-project/tree/master/apps/etherscan",
"documentation": "https://remix-ide.readthedocs.io/en/latest/contract_verification.html#etherscan",
"maintainedBy": "Remix",
"authorContact": "remix@ethereum.org"
}

@ -1 +0,0 @@
/* You can add global styles to this file, and also import other style files */

@ -1,22 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -1,16 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
}
]
}

@ -1,90 +0,0 @@
const {composePlugins, withNx} = require('@nrwl/webpack')
const webpack = require('webpack')
const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const versionData = {
timestamp: Date.now(),
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development'
}
// Nx plugins for webpack.
module.exports = composePlugins(withNx(), (config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
// add fallback for node modules
config.resolve.fallback = {
...config.resolve.fallback,
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
path: require.resolve('path-browserify'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
constants: require.resolve('constants-browserify'),
os: false, //require.resolve("os-browserify/browser"),
timers: false, // require.resolve("timers-browserify"),
zlib: require.resolve('browserify-zlib'),
fs: false,
module: false,
tls: false,
net: false,
readline: false,
child_process: false,
buffer: require.resolve('buffer/'),
vm: require.resolve('vm-browserify')
}
// add externals
config.externals = {
...config.externals,
solc: 'solc'
}
// add public path
config.output.publicPath = '/'
// set filename
config.output.filename = `[name].plugin-etherscan.${versionData.timestamp}.js`
config.output.chunkFilename = `[name].plugin-etherscan.${versionData.timestamp}.js`
// add copy & provide plugin
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
url: ['url', 'URL'],
process: 'process/browser'
})
)
// souce-map loader
config.module.rules.push({
test: /\.js$/,
use: ['source-map-loader'],
enforce: 'pre'
})
config.ignoreWarnings = [/Failed to parse source map/] // ignore source-map-loader warnings
// set minimizer
config.optimization.minimizer = [
new TerserPlugin({
parallel: true,
terserOptions: {
ecma: 2015,
compress: false,
mangle: false,
format: {
comments: false
}
},
extractComments: false
}),
new CssMinimizerPlugin()
]
config.watchOptions = {
ignored: /node_modules/
}
return config
})

@ -62,7 +62,7 @@ const branch = process.env.CIRCLE_BRANCH;
const isMasterBranch = branch === 'master';
module.exports = {
...(branch ? (isMasterBranch ? tests : {}) : tests),
...{} //(branch ? (isMasterBranch ? tests : {}) : tests),
};

@ -3,7 +3,7 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/remix-ide/src",
"projectType": "application",
"implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "contract-verification", "vyper", "solhint", "walletconnect", "circuit-compiler", "learneth", "quick-dapp", "remix-dapp"],
"implicitDependencies": ["doc-gen", "doc-viewer", "contract-verification", "vyper", "solhint", "walletconnect", "circuit-compiler", "learneth", "quick-dapp", "remix-dapp"],
"targets": {
"build": {
"executor": "@nrwl/webpack:webpack",

@ -105,8 +105,7 @@ class PluginManagerComponent extends ViewPlugin {
getAndFilterPlugins = (filter) => {
this.filter = typeof filter === 'string' ? filter.toLowerCase() : this.filter
const isFiltered = (profile) => (profile.displayName ? profile.displayName : profile.name).toLowerCase().includes(this.filter)
const isFiltered = (profile) => (profile.displayName + profile.name + profile.description).toLowerCase().includes(this.filter)
const isNotRequired = (profile) => !this.appManager.isRequired(profile.name)
const isNotDependent = (profile) => !this.appManager.isDependent(profile.name)
const isNotHome = (profile) => profile.name !== 'home'

@ -11,7 +11,7 @@ const profile = {
version: '1.0.0'
}
const allowedPlugins = ['LearnEth', 'etherscan', 'vyper', 'circuit-compiler', 'doc-gen', 'doc-viewer', 'solhint', 'walletconnect', 'scriptRunner', 'dgit']
const allowedPlugins = ['LearnEth', 'etherscan', 'vyper', 'circuit-compiler', 'doc-gen', 'doc-viewer', 'solhint', 'walletconnect', 'scriptRunner', 'dgit', 'contract-verification']
export class Matomo extends Plugin {

@ -87,11 +87,11 @@ export class RemixAIPlugin extends Plugin {
}
}
async code_completion(prompt: string): Promise<any> {
async code_completion(prompt: string, promptAfter: string): Promise<any> {
if (this.isOnDesktop) {
return await this.call(this.remixDesktopPluginName, 'code_completion', prompt)
return await this.call(this.remixDesktopPluginName, 'code_completion', prompt, promptAfter)
} else {
return await this.remoteInferencer.code_completion(prompt)
return await this.remoteInferencer.code_completion(prompt, promptAfter)
}
}

File diff suppressed because one or more lines are too long

@ -1,17 +0,0 @@
import * as packageJson from '../../../../../package.json'
import { InjectedCustomProvider } from './injected-custom-provider'
const profile = {
name: 'injected-arbitrum-one-provider',
displayName: 'Injected Arbitrum One Provider',
kind: 'provider',
description: 'injected Arbitrum One Provider',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class InjectedArbitrumOneProvider extends InjectedCustomProvider {
constructor() {
super(profile, 'Arbitrum One', '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
}
}

@ -4,12 +4,14 @@ import { InjectedProviderDefault } from './injected-provider-default'
export class InjectedCustomProvider extends InjectedProviderDefault {
chainName: string
chainId: string
pluginName: string
rpcUrls: Array<string>
nativeCurrency: Record<string, any>
blockExplorerUrls: Array<string>
constructor(provider: any, chainName: string, chainId: string, rpcUrls: Array<string>, nativeCurrency?: Record<string, any>, blockExplorerUrls?: Array<string>) {
super(provider, chainName)
constructor(provider: any, pluginName: string, chainName: string, chainId: string, rpcUrls: Array<string>, nativeCurrency?: Record<string, any>, blockExplorerUrls?: Array<string>) {
super(provider, pluginName)
this.pluginName = pluginName
this.chainName = chainName
this.chainId = chainId
this.rpcUrls = rpcUrls
@ -44,8 +46,8 @@ export const setCustomNetwork = async (chainName: string, chainId: string, rpcUr
chainName: chainName,
rpcUrls: rpcUrls,
}
if (nativeCurrency) paramsObj.nativeCurrency = nativeCurrency
if (blockExplorerUrls) paramsObj.blockExplorerUrls = blockExplorerUrls
paramsObj.nativeCurrency = nativeCurrency ? nativeCurrency : null
paramsObj.blockExplorerUrls = blockExplorerUrls ? blockExplorerUrls : null
await (window as any).ethereum.request({
method: 'wallet_addEthereumChain',
params: [paramsObj]

@ -1,40 +0,0 @@
import * as packageJson from '../../../../../package.json'
import { InjectedCustomProvider } from './injected-custom-provider'
import { Web3 } from 'web3'
const profile = {
name: 'injected-ephemery-testnet-provider',
displayName: 'Injected Ephemery Testnet Provider',
kind: 'provider',
description: 'Injected Ephemery Testnet Provider',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class InjectedEphemeryTestnetProvider extends InjectedCustomProvider {
constructor() {
super(profile,
'Ephemery Testnet',
'',
['https://otter.bordel.wtf/erigon', 'https://eth.ephemeral.zeus.fyi'],
{
"name": "Ephemery ETH",
"symbol": "ETH",
"decimals": 18
},
[
'https://otter.bordel.wtf/',
'https://explorer.ephemery.dev/'
]
)
}
async init() {
const chainId = await new Web3(this.rpcUrls[0]).eth.getChainId()
this.chainId = `0x${chainId.toString(16)}`
this.chainName = `Ephemery Testnet ${chainId}`
await super.init()
return {}
}
}

@ -1,17 +0,0 @@
import * as packageJson from '../../../../../package.json'
import { InjectedCustomProvider } from './injected-custom-provider'
const profile = {
name: 'injected-optimism-provider',
displayName: 'Injected Optimism Provider',
kind: 'provider',
description: 'injected Optimism Provider',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class Injected0ptimismProvider extends InjectedCustomProvider {
constructor() {
super(profile, 'Optimism', '0xa', ['https://mainnet.optimism.io'])
}
}

@ -1,26 +0,0 @@
/* global ethereum */
import * as packageJson from '../../../../../package.json'
import { InjectedProvider } from './injected-provider'
const profile = {
name: 'injected-trustwallet',
displayName: 'Trust wallet',
kind: 'provider',
description: 'Trust wallet',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class InjectedProviderTrustWallet extends InjectedProvider {
constructor() {
super(profile)
}
getInjectedProvider() {
return (window as any).trustwallet
}
notFound() {
return 'Could not find Trust Wallet provider. Please make sure the Trust Wallet extension is active. Download the latest version from https://trustwallet.com/browser-extension'
}
}

@ -1,27 +0,0 @@
import * as packageJson from '../../../../../package.json'
import { InjectedCustomProvider } from './injected-custom-provider'
const profile = {
name: 'injected-skale-chaos-testnet-provider',
displayName: 'Injected SKALE Chaos Testnet',
kind: 'provider',
description: 'Injected SKALE Chaos Testnet Provider',
methods: ['sendAsync', 'init'],
version: packageJson.version
}
export class InjectedSKALEChaosTestnetProvider extends InjectedCustomProvider {
constructor () {
super(profile,
'SKALE Chaos Testnet',
'0x50877ed6',
['https://staging-v3.skalenodes.com/v1/staging-fast-active-bellatrix'],
{
"name": "sFUEL",
"symbol": "sFUEL",
"decimals": 18
}
)
}
}

@ -215,8 +215,8 @@ export class RunTab extends ViewPlugin {
const addCustomInjectedProvider = async (position, event, name, displayName, networkId, urls, nativeCurrency?) => {
// name = `${name} through ${event.detail.info.name}`
await this.engine.register([new InjectedCustomProvider(event.detail.provider, name, networkId, urls, nativeCurrency)])
await addProvider(position, name, displayName, true, false)
await this.engine.register([new InjectedCustomProvider(event.detail.provider, name, displayName, networkId, urls, nativeCurrency)])
await addProvider(position, name, displayName + ' - ' + event.detail.info.name, true, false)
}
const registerInjectedProvider = async (event) => {
const name = 'injected-' + event.detail.info.name
@ -225,21 +225,21 @@ export class RunTab extends ViewPlugin {
await addProvider(0, name, displayName, true, false)
if (event.detail.info.name === 'MetaMask') {
await addCustomInjectedProvider(7, event, 'injected-metamask-optimism', 'L2 - Optimism - ' + event.detail.info.name, '0xa', ['https://mainnet.optimism.io'])
await addCustomInjectedProvider(8, event, 'injected-metamask-arbitrum', 'L2 - Arbitrum - ' + event.detail.info.name, '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
await addCustomInjectedProvider(5, event, 'injected-metamask-sepolia', 'Sepolia Testnet - ' + event.detail.info.name, '0xaa36a7', [],
await addCustomInjectedProvider(7, event, 'injected-metamask-optimism', 'L2 - Optimism', '0xa', ['https://mainnet.optimism.io'])
await addCustomInjectedProvider(8, event, 'injected-metamask-arbitrum', 'L2 - Arbitrum', '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
await addCustomInjectedProvider(5, event, 'injected-metamask-sepolia', 'Sepolia Testnet', '0xaa36a7', [],
{
"name": "Sepolia ETH",
"symbol": "ETH",
"decimals": 18
})
await addCustomInjectedProvider(9, event, 'injected-metamask-ephemery', 'Ephemery Testnet - ' + event.detail.info.name, '', ['https://otter.bordel.wtf/erigon', 'https://eth.ephemeral.zeus.fyi'],
await addCustomInjectedProvider(9, event, 'injected-metamask-ephemery', 'Ephemery Testnet', '', ['https://otter.bordel.wtf/erigon', 'https://eth.ephemeral.zeus.fyi'],
{
"name": "Ephemery ETH",
"symbol": "ETH",
"decimals": 18
})
await addCustomInjectedProvider(10, event, 'injected-metamask-gnosis', 'Gnosis Mainnet - ' + event.detail.info.name, '', ['https://rpc.ankr.com/gnosis', 'https://1rpc.io/gnosis'],
await addCustomInjectedProvider(10, event, 'injected-metamask-gnosis', 'Gnosis Mainnet', '', ['https://rpc.ankr.com/gnosis', 'https://1rpc.io/gnosis'],
{
"name": "XDAI",
"symbol": "XDAI",

@ -1033,9 +1033,21 @@
"urls": [
"dweb:/ipfs/QmVTALD1WUQwRvEL19jgwrEFyBJMQmy9z32zvT6TAtYPY1"
]
},
{
"path": "soljson-v0.8.28+commit.7893614a.js",
"version": "0.8.28",
"build": "commit.7893614a",
"longVersion": "0.8.28+commit.7893614a",
"keccak256": "0x8e01bd0cafb8a8bab060453637101a88e4ab6d41c32645a26eaca541fb169c8e",
"sha256": "0x72ef580a6ec5943130028e5294313f24e9435520acc89f8c9dbfd0139d9ae146",
"urls": [
"dweb:/ipfs/QmVtdNYdUC4aX6Uk5LrxDT55B7NgGLnLcA2wTecF5xUbSS"
]
}
],
"releases": {
"0.8.28": "soljson-v0.8.28+commit.7893614a.js",
"0.8.27": "soljson-v0.8.27+commit.40a35a09.js",
"0.8.26": "soljson-v0.8.26+commit.8a97fa7a.js",
"0.8.25": "soljson-v0.8.25+commit.b61c2a91.js",
@ -1131,5 +1143,5 @@
"0.4.0": "soljson-v0.4.0+commit.acd334c9.js",
"0.3.6": "soljson-v0.3.6+commit.3fc68da5.js"
},
"latestRelease": "0.8.27"
"latestRelease": "0.8.28"
}

@ -83,7 +83,6 @@ let requiredModules = [
'dgit',
'pinnedPanel',
'pluginStateLogger',
//'remixGuide',
'environmentExplorer',
'templateSelection',
'matomo',
@ -93,7 +92,7 @@ let requiredModules = [
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither']
const loadLocalPlugins = ['doc-gen', 'doc-viewer', 'etherscan', 'contract-verification', 'vyper', 'solhint', 'walletconnect', 'circuit-compiler', 'learneth', 'quick-dapp']
const loadLocalPlugins = ['doc-gen', 'doc-viewer', 'contract-verification', 'vyper', 'solhint', 'walletconnect', 'circuit-compiler', 'learneth', 'quick-dapp']
const partnerPlugins = ['cookbookdev']
@ -138,7 +137,7 @@ export function isNative(name) {
'circuit-compiler',
'compilationDetails',
'vyperCompilationDetails',
//'remixGuide',
'remixGuide',
'environmentExplorer',
'templateSelection',
'walletconnect',
@ -410,7 +409,8 @@ class PluginLoader {
'compilationDetails',
'walletconnect',
'dapp-draft',
'solidityumlgen'
'solidityumlgen',
'remixGuide'
]
this.loaders = {}
this.loaders.localStorage = {

@ -1,6 +1,6 @@
{
"name": "@remix-project/ghaction-helper",
"version": "0.1.38",
"version": "0.1.39",
"description": "Solidity Tests GitHub Action Helper",
"main": "src/index.js",
"scripts": {
@ -19,17 +19,17 @@
},
"homepage": "https://github.com/ethereum/remix-project#readme",
"devDependencies": {
"@remix-project/remix-solidity": "^0.5.44",
"@remix-project/remix-solidity": "^0.5.45",
"@types/chai": "^4.3.4",
"typescript": "^4.9.3"
},
"dependencies": {
"@ethereum-waffle/chai": "^3.4.4",
"@remix-project/remix-simulator": "^0.2.58",
"@remix-project/remix-simulator": "^0.2.59",
"chai": "^4.3.7",
"ethers": "^5.7.2",
"web3": "^4.1.1"
},
"types": "./src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034"
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91"
}

@ -103,10 +103,10 @@ export class RemoteInferencer implements ICompletions {
}
}
async code_completion(prompt, options:IParams=null): Promise<any> {
async code_completion(prompt, promptAfter, options:IParams=null): Promise<any> {
const payload = !options?
{ "data": [prompt, "code_completion", "", false, 30, 0.9, 0.90, 50]} :
{ "data": [prompt, "code_completion", "", options.stream_result,
{ "data": [prompt, "code_completion", promptAfter, false, 30, 0.9, 0.90, 50]} :
{ "data": [prompt, "code_completion", promptAfter, options.stream_result,
options.max_new_tokens, options.temperature, options.top_p, options.top_k]
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-analyzer",
"version": "0.5.67",
"version": "0.5.68",
"description": "Tool to perform static analysis on Solidity smart contracts",
"scripts": {
"test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts"
@ -25,8 +25,8 @@
"@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.1",
"@remix-project/remix-astwalker": "^0.0.88",
"@remix-project/remix-lib": "^0.5.65",
"@remix-project/remix-astwalker": "^0.0.89",
"@remix-project/remix-lib": "^0.5.66",
"async": "^2.6.2",
"ethers": "^5.4.2",
"ethjs-util": "^0.1.6",
@ -50,6 +50,6 @@
"typescript": "^3.7.5"
},
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034",
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91",
"main": "./src/index.js"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-astwalker",
"version": "0.0.88",
"version": "0.0.89",
"description": "Tool to walk through Solidity AST",
"main": "src/index.js",
"scripts": {
@ -37,7 +37,7 @@
"@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.1",
"@remix-project/remix-lib": "^0.5.65",
"@remix-project/remix-lib": "^0.5.66",
"@types/tape": "^4.2.33",
"async": "^2.6.2",
"ethers": "^5.4.2",
@ -53,6 +53,6 @@
"tap-spec": "^5.0.0"
},
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034",
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91",
"types": "./src/index.d.ts"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-debug",
"version": "0.5.58",
"version": "0.5.59",
"description": "Tool to debug Ethereum transactions",
"contributors": [
{
@ -26,10 +26,10 @@
"@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.1",
"@remix-project/remix-astwalker": "^0.0.88",
"@remix-project/remix-lib": "^0.5.65",
"@remix-project/remix-simulator": "^0.2.58",
"@remix-project/remix-solidity": "^0.5.44",
"@remix-project/remix-astwalker": "^0.0.89",
"@remix-project/remix-lib": "^0.5.66",
"@remix-project/remix-simulator": "^0.2.59",
"@remix-project/remix-solidity": "^0.5.45",
"ansi-gray": "^0.1.1",
"async": "^2.6.2",
"color-support": "^1.1.3",
@ -69,6 +69,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034",
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91",
"types": "./src/index.d.ts"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-lib",
"version": "0.5.65",
"version": "0.5.66",
"description": "Library to various Remix tools",
"contributors": [
{
@ -55,6 +55,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034",
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91",
"types": "./src/index.d.ts"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-simulator",
"version": "0.2.58",
"version": "0.2.59",
"description": "Ethereum IDE and tools for the web",
"contributors": [
{
@ -23,7 +23,7 @@
"@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.1",
"@metamask/eth-sig-util": "^7.0.2",
"@remix-project/remix-lib": "^0.5.65",
"@remix-project/remix-lib": "^0.5.66",
"ansi-gray": "^0.1.1",
"async": "^3.1.0",
"body-parser": "^1.18.2",
@ -71,6 +71,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034",
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91",
"types": "./src/index.d.ts"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-solidity",
"version": "0.5.44",
"version": "0.5.45",
"description": "Tool to load and run Solidity compiler",
"main": "src/index.js",
"types": "src/index.d.ts",
@ -19,7 +19,7 @@
"@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.1",
"@remix-project/remix-lib": "^0.5.65",
"@remix-project/remix-lib": "^0.5.66",
"async": "^2.6.2",
"eslint-scope": "^5.0.0",
"ethers": "^5.4.2",
@ -57,5 +57,5 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-solidity#readme",
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034"
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-tests",
"version": "0.2.58",
"version": "0.2.59",
"description": "Tool to test Solidity smart contracts",
"main": "src/index.js",
"types": "./src/index.d.ts",
@ -41,9 +41,9 @@
"@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.1",
"@remix-project/remix-lib": "^0.5.65",
"@remix-project/remix-simulator": "^0.2.58",
"@remix-project/remix-solidity": "^0.5.44",
"@remix-project/remix-lib": "^0.5.66",
"@remix-project/remix-simulator": "^0.2.59",
"@remix-project/remix-solidity": "^0.5.45",
"@remix-project/remix-url-resolver": "^0.0.42",
"ansi-gray": "^0.1.1",
"async": "^2.6.0",
@ -89,5 +89,5 @@
"@ethereumjs/trie": "6.2.1"
},
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034"
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91"
}

@ -1,5 +1,6 @@
/* eslint-disable no-control-regex */
import { EditorUIProps, monacoTypes } from '@remix-ui/editor';
import * as monaco from 'monaco-editor';
const _paq = (window._paq = window._paq || [])
export class RemixInLineCompletionProvider implements monacoTypes.languages.InlineCompletionsProvider {
@ -81,10 +82,12 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli
const data = await this.props.plugin.call('remixAI', 'code_insertion', word, word_after)
this.task = 'code_generation'
_paq.push(['trackEvent', 'ai', 'remixAI', 'code_generation'])
const parsedData = data.trimStart() //JSON.parse(data).trimStart()
const item: monacoTypes.languages.InlineCompletion = {
insertText: parsedData
insertText: parsedData,
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column)
};
this.currentCompletion.text = parsedData
this.currentCompletion.item = item
@ -116,11 +119,13 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli
// Code insertion
try {
const output = await this.props.plugin.call('remixAI', 'code_insertion', word, word_after)
_paq.push(['trackEvent', 'ai', 'remixAI', 'code_insertion'])
const generatedText = output // no need to clean it. should already be
this.task = 'code_insertion'
const item: monacoTypes.languages.InlineCompletion = {
insertText: generatedText
insertText: generatedText,
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column)
};
this.currentCompletion.text = generatedText
this.currentCompletion.item = item
@ -138,7 +143,8 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli
try {
// Code completion
this.task = 'code_completion'
const output = await this.props.plugin.call('remixAI', 'code_completion', word)
const output = await this.props.plugin.call('remixAI', 'code_completion', word, word_after)
_paq.push(['trackEvent', 'ai', 'remixAI', 'code_completion'])
const generatedText = output
let clean = generatedText
@ -150,6 +156,7 @@ export class RemixInLineCompletionProvider implements monacoTypes.languages.Inli
const item: monacoTypes.languages.InlineCompletion = {
insertText: clean,
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column)
};
this.currentCompletion.text = clean
this.currentCompletion.item = item

@ -18,6 +18,11 @@ function HomeTabFeatured(props:HomeTabFeaturedProps) {
props.plugin.verticalIcons.select('LearnEth')
_paq.push(['trackEvent', 'hometab', 'featuredSection', 'LearnEth'])
}
const handleStartRemixGuide = async () => {
_paq.push(['trackEvent', 'hometab', 'featuredSection', 'watchOnRemixGuide'])
await props.plugin.appManager.activatePlugin(['remixGuide'])
await props.plugin.call('tabs', 'focus', 'remixGuide')
}
return (
<div className="pt-1 pl-2" id="hTFeaturedeSection">
<div className="mb-2 remix_ui-carousel-container">
@ -80,19 +85,18 @@ function HomeTabFeatured(props:HomeTabFeaturedProps) {
<div style={{ fontSize: '0.8rem', lineHeight: '1.25rem' }} className="mb-3">
<FormattedMessage id="home.learnEthPromoText" />
</div>
<span
<button
className="remixui_home_text btn-sm btn-secondary mt-2 text-decoration-none mb-3"
style={{ cursor: 'pointer' }}
onClick={()=>handleStartLearneth()}
>
<FormattedMessage id="home.learnEthPromoButton" />
</span>
</button>
</div>
</div>
<div className="mr-1 pr-1 d-flex align-items-center justify-content-center h-100">
<a href="https://www.youtube.com/@EthereumRemix/videos" target="__blank">
<button className="btn" onClick={() => handleStartRemixGuide()}>
<img src={'assets/img/YouTubeLogo.webp'} className="remixui_carouselImage" alt=""></img>
</a>
</button>
<div className="h6 w-50 p-2 pl-4 align-self-center" style={{ flex: '1' }}>
<h5>
<FormattedMessage id="home.remixYouTube" />
@ -103,14 +107,12 @@ function HomeTabFeatured(props:HomeTabFeaturedProps) {
<div style={{ fontSize: '0.8rem' }} className="mb-3">
<FormattedMessage id="home.remixYouTubeText2" />
</div>
<a
<button
className="remixui_home_text btn-sm btn-secondary mt-2 text-decoration-none mb-3"
onClick={() => _paq.push(['trackEvent', 'hometab', 'featuredSection', 'youTubeMore'])}
target="__blank"
href="https://www.youtube.com/@EthereumRemix/videos"
onClick={() => handleStartRemixGuide()}
>
<FormattedMessage id="home.remixYouTubeMore" />
</a>
</button>
</div>
</div>
</Carousel>

@ -122,7 +122,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
description={intl.formatMessage({
id: 'home.codeAnalyizerPluginDesc'
})}
remixMaintained={true}
maintainedBy='Remix'
callback={() => startCodeAnalyzer()}
/>
<PluginButton
@ -132,7 +132,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
description={intl.formatMessage({
id: 'home.learnEthPluginDesc'
})}
remixMaintained={true}
maintainedBy='Remix'
callback={() => startLearnEth()}
/>
<PluginButton
@ -140,7 +140,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
envID="cookbookLogo"
envText="Cookbook"
description={intl.formatMessage({ id: 'home.cookbookDesc' })}
remixMaintained={false}
maintainedBy="Cookbook"
callback={() => startCookbook()}
/>
<PluginButton
@ -148,7 +148,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
envID="solidityLogo"
envText="Solidity"
description={intl.formatMessage({ id: 'home.solidityPluginDesc' })}
remixMaintained={true}
maintainedBy='Remix'
callback={() => startSolidity()}
/>
<PluginButton
@ -156,7 +156,7 @@ function HomeTabFeaturedPlugins({ plugin }: HomeTabFeaturedPluginsProps) {
envID="sUTLogo"
envText="Solidity unit testing"
description={intl.formatMessage({ id: 'home.unitTestPluginDesc' })}
remixMaintained={true}
maintainedBy='Remix'
callback={() => startSolidityUnitTesting()}
/>
</Carousel>

@ -10,10 +10,10 @@ interface PluginButtonProps {
callback: any
l2?: boolean
description: string
remixMaintained?: boolean
maintainedBy?: string
}
function PluginButton({ imgPath, envID, envText, callback, l2, description, remixMaintained }: PluginButtonProps) {
function PluginButton({ imgPath, envID, envText, callback, l2, description, maintainedBy }: PluginButtonProps) {
const themeFilter = useContext(ThemeContext)
return (
@ -30,13 +30,17 @@ function PluginButton({ imgPath, envID, envText, callback, l2, description, remi
</div>
</button>
{l2 && <label className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_l2Label">L2</label>}
{remixMaintained ? (
{ maintainedBy?.toLowerCase() === 'remix' ? (
<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-by-remix" tooltipText={<FormattedMessage id="home.maintainedByRemix" />}>
<i className="bg-light text-success mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel fas fa-check"></i>
</CustomTooltip>)
: (<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-external" tooltipText={<FormattedMessage id="panel.maintainedExternally" />}>
<i aria-hidden="true" className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel text-warning far fa-exclamation-circle"></i>
</CustomTooltip>)
</CustomTooltip>) :
maintainedBy ?
(<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-external" tooltipText={"Maintained by " + maintainedBy}>
<i aria-hidden="true" className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
: (<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-external" tooltipText={<FormattedMessage id="panel.maintainedExternally" />}>
<i aria-hidden="true" className="bg-light mx-1 px-1 mb-0 mx-2 position-absolute remixui_home_maintainedLabel text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
}
</div>
)

@ -48,13 +48,17 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
</h6>
<div className="d-flex flex-row">
<div className="d-flex flex-row">
{plugin?.profile?.maintainedBy?.toLowerCase() === 'remix' ? (
<CustomTooltip placement="auto-end" tooltipId="maintainedByTooltip" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="panel.maintainedByRemix" />}>
<i aria-hidden="true" className="text-success mt-1 px-1 fas fa-check"></i>
</CustomTooltip>)
: (<CustomTooltip placement="auto-end" tooltipId="maintainedExternally" tooltipClasses="text-nowrap" tooltipText={<FormattedMessage id="panel.maintainedExternally" />}>
<i aria-hidden="true" className="mt-1 px-1 text-warning far fa-exclamation-circle"></i>
</CustomTooltip>)
{ plugin?.profile?.maintainedBy?.toLowerCase() === 'remix' ? (
<CustomTooltip placement="auto" tooltipId="maintainedByTooltipRemix" tooltipText={<FormattedMessage id="home.maintainedByRemix" />}>
<i className="text-success mt-1 px-1 fas fa-check"></i>
</CustomTooltip>) :
plugin?.profile?.maintainedBy ?
(<CustomTooltip placement="auto" tooltipId={"maintainedByTooltip" + plugin?.profile?.maintainedBy} tooltipText={"Maintained by " + plugin?.profile?.maintainedBy}>
<i aria-hidden="true" className="mt-1 px-1 text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
: (<CustomTooltip placement="auto" tooltipId="maintainedByTooltipRemixUnknown" tooltipText={<FormattedMessage id="panel.maintainedExternally" />}>
<i aria-hidden="true" className="mt-1 px-1 text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
}
</div>
<div className="swapitHeaderInfoSection d-flex justify-content-between" data-id="swapitHeaderInfoSectionId" onClick={toggleClass}>

@ -26,14 +26,23 @@ function ActivePluginCard({ profile, buttonText, deactivatePlugin }: PluginCardP
>
<i aria-hidden="true" className="px-1 text-success fas fa-check"></i>
</CustomTooltip>)
: (<CustomTooltip
placement="right"
tooltipId="pluginManagerActiveTitleExternally"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedExternally" />}
>
<i aria-hidden="true" className="px-1 text-warning far fa-exclamation-circle"></i>
</CustomTooltip>)
: profile?.maintainedBy ? (
<CustomTooltip
placement="right"
tooltipId="pluginManagerActiveTitleByRemix"
tooltipClasses="text-nowrap"
tooltipText={"Maintained by " + profile?.maintainedBy}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
: (<CustomTooltip
placement="right"
tooltipId="pluginManagerActiveTitleExternally"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedExternally" />}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
}
{profile.documentation && (
<CustomTooltip

@ -37,20 +37,29 @@ function InactivePluginCard({ profile, buttonText, activatePlugin }: PluginCardP
{profile?.maintainedBy?.toLowerCase() == 'remix' ? (
<CustomTooltip
placement="right"
tooltipId="pluginManagerInactiveTitleByRemix"
tooltipId="pluginManagerActiveTitleByRemix"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedByRemix" />}
>
<i aria-hidden="true" className="px-1 text-success fas fa-check"></i>
</CustomTooltip>)
: (<CustomTooltip
placement="right"
tooltipId="pluginManagerInactiveTitleExternally"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedExternally" />}
>
<i aria-hidden="true" className="px-1 text-warning far fa-exclamation-circle"></i>
</CustomTooltip>)
: profile?.maintainedBy ? (
<CustomTooltip
placement="right"
tooltipId="pluginManagerActiveTitleByRemix"
tooltipClasses="text-nowrap"
tooltipText={"Maintained by " + profile?.maintainedBy}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
: (<CustomTooltip
placement="right"
tooltipId="pluginManagerActiveTitleExternally"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="pluginManager.maintainedExternally" />}
>
<i aria-hidden="true" className="px-1 text-secondary far fa-exclamation-circle"></i>
</CustomTooltip>)
}
{profile.documentation && (
<CustomTooltip

@ -29,7 +29,12 @@ function InactivePluginCardContainer({ pluginComponent }: InactivePluginCardCont
) : null}
{pluginComponent.inactivePlugins &&
pluginComponent.inactivePlugins.map((profile, idx) => {
return <InactivePluginCard buttonText={intl.formatMessage({ id: 'pluginManager.activate' })} profile={profile} key={idx} activatePlugin={activatePlugin} />
return <InactivePluginCard
buttonText={intl.formatMessage({ id: 'pluginManager.activate' })}
profile={profile}
key={idx}
activatePlugin={activatePlugin}
/>
})}
</React.Fragment>
)

@ -103,9 +103,9 @@ declare class LocalPlugin {
* @returns {Promise<{api: any, profile: any}>} A promise with the new plugin profile
*/
open(plugins: any[]): Promise<{
api: any
profile: any
}>
api: any
profile: any
}>
profile: any
/**
@ -113,21 +113,21 @@ declare class LocalPlugin {
*/
create(): any
updateName({ target }: {
target: any
}): void
target: any
}): void
updateUrl({ target }: {
target: any
}): void
target: any
}): void
updateDisplayName({ target }: {
target: any
}): void
target: any
}): void
updateProfile(key: any, e: any): void
updateMethods({ target }: {
target: any
}): void
target: any
}): void
/** The form to create a local plugin */
form(): any

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-url-resolver",
"version": "0.0.87",
"version": "0.0.88",
"description": "Solidity import url resolver engine",
"main": "src/index.js",
"types": "src/index.d.ts",
@ -41,5 +41,5 @@
"typescript": "^3.1.6"
},
"typings": "src/index.d.ts",
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034"
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remix-ws-templates",
"version": "1.0.52",
"version": "1.0.53",
"description": "Create a Remix IDE workspace using different templates",
"main": "src/index.js",
"types": "src/index.d.ts",
@ -24,5 +24,5 @@
"ethers": "^5.4.2",
"web3": "^4.1.1"
},
"gitHead": "1beb79ae8b8269ba42440169ae8674b82f2ef034"
"gitHead": "0875db01e67e48861e55a44fc8293e4c4bf58d91"
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/remixd",
"version": "0.6.38",
"version": "0.6.39",
"description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)",
"main": "index.js",
"types": "./index.d.ts",

@ -1,6 +1,6 @@
{
"name": "remix-project",
"version": "0.56.0-dev",
"version": "0.57.0-dev",
"license": "MIT",
"description": "Ethereum Remix Monorepo",
"main": "index.js",

@ -1,13 +1,15 @@
{
"version": "v0.54.0",
"version": "v0.56.0",
"title": "RELEASE HIGHLIGHTS",
"highlight1": "Login to GitHub from 'File Explorer'",
"highlight2": "Added Cookbook workspace templates",
"highlight3": "Added 'sendRawTransaction' API to remix-simulator",
"highlight1": "Added new 'Contract Verification' plugin to verify contract on multiple platforms",
"highlight2": "Added new 'Remix Guide' plugin to learn using Remix IDE using videos",
"highlight3": "Added support for message signing using EIP712",
"highlight4": "",
"more": "Read More",
"moreLink": "https://medium.com/remix-ide/remix-release-v0-54-0-021575d54849?source=friends_link&sk=e3960ba90994b63993987b007ee3ac63"
"moreLink": "https://medium.com/remix-ide/remix-release-v0-56-0-574a5774f94c?source=friends_link&sk=af46111d5a9976306e5770ccc468ae28"
}
Loading…
Cancel
Save