Merge branch 'master' into latinlangs

pull/4173/head
Joseph Izang 1 year ago committed by GitHub
commit 06042a8a59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      apps/circuit-compiler/project.json
  2. 43
      apps/circuit-compiler/src/app/actions/index.ts
  3. 138
      apps/circuit-compiler/src/app/app.tsx
  4. 11
      apps/circuit-compiler/src/app/components/actions.tsx
  5. 51
      apps/circuit-compiler/src/app/components/compileBtn.tsx
  6. 36
      apps/circuit-compiler/src/app/components/configToggler.tsx
  7. 38
      apps/circuit-compiler/src/app/components/configurations.tsx
  8. 81
      apps/circuit-compiler/src/app/components/container.tsx
  9. 60
      apps/circuit-compiler/src/app/components/feedback.tsx
  10. 31
      apps/circuit-compiler/src/app/components/feedbackAlert.tsx
  11. 36
      apps/circuit-compiler/src/app/components/options.tsx
  12. 43
      apps/circuit-compiler/src/app/components/r1csBtn.tsx
  13. 26
      apps/circuit-compiler/src/app/components/versions.tsx
  14. 50
      apps/circuit-compiler/src/app/components/witness.tsx
  15. 36
      apps/circuit-compiler/src/app/components/witnessToggler.tsx
  16. 4
      apps/circuit-compiler/src/app/contexts/index.ts
  17. 77
      apps/circuit-compiler/src/app/reducers/state.ts
  18. 325
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  19. 87
      apps/circuit-compiler/src/app/types/index.ts
  20. 88
      apps/circuit-compiler/src/css/app.css
  21. 27
      apps/circuit-compiler/src/example/hash.circom
  22. 2
      apps/circuit-compiler/src/example/simple.circom
  23. 8
      apps/circuit-compiler/src/index.html
  24. 8
      apps/circuit-compiler/src/profile.json
  25. 9
      apps/circuit-compiler/src/snarkjs.min.js
  26. 23
      apps/remix-ide-e2e/src/tests/homeTab.test.ts
  27. 4
      apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
  28. 55
      apps/remix-ide/src/app/files/fileManager.ts
  29. 10
      apps/remix-ide/src/app/files/fileProvider.js
  30. 95
      apps/remix-ide/src/app/panels/file-panel.js
  31. 14
      apps/remix-ide/src/app/providers/injected-provider.tsx
  32. 15
      apps/remix-ide/src/app/tabs/locales/en/circuit.json
  33. 1
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  34. 3
      apps/remix-ide/src/app/tabs/locales/en/home.json
  35. 2
      apps/remix-ide/src/app/tabs/locales/en/index.js
  36. 15
      apps/remix-ide/src/app/tabs/locales/es/circuit.json
  37. 15
      apps/remix-ide/src/app/tabs/locales/fr/circuit.json
  38. 16
      apps/remix-ide/src/app/tabs/locales/zh/circuit.json
  39. 2
      apps/remix-ide/src/app/tabs/locales/zh/index.js
  40. 3
      apps/remix-ide/src/app/udapp/run-tab.js
  41. BIN
      apps/remix-ide/src/assets/img/circom-icon-bw-800b.webp
  42. BIN
      apps/remix-ide/src/assets/img/circom.webp
  43. 3175
      apps/remix-ide/src/assets/js/parser/antlr.js
  44. 36
      apps/remix-ide/src/blockchain/blockchain.tsx
  45. 12
      apps/remix-ide/src/blockchain/execution-context.js
  46. 14
      apps/remix-ide/src/blockchain/providers/injected.ts
  47. 2
      apps/remix-ide/src/blockchain/providers/vm.ts
  48. 9
      apps/remix-ide/src/remixAppManager.js
  49. 2
      libs/remix-lib/src/execution/txExecution.ts
  50. 14
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  51. 2
      libs/remix-ui/helper/src/lib/remix-ui-helper.ts
  52. 220
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  53. 22
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  54. 6
      libs/remix-ui/plugin-manager/src/lib/components/LocalPluginForm.tsx
  55. 2
      libs/remix-ui/run-tab/src/lib/actions/deploy.ts
  56. 5
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  57. 14
      libs/remix-ui/workspace/src/lib/actions/events.ts
  58. 15
      libs/remix-ui/workspace/src/lib/actions/index.ts
  59. 118
      libs/remix-ui/workspace/src/lib/actions/payload.ts
  60. 430
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  61. 4
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  62. 4
      libs/remix-ui/workspace/src/lib/components/file-render.tsx
  63. 97
      libs/remix-ui/workspace/src/lib/reducers/workspace.ts
  64. 16
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  65. 266
      libs/remix-ui/workspace/src/lib/types/index.ts
  66. 4
      libs/remix-ui/workspace/src/lib/utils/constants.ts
  67. 2
      libs/remix-ws-templates/src/index.ts
  68. 8
      libs/remix-ws-templates/src/templates/ozerc1155/index.ts
  69. 23
      libs/remix-ws-templates/src/templates/ozerc1155/tests/MyToken_mintable_test.sol
  70. 1
      libs/remix-ws-templates/src/templates/ozerc20/index.ts
  71. 6
      libs/remix-ws-templates/src/templates/ozerc20/tests/MyToken_mintable_test.sol
  72. 7
      libs/remix-ws-templates/src/templates/ozerc721/index.ts
  73. 22
      libs/remix-ws-templates/src/templates/ozerc721/tests/MyToken_mintable_test.sol
  74. 38
      libs/remix-ws-templates/src/templates/playground/.prettierrc
  75. 28
      libs/remix-ws-templates/src/templates/playground/README.txt
  76. 8
      libs/remix-ws-templates/src/templates/playground/contracts/helloWorld.sol
  77. 16
      libs/remix-ws-templates/src/templates/playground/index.ts
  78. 14
      libs/remix-ws-templates/src/templates/playground/scripts/deploy_with_ethers.ts
  79. 14
      libs/remix-ws-templates/src/templates/playground/scripts/deploy_with_web3.ts
  80. 29
      libs/remix-ws-templates/src/templates/playground/scripts/ethers-lib.ts
  81. 36
      libs/remix-ws-templates/src/templates/playground/scripts/web3-lib.ts
  82. 9
      libs/remix-ws-templates/src/templates/semaphore/README.txt
  83. 90
      libs/remix-ws-templates/src/templates/semaphore/circuits/semaphore.circom
  84. 11
      libs/remix-ws-templates/src/templates/semaphore/circuits/simple.circom
  85. 40
      libs/remix-ws-templates/src/templates/semaphore/circuits/tree.circom
  86. 18
      libs/remix-ws-templates/src/templates/semaphore/index.ts
  87. 72
      libs/remix-ws-templates/src/templates/semaphore/scripts/run_setup.ts
  88. 102
      libs/remix-ws-templates/src/templates/semaphore/scripts/run_verification.ts
  89. 165
      libs/remix-ws-templates/src/templates/semaphore/templates/groth16_verifier.sol.ejs
  90. 3
      package.json
  91. 0
      pkg/index.js
  92. 253
      yarn.lock

@ -3,7 +3,7 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/circuit-compiler/src",
"projectType": "application",
"implicitDependencies": ["remixd"],
"implicitDependencies": [],
"targets": {
"build": {
"executor": "@nrwl/webpack:webpack",
@ -17,7 +17,7 @@
"main": "apps/circuit-compiler/src/main.tsx",
"polyfills": "apps/circuit-compiler/src/polyfills.ts",
"tsConfig": "apps/circuit-compiler/tsconfig.app.json",
"assets": ["apps/circuit-compiler/src/profile.json"],
"assets": ["apps/circuit-compiler/src/profile.json", "apps/circuit-compiler/src/snarkjs.min.js"],
"styles": ["apps/circuit-compiler/src/css/app.css"],
"scripts": [],
"webpackConfig": "apps/circuit-compiler/webpack.config.js"

@ -0,0 +1,43 @@
import type { CircomPluginClient } from "../services/circomPluginClient"
import { Actions, AppState } from "../types"
export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState) => {
try {
if (appState.status !== "compiling") {
await plugin.compile(appState.filePath, { version: appState.version, prime: appState.primeValue })
} else {
console.log('Exisiting circuit compilation in progress')
}
} catch (e) {
plugin.internalEvents.emit('circuit_compiling_errored', e)
console.error(e)
}
}
export const generateR1cs = async (plugin: CircomPluginClient, appState: AppState) => {
try {
if (appState.status !== "generating") {
await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue })
} else {
console.log('Exisiting r1cs generation in progress')
}
} catch (e) {
plugin.internalEvents.emit('circuit_generating_r1cs_errored', e)
console.error('Generating R1CS failed: ', e)
}
}
export const computeWitness = async (plugin: CircomPluginClient, status: string, witnessValues: Record<string, string>) => {
try {
if (status !== "computing") {
const input = JSON.stringify(witnessValues)
await plugin.computeWitness(input)
} else {
console.log('Exisiting witness computation in progress')
}
} catch (e) {
plugin.internalEvents.emit('circuit_computing_witness_errored', e)
console.error('Computing witness failed: ', e)
}
}

@ -1,17 +1,143 @@
import React, { useEffect } from 'react'
import React, {useEffect, useReducer, useState} from 'react'
import {RenderIf} from '@remix-ui/helper'
import {IntlProvider} from 'react-intl'
import { CircomPluginClient } from './services/circomPluginClient'
import { Container } from './components/container'
import {CircuitAppContext} from './contexts'
import {appInitialState, appReducer} from './reducers/state'
import {CircomPluginClient} from './services/circomPluginClient'
import { compileCircuit } from './actions'
const plugin = new CircomPluginClient()
function App() {
const [appState, dispatch] = useReducer(appReducer, appInitialState)
const [locale, setLocale] = useState<{code: string; messages: any}>({
code: 'en',
messages: null
})
const [isContentChanged, setIsContentChanged] = useState<boolean>(false)
const [isPluginActivated, setIsPluginActivated] = useState<boolean>(false)
useEffect(() => {
new CircomPluginClient()
plugin.internalEvents.on('circom_activated', () => {
// @ts-ignore
plugin.on('locale', 'localeChanged', (locale: any) => {
setLocale(locale)
})
plugin.on('fileManager', 'currentFileChanged', (filePath) => {
if (filePath.endsWith('.circom')) {
dispatch({ type: 'SET_FILE_PATH', payload: filePath })
plugin.parse(filePath)
} else {
dispatch({ type: 'SET_FILE_PATH', payload: '' })
}
})
// @ts-ignore
plugin.on('editor', 'contentChanged', async (path: string, content: string) => {
setIsContentChanged(true)
if (path.endsWith('.circom')) {
plugin.parse(path, content)
}
})
setIsPluginActivated(true)
})
// compiling events
plugin.internalEvents.on('circuit_compiling_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'compiling' }))
plugin.internalEvents.on('circuit_compiling_done', (signalInputs: string[]) => {
signalInputs = (signalInputs || []).filter(input => input)
dispatch({ type: 'SET_SIGNAL_INPUTS', payload: signalInputs })
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })
})
plugin.internalEvents.on('circuit_compiling_errored', compilerErrored)
// r1cs events
plugin.internalEvents.on('circuit_generating_r1cs_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'generating' }))
plugin.internalEvents.on('circuit_generating_r1cs_done', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }))
plugin.internalEvents.on('circuit_generating_r1cs_errored', compilerErrored)
// witness events
plugin.internalEvents.on('circuit_computing_witness_start', () => dispatch({ type: 'SET_COMPILER_STATUS', payload: 'computing' }))
plugin.internalEvents.on('circuit_computing_witness_done', () => {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: null })
})
plugin.internalEvents.on('circuit_computing_witness_errored', compilerErrored)
// parsing events
plugin.internalEvents.on('circuit_parsing_done', (_, filePathToId) => {
dispatch({ type: 'SET_FILE_PATH_TO_ID', payload: filePathToId })
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: null })
})
plugin.internalEvents.on('circuit_parsing_errored', (report) => {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' })
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report })
})
plugin.internalEvents.on('circuit_parsing_warning', (report) => {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'warning' })
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report })
})
}, [])
useEffect(() => {
if (isContentChanged) {
(async () => {
if (appState.autoCompile) await compileCircuit(plugin, appState)
})()
setIsContentChanged(false)
}
}, [appState.autoCompile, isContentChanged])
useEffect(() => {
if (isPluginActivated) {
setCurrentLocale()
}
}, [isPluginActivated])
useEffect(() => {
if (appState.filePath) {
(async () => {
if (appState.autoCompile) await compileCircuit(plugin, appState)
})()
}
}, [appState.filePath])
const setCurrentLocale = async () => {
// @ts-ignore
const currentLocale = await plugin.call('locale', 'currentLocale')
setLocale(currentLocale)
}
const compilerErrored = (err: ErrorEvent) => {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' })
try {
const report = JSON.parse(err.message)
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: report })
} catch (e) {
dispatch({ type: 'SET_COMPILER_FEEDBACK', payload: err.message })
}
}
const value = {
appState,
dispatch,
plugin
}
return (
<div className="App">
<div className="circuit_compiler_app">
<RenderIf condition={locale.messages}>
<IntlProvider locale={locale.code} messages={locale.messages}>
<CircuitAppContext.Provider value={value}>
<Container />
</CircuitAppContext.Provider>
</IntlProvider>
</RenderIf>
</div>
)
}
export default App
export default App

@ -0,0 +1,11 @@
import { CompileBtn } from "./compileBtn";
import { R1CSBtn } from "./r1csBtn";
export function CircuitActions () {
return (
<div className="pb-3">
<CompileBtn />
<R1CSBtn />
</div>
)
}

@ -0,0 +1,51 @@
import { CustomTooltip, RenderIf, RenderIfNot, extractNameFromKey } from "@remix-ui/helper";
import { useContext } from "react";
import { CircuitAppContext } from "../contexts";
import { FormattedMessage } from "react-intl";
import { compileCircuit } from "../actions";
export function CompileBtn () {
const { plugin, appState } = useContext(CircuitAppContext)
return (
<button
className="btn btn-primary btn-block d-block w-100 text-break mb-1 mt-3"
onClick={() => { compileCircuit(plugin, appState) }}
disabled={(appState.filePath === "") || (appState.status === "compiling") || (appState.status === "generating")}
>
<CustomTooltip
placement="auto"
tooltipId="overlay-tooltip-compile"
tooltipText={
<div className="text-left">
<div>
<b>Ctrl+S</b> to compile {appState.filePath}
</div>
</div>
}
>
<div className="d-flex align-items-center justify-content-center">
<RenderIf condition={appState.status === 'compiling'}>
<i className="fas fa-sync fa-spin mr-2" aria-hidden="true"></i>
</RenderIf>
<RenderIfNot condition={appState.status === 'compiling'}>
<i className="fas fa-sync mr-2" aria-hidden="true"></i>
</RenderIfNot>
<div className="text-truncate overflow-hidden text-nowrap">
<span>
<FormattedMessage id="circuit.compile" />
</span>
<span className="ml-1 text-nowrap">
<RenderIf condition={appState.filePath === ""}>
<FormattedMessage id="circuit.noFileSelected" />
</RenderIf>
<RenderIfNot condition={appState.filePath === ""}>
<>{extractNameFromKey(appState.filePath)}</>
</RenderIfNot>
</span>
</div>
</div>
</CustomTooltip>
</button>
)
}

@ -0,0 +1,36 @@
import { useState } from "react"
import { FormattedMessage } from "react-intl"
import { RenderIf, RenderIfNot } from "@remix-ui/helper"
export function ConfigToggler ({ children }: { children: JSX.Element }) {
const [toggleExpander, setToggleExpander] = useState<boolean>(false)
const toggleConfigurations = () => {
setToggleExpander(!toggleExpander)
}
return (
<div>
<div className="d-flex circuit_config_section justify-content-between" onClick={toggleConfigurations}>
<div className="d-flex">
<label className="mt-1 circuit_config_section">
<FormattedMessage id="circuit.advancedConfigurations" />
</label>
</div>
<div>
<span data-id="scConfigExpander" onClick={toggleConfigurations}>
<RenderIf condition={toggleExpander}>
<i className="fas fa-angle-down" aria-hidden="true"></i>
</RenderIf>
<RenderIfNot condition={toggleExpander}>
<i className="fas fa-angle-right" aria-hidden="true"></i>
</RenderIfNot>
</span>
</div>
</div>
<RenderIf condition={toggleExpander}>
{ children }
</RenderIf>
</div>
)
}

@ -0,0 +1,38 @@
import { CustomTooltip } from "@remix-ui/helper"
import { FormattedMessage } from "react-intl"
import { ConfigurationsProps, PrimeValue } from "../types"
export function Configurations ({primeValue, setPrimeValue}: ConfigurationsProps) {
return (
<div className="pb-2 border-bottom flex-column">
<div className="flex-column d-flex">
<div className="mb-2 ml-0">
<label className="circuit_inner_label form-check-label" htmlFor="circuitPrimeSelector">
<FormattedMessage id="circuit.prime" />
</label>
<CustomTooltip
placement={"auto"}
tooltipId="circuitPrimeLabelTooltip"
tooltipClasses="text-nowrap"
tooltipText={<span>{'To choose the prime number to use to generate the circuit. Receives the name of the curve (bn128, bls12381, goldilocks) [default: bn128]'}</span>}
>
<div>
<select
onChange={(e) => setPrimeValue(e.target.value as PrimeValue)}
value={primeValue}
className="custom-select"
style={{
pointerEvents: 'auto'
}}
>
<option value="bn128">bn128</option>
<option value="bls12381">bls12381</option>
<option value="goldilocks">goldilocks</option>
</select>
</div>
</CustomTooltip>
</div>
</div>
</div>
)
}

@ -0,0 +1,81 @@
import { useContext } from 'react'
import { CustomTooltip, RenderIf } from '@remix-ui/helper'
import {FormattedMessage} from 'react-intl'
import { CircuitAppContext } from '../contexts'
import { CompileOptions } from './options'
import { VersionList } from './versions'
import { ConfigToggler } from './configToggler'
import { Configurations } from './configurations'
import { CircuitActions } from './actions'
import { WitnessToggler } from './witnessToggler'
import { WitnessSection } from './witness'
import { CompilerFeedback } from './feedback'
import { PrimeValue } from '../types'
export function Container () {
const circuitApp = useContext(CircuitAppContext)
const showCompilerLicense = (message = 'License not available') => {
// @ts-ignore
circuitApp.plugin.call('notification', 'modal', { id: 'modal_circuit_compiler_license', title: 'Compiler License', message })
}
const handleVersionSelect = (version: string) => {
circuitApp.dispatch({ type: 'SET_COMPILER_VERSION', payload: version })
}
const handleOpenErrorLocation = async (location: string, startRange: string) => {
if (location) {
const fullPathLocation = await circuitApp.plugin.resolveReportPath(location)
await circuitApp.plugin.call('fileManager', 'open', fullPathLocation)
// @ts-ignore
const startPosition: { lineNumber: number; column: number } = await circuitApp.plugin.call('editor', 'getPositionAt', startRange)
// @ts-ignore
await circuitApp.plugin.call('editor', 'gotoLine', startPosition.lineNumber - 1, startPosition.column)
}
}
const handlePrimeChange = (value: string) => {
circuitApp.dispatch({ type: 'SET_PRIME_VALUE', payload: value as PrimeValue })
}
const handleCircuitAutoCompile = (value: boolean) => {
circuitApp.dispatch({ type: 'SET_AUTO_COMPILE', payload: value })
}
const handleCircuitHideWarnings = (value: boolean) => {
circuitApp.dispatch({ type: 'SET_HIDE_WARNINGS', payload: value })
}
return (
<section>
<article>
<div className="pt-0 circuit_section">
<div className="mb-1">
<label className="circuit_label form-check-label" htmlFor="versionSelector">
<FormattedMessage id="circuit.compiler" />
</label>
<CustomTooltip placement="top" tooltipId="showCompilerTooltip" tooltipClasses="text-nowrap" tooltipText={'See compiler license'}>
<span className="fa fa-file-text-o border-0 p-0 ml-2" onClick={() => showCompilerLicense()}></span>
</CustomTooltip>
<VersionList setVersion={handleVersionSelect} versionList={circuitApp.appState.versionList} currentVersion={circuitApp.appState.version} />
<CompileOptions setCircuitAutoCompile={handleCircuitAutoCompile} setCircuitHideWarnings={handleCircuitHideWarnings} autoCompile={circuitApp.appState.autoCompile} hideWarnings={circuitApp.appState.hideWarnings} />
<ConfigToggler>
<Configurations setPrimeValue={handlePrimeChange} primeValue={circuitApp.appState.primeValue} />
</ConfigToggler>
<CircuitActions />
<RenderIf condition={circuitApp.appState.signalInputs.length > 0}>
<WitnessToggler>
<WitnessSection plugin={circuitApp.plugin} signalInputs={circuitApp.appState.signalInputs} status={circuitApp.appState.status} />
</WitnessToggler>
</RenderIf>
<RenderIf condition={circuitApp.appState.status !== 'compiling' && circuitApp.appState.status !== 'computing' && circuitApp.appState.status !== 'generating'}>
<CompilerFeedback feedback={circuitApp.appState.feedback} filePathToId={circuitApp.appState.filePathToId} openErrorLocation={handleOpenErrorLocation} hideWarnings={circuitApp.appState.hideWarnings} />
</RenderIf>
</div>
</div>
</article>
</section>
)
}

@ -0,0 +1,60 @@
import { useState } from 'react'
import { CompilerFeedbackProps, CompilerReport } from '../types'
import { RenderIf } from '@remix-ui/helper'
import {CopyToClipboard} from '@remix-ui/clipboard'
import { FeedbackAlert } from './feedbackAlert'
export function CompilerFeedback ({ feedback, filePathToId, hideWarnings, openErrorLocation }: CompilerFeedbackProps) {
const [ showException, setShowException ] = useState<boolean>(true)
const handleCloseException = () => {
setShowException(false)
}
const handleOpenError = (report: CompilerReport) => {
if (report.labels.length > 0) {
openErrorLocation(filePathToId[report.labels[0].file_id], report.labels[0].range.start)
}
}
return (
<div>
<div className="circuit_errors_box py-4">
<RenderIf condition={ (typeof feedback === "string") && showException }>
<div className="circuit_feedback error alert alert-danger">
<span> { feedback } </span>
<div className="close" data-id="renderer" onClick={handleCloseException}>
<i className="fas fa-times"></i>
</div>
<div className="d-flex pt-1 flex-row-reverse">
<span className="ml-3 pt-1 py-1" >
<CopyToClipboard content={feedback} className="p-0 m-0 far fa-copy error" direction={'top'} />
</span>
</div>
</div>
</RenderIf>
<RenderIf condition={ Array.isArray(feedback) }>
<>
{
Array.isArray(feedback) && feedback.map((response, index) => (
<div key={index} onClick={() => handleOpenError(response)}>
<RenderIf condition={response.type === 'Error'}>
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-danger`}>
<FeedbackAlert message={response.message} location={ response.labels[0] ? response.labels[0].message + ` ${filePathToId[response.labels[0].file_id]}:${response.labels[0].range.start}:${response.labels[0].range.end}` : null} />
</div>
</RenderIf>
<RenderIf condition={(response.type === 'Warning') && !hideWarnings}>
<div className={`circuit_feedback ${response.type.toLowerCase()} alert alert-warning`}>
<FeedbackAlert message={response.message} location={null} />
</div>
</RenderIf>
</div>
)
)
}
</>
</RenderIf>
</div>
</div>
)
}

@ -0,0 +1,31 @@
import { useState } from 'react'
import { FeedbackAlertProps } from '../types'
import { RenderIf } from '@remix-ui/helper'
import {CopyToClipboard} from '@remix-ui/clipboard'
export function FeedbackAlert ({ message, location }: FeedbackAlertProps) {
const [ showAlert, setShowAlert] = useState<boolean>(true)
const handleCloseAlert = () => {
setShowAlert(false)
}
return (
<RenderIf condition={showAlert}>
<>
<span> { message } </span>
<RenderIf condition={location !== null}>
<span> { location }</span>
</RenderIf>
<div className="close" data-id="renderer" onClick={handleCloseAlert}>
<i className="fas fa-times"></i>
</div>
<div className="d-flex pt-1 flex-row-reverse">
<span className="ml-3 pt-1 py-1" >
<CopyToClipboard content={message} className="p-0 m-0 far fa-copy error" direction={'top'} />
</span>
</div>
</>
</RenderIf>
)
}

@ -0,0 +1,36 @@
import {FormattedMessage} from 'react-intl'
import { CompileOptionsProps } from '../types'
export function CompileOptions ({autoCompile, hideWarnings, setCircuitAutoCompile, setCircuitHideWarnings}: CompileOptionsProps) {
return (
<div className='pb-2'>
<div className="mt-2 custom-control custom-checkbox">
<input
className="custom-control-input"
type="checkbox"
onChange={(e) => setCircuitAutoCompile(e.target.checked)}
title="Auto compile"
checked={autoCompile}
id="autoCompileCircuit"
/>
<label className="form-check-label custom-control-label" htmlFor="autoCompileCircuit">
<FormattedMessage id="circuit.autoCompile" />
</label>
</div>
<div className="mt-1 mb-2 circuit_warnings_box custom-control custom-checkbox">
<input
className="custom-control-input"
onChange={(e) => setCircuitHideWarnings(e.target.checked)}
id="hideCircuitWarnings"
type="checkbox"
title="Hide warnings"
checked={hideWarnings}
/>
<label className="form-check-label custom-control-label" htmlFor="hideCircuitWarnings">
<FormattedMessage id="solidity.hideWarnings" />
</label>
</div>
</div>
)
}

@ -0,0 +1,43 @@
import { CustomTooltip, RenderIf, RenderIfNot } from "@remix-ui/helper";
import { useContext } from "react";
import { CircuitAppContext } from "../contexts";
import { FormattedMessage } from "react-intl";
import { generateR1cs } from "../actions";
export function R1CSBtn () {
const { plugin, appState } = useContext(CircuitAppContext)
return (
<button
className="btn btn-secondary btn-block d-block w-100 text-break mb-1 mt-2"
onClick={() => { generateR1cs(plugin, appState) }}
disabled={(appState.filePath === "") || (appState.status === "compiling") || (appState.status === "generating") || (appState.status === "computing")}
>
<CustomTooltip
placement="auto"
tooltipId="overlay-tooltip-compile"
tooltipText={
<div className="text-left">
<div>
Outputs the constraints in r1cs format
</div>
</div>
}
>
<div className="d-flex align-items-center justify-content-center">
<RenderIf condition={appState.status === 'generating'}>
<i className="fas fa-sync fa-spin mr-2" aria-hidden="true"></i>
</RenderIf>
<RenderIfNot condition={appState.status === 'generating'}>
<i className="fas fa-sync mr-2" aria-hidden="true"></i>
</RenderIfNot>
<div className="text-truncate overflow-hidden text-nowrap">
<span>
<FormattedMessage id="circuit.generateR1cs" />
</span>
</div>
</div>
</CustomTooltip>
</button>
)
}

@ -0,0 +1,26 @@
import { RenderIf } from "@remix-ui/helper";
import { AppState } from "../types";
export function VersionList ({ currentVersion, versionList, setVersion }: { versionList: AppState['versionList'], currentVersion: string, setVersion: (version: string) => void }) {
const versionListKeys = Object.keys(versionList)
return (
<select
value={currentVersion}
onChange={(e) => setVersion(e.target.value)}
className="custom-select"
>
<RenderIf condition={versionListKeys.length > 0}>
<>
{
versionListKeys.map((version, index) => (
<option value={version} key={index}>
{ versionList[version].name }
</option>
))
}
</>
</RenderIf>
</select>
)
}

@ -0,0 +1,50 @@
import { RenderIf, RenderIfNot } from "@remix-ui/helper";
import { FormattedMessage } from "react-intl";
import { CompilerStatus } from "../types";
import { computeWitness } from "../actions";
import { useState } from "react";
import type { CircomPluginClient } from "../services/circomPluginClient";
export function WitnessSection ({ plugin, signalInputs, status }: {plugin: CircomPluginClient, signalInputs: string[], status: CompilerStatus}) {
const [witnessValues, setWitnessValues] = useState<Record<string, string>>({})
const handleSignalInput = (e: any) => {
setWitnessValues({
...witnessValues,
[e.target.name]: e.target.value
})
}
return (
<div className="pb-2 border-bottom flex-column">
<div className="flex-column d-flex">
<RenderIf condition={signalInputs.length > 0}>
<>
{
signalInputs.map((input, index) => (
<div className="mb-2 ml-0" key={index}>
<label className="circuit_inner_label form-check-label" htmlFor="circuitPrimeSelector">
<FormattedMessage id="circuit.signalInput" /> { input }
</label>
<input className="form-control m-0 txinput" placeholder={input} name={input} onChange={handleSignalInput} />
</div>
))
}
<button
className="btn btn-sm btn-secondary"
onClick={() => { computeWitness(plugin, status, witnessValues) }}
disabled={(status === "compiling") || (status === "generating") || (status === "computing")}>
<RenderIf condition={status === 'computing'}>
<i className="fas fa-sync fa-spin mr-2" aria-hidden="true"></i>
</RenderIf>
<RenderIfNot condition={status === 'computing'}>
<i className="fas fa-sync mr-2" aria-hidden="true"></i>
</RenderIfNot>
<FormattedMessage id="circuit.compute" />
</button>
</>
</RenderIf>
</div>
</div>
)
}

@ -0,0 +1,36 @@
import { useState } from "react"
import { FormattedMessage } from "react-intl"
import { RenderIf, RenderIfNot } from "@remix-ui/helper"
export function WitnessToggler ({ children }: { children: JSX.Element }) {
const [toggleExpander, setToggleExpander] = useState<boolean>(false)
const toggleConfigurations = () => {
setToggleExpander(!toggleExpander)
}
return (
<div>
<div className="d-flex circuit_config_section justify-content-between" onClick={toggleConfigurations}>
<div className="d-flex">
<label className="mt-1 circuit_config_section">
<FormattedMessage id="circuit.computeWitness" />
</label>
</div>
<div>
<span data-id="scConfigExpander" onClick={toggleConfigurations}>
<RenderIf condition={toggleExpander}>
<i className="fas fa-angle-down" aria-hidden="true"></i>
</RenderIf>
<RenderIfNot condition={toggleExpander}>
<i className="fas fa-angle-right" aria-hidden="true"></i>
</RenderIfNot>
</span>
</div>
</div>
<RenderIf condition={toggleExpander}>
{ children }
</RenderIf>
</div>
)
}

@ -0,0 +1,4 @@
import {createContext} from 'react'
import {ICircuitAppContext} from '../types'
export const CircuitAppContext = createContext<ICircuitAppContext>({} as ICircuitAppContext)

@ -0,0 +1,77 @@
import {Actions, AppState} from '../types'
import { compiler_list } from 'circom_wasm'
export const appInitialState: AppState = {
version: compiler_list.latest,
versionList: compiler_list.wasm_builds,
filePath: "",
filePathToId: {},
status: "idle",
primeValue: "bn128",
autoCompile: false,
hideWarnings: false,
signalInputs: [],
feedback: null
}
export const appReducer = (state = appInitialState, action: Actions): AppState => {
switch (action.type) {
case 'SET_COMPILER_VERSION':
return {
...state,
version: action.payload
}
case 'SET_FILE_PATH':
return {
...state,
filePath: action.payload
}
case 'SET_COMPILER_STATUS':
return {
...state,
status: action.payload
}
case 'SET_PRIME_VALUE':
return {
...state,
primeValue: action.payload
}
case 'SET_AUTO_COMPILE':
return {
...state,
autoCompile: action.payload
}
case 'SET_HIDE_WARNINGS':
return {
...state,
hideWarnings: action.payload
}
case 'SET_SIGNAL_INPUTS':
return {
...state,
signalInputs: action.payload
}
case 'SET_COMPILER_FEEDBACK':
return {
...state,
feedback: action.payload
}
case 'SET_FILE_PATH_TO_ID':
return {
...state,
filePathToId: action.payload
}
default:
throw new Error()
}
}

@ -1,17 +1,26 @@
import {PluginClient} from '@remixproject/plugin'
import {createClient} from '@remixproject/plugin-webview'
import { PluginClient } from '@remixproject/plugin'
import { createClient } from '@remixproject/plugin-webview'
import EventManager from 'events'
import pathModule from 'path'
import {parse} from 'circom_wasm'
import { parse, compile, generate_witness, generate_r1cs, compiler_list } from 'circom_wasm'
import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper'
import { CompilationConfig, CompilerReport } from '../types'
export class CircomPluginClient extends PluginClient {
public internalEvents: EventManager
private _compilationConfig: CompilationConfig = {
version: "2.1.5",
prime: "bn128"
}
private lastCompiledCircuitPath: string = ''
private lastParsedFiles: Record<string, string> = {}
private lastCompiledFile: string = ''
constructor() {
super()
this.methods = ['init', 'parse', 'compile', 'generateR1cs']
createClient(this)
this.internalEvents = new EventManager()
this.methods = ['init', 'parse']
this.onload()
}
@ -20,24 +29,23 @@ export class CircomPluginClient extends PluginClient {
}
onActivation(): void {
// @ts-ignore
this.on('editor', 'contentChanged', (path: string, fileContent) => {
if (path.endsWith('.circom')) {
this.parse(path, fileContent)
}
})
this.internalEvents.emit('circom_activated')
}
async parse(path: string, fileContent: string): Promise<void> {
let buildFiles = {
[path]: fileContent
async parse(path: string, fileContent?: string): Promise<CompilerReport[]> {
if (!fileContent) {
// @ts-ignore
fileContent = await this.call('fileManager', 'readFile', path)
}
buildFiles = await this.resolveDependencies(path, fileContent, buildFiles)
const parsedOutput = parse(path, buildFiles)
this.lastParsedFiles = {
[path]: fileContent,
}
this.lastParsedFiles = await this.resolveDependencies(path, fileContent, this.lastParsedFiles)
const parsedOutput = parse(path, this.lastParsedFiles)
try {
const result = JSON.parse(parsedOutput)
const result: CompilerReport[] = JSON.parse(parsedOutput.report())
const mapReportFilePathToId = {}
if (result.length === 0) {
// @ts-ignore
@ -47,23 +55,24 @@ export class CircomPluginClient extends PluginClient {
for (const report of result) {
for (const label in report.labels) {
if (report.labels[label].file_id === '0') {
const file_id = report.labels[label].file_id
mapReportFilePathToId[file_id] = parsedOutput.get_report_name(parseInt(file_id))
if (file_id === '0') {
// @ts-ignore
const startPosition: {lineNumber: number; column: number} =
await this.call(
'editor',
// @ts-ignore
'getPositionAt',
report.labels[label].range.start
)
const startPosition: { lineNumber: number; column: number } = await this.call(
'editor',
// @ts-ignore
'getPositionAt',
report.labels[label].range.start
)
// @ts-ignore
const endPosition: {lineNumber: number; column: number} =
await this.call(
'editor',
// @ts-ignore
'getPositionAt',
report.labels[label].range.end
)
const endPosition: { lineNumber: number; column: number } = await this.call(
'editor',
// @ts-ignore
'getPositionAt',
report.labels[label].range.end
)
markers.push({
message: report.message,
@ -71,14 +80,14 @@ export class CircomPluginClient extends PluginClient {
position: {
start: {
line: startPosition.lineNumber,
column: startPosition.column
column: startPosition.column,
},
end: {
line: endPosition.lineNumber,
column: endPosition.column
}
column: endPosition.column,
},
},
file: path
file: path,
})
}
}
@ -92,22 +101,117 @@ export class CircomPluginClient extends PluginClient {
await this.call('editor', 'clearErrorMarkers', [path])
}
}
this.internalEvents.emit('circuit_parsing_done', result, mapReportFilePathToId)
return result
} catch (e) {
console.log(e)
throw new Error(e)
}
}
async compile(path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_compiling_start')
const parseErrors = await this.parse(path)
if (parseErrors && (parseErrors.length > 0)) {
if (parseErrors[0].type === 'Error') {
this.internalEvents.emit('circuit_parsing_errored', parseErrors)
return
} else if (parseErrors[0].type === 'Warning') {
this.internalEvents.emit('circuit_parsing_warning', parseErrors)
}
}
if (compilationConfig) {
const { prime, version } = compilationConfig
if ((prime !== "bn128") && (prime !== "bls12381") && (prime !== "goldilocks")) throw new Error('Invalid prime value')
if (!compiler_list.versions.includes(version)) throw new Error("Unsupported compiler version")
this._compilationConfig.prime = prime
this._compilationConfig.version = version
}
const circuitApi = compile(path, this.lastParsedFiles, { prime: this._compilationConfig.prime })
const circuitProgram = circuitApi.program()
if (circuitProgram.length < 1) {
const circuitErrors = circuitApi.report()
throw new Error(circuitErrors)
} else {
this.lastCompiledFile = path
const fileName = extractNameFromKey(path)
this.lastCompiledCircuitPath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'wasm')
// @ts-ignore
await this.call('fileManager', 'writeFile', this.lastCompiledCircuitPath, circuitProgram, { encoding: null })
const fileContent = this.lastParsedFiles[path]
const searchComponentName = fileContent.match(/component\s+main\s*(?:{[^{}]*})?\s*=\s*([A-Za-z_]\w*)\s*\(.*\)/)
if (searchComponentName) {
const componentName = searchComponentName[1]
const signals = circuitApi.input_signals(componentName)
this.internalEvents.emit('circuit_compiling_done', signals)
} else {
this.internalEvents.emit('circuit_compiling_done', [])
}
}
}
async generateR1cs (path: string, compilationConfig?: CompilationConfig): Promise<void> {
this.internalEvents.emit('circuit_generating_r1cs_start')
const parseErrors = await this.parse(path)
if (parseErrors && (parseErrors.length > 0)) {
if (parseErrors[0].type === 'Error') {
this.internalEvents.emit('circuit_parsing_errored', parseErrors)
return
} else if (parseErrors[0].type === 'Warning') {
this.internalEvents.emit('circuit_parsing_warning', parseErrors)
}
}
if (compilationConfig) {
const { prime, version } = compilationConfig
if ((prime !== "bn128") && (prime !== "bls12381") && (prime !== "goldilocks")) throw new Error('Invalid prime value')
if (!compiler_list.versions.includes(version)) throw new Error("Unsupported compiler version")
this._compilationConfig.prime = prime
this._compilationConfig.version = version
}
const r1csApi = generate_r1cs(path, this.lastParsedFiles, { prime: this._compilationConfig.prime })
const r1csProgram = r1csApi.program()
if (r1csProgram.length < 1) {
const r1csErrors = r1csApi.report()
throw new Error(r1csErrors)
} else {
this.internalEvents.emit('circuit_generating_r1cs_done')
const fileName = extractNameFromKey(path)
const writePath = extractParentFromKey(path) + "/.bin/" + fileName.replace('circom', 'r1cs')
// @ts-ignore
await this.call('fileManager', 'writeFile', writePath, r1csProgram, true)
}
}
async computeWitness (input: string): Promise<void> {
this.internalEvents.emit('circuit_computing_witness_start')
const wasmPath = this.lastCompiledCircuitPath
if (!wasmPath) throw new Error('No wasm file found')
// @ts-ignore
const buffer: any = await this.call('fileManager', 'readFile', wasmPath, { encoding: null })
const dataRead = new Uint8Array(buffer)
const witness = await generate_witness(dataRead, input)
// @ts-ignore
await this.call('fileManager', 'writeFile', wasmPath.replace('.wasm', '.wtn'), witness, true)
this.internalEvents.emit('circuit_computing_witness_done')
}
async resolveDependencies(
filePath: string,
fileContent: string,
output = {},
depPath: string = '',
blackPath: string[] = []
): Promise<Record<string, string>> {
async resolveDependencies(filePath: string, fileContent: string, output = {}, depPath: string = '', blackPath: string[] = []): Promise<Record<string, string>> {
// extract all includes
const includes = (fileContent.match(/include ['"].*['"]/g) || []).map(
(include) => include.replace(/include ['"]/g, '').replace(/['"]/g, '')
)
const includes = (fileContent.match(/include ['"].*['"]/g) || []).map((include) => include.replace(/include ['"]/g, '').replace(/['"]/g, ''))
await Promise.all(
includes.map(async (include) => {
@ -123,12 +227,8 @@ export class CircomPluginClient extends PluginClient {
dependencyContent = await this.call('fileManager', 'readFile', path)
} else {
// if include import (path) does not exist, try to construct relative path using the original file path (current file opened in editor)
let relativePath = pathModule.resolve(
filePath.slice(0, filePath.lastIndexOf('/')),
include
)
if (relativePath.indexOf('/') === 0)
relativePath = relativePath.slice(1)
let relativePath = pathModule.resolve(filePath.slice(0, filePath.lastIndexOf('/')), include)
if (relativePath.indexOf('/') === 0) relativePath = relativePath.slice(1)
const relativePathExists = await this.call(
'fileManager',
// @ts-ignore
@ -138,81 +238,40 @@ export class CircomPluginClient extends PluginClient {
if (relativePathExists) {
// fetch file content if include import exists as a relative path
dependencyContent = await this.call(
'fileManager',
'readFile',
relativePath
)
dependencyContent = await this.call('fileManager', 'readFile', relativePath)
} else {
if (depPath) {
// if depPath is provided, try to resolve include import from './deps' folder in remix
path = pathModule.resolve(
depPath.slice(0, depPath.lastIndexOf('/')),
include
)
if (path.indexOf('/') === 0) path = path.slice(1)
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
if (include.startsWith('circomlib')) {
// try to resolve include import from github if it is a circomlib dependency
const splitInclude = include.split('/')
const version = splitInclude[1].match(/v[0-9]+.[0-9]+.[0-9]+/g)
if (version && version[0]) {
path = `https://raw.githubusercontent.com/iden3/circomlib/${version[0]}/circuits/${splitInclude.slice(2).join('/')}`
dependencyContent = await this.call('contentImport', 'resolveAndSave', path, null)
} else {
path = `https://raw.githubusercontent.com/iden3/circomlib/master/circuits/${splitInclude.slice(1).join('/')}`
dependencyContent = await this.call('contentImport', 'resolveAndSave', path, null)
}
} else {
if (include.startsWith('circomlib')) {
// try to resolve include import from github if it is a circomlib dependency
const splitInclude = include.split('/')
const version = splitInclude[1].match(/v[0-9]+.[0-9]+.[0-9]+/g)
if (version && version[0]) {
path = `https://raw.githubusercontent.com/iden3/circomlib/${
version[0]
}/circuits/${splitInclude.slice(2).join('/')}`
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
} else {
path = `https://raw.githubusercontent.com/iden3/circomlib/master/circuits/${splitInclude
.slice(1)
.join('/')}`
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
}
if (depPath) {
// if depPath is provided, try to resolve include import from './deps' folder in remix
path = pathModule.resolve(depPath.slice(0, depPath.lastIndexOf('/')), include)
if (path.indexOf('/') === 0) path = path.slice(1)
dependencyContent = await this.call('contentImport', 'resolveAndSave', path, null)
} else {
// If all import cases are not true, use the default import to try fetching from node_modules and unpkg
dependencyContent = await this.call(
'contentImport',
'resolveAndSave',
path,
null
)
dependencyContent = await this.call('contentImport', 'resolveAndSave', path, null)
}
}
}
}
// extract all includes from the dependency content
const dependencyIncludes = (
dependencyContent.match(/include ['"].*['"]/g) || []
).map((include) =>
include.replace(/include ['"]/g, '').replace(/['"]/g, '')
)
const dependencyIncludes = (dependencyContent.match(/include ['"].*['"]/g) || []).map((include) => include.replace(/include ['"]/g, '').replace(/['"]/g, ''))
blackPath.push(include)
// recursively resolve all dependencies of the dependency
if (dependencyIncludes.length > 0) {
await this.resolveDependencies(
filePath,
dependencyContent,
output,
path,
blackPath
)
await this.resolveDependencies(filePath, dependencyContent, output, path, blackPath)
output[include] = dependencyContent
} else {
output[include] = dependencyContent
@ -221,4 +280,40 @@ export class CircomPluginClient extends PluginClient {
)
return output
}
async resolveReportPath (path: string): Promise<string> {
// @ts-ignore
const pathExists = await this.call('fileManager', 'exists', path)
if (pathExists) return path
else {
// if include import (path) does not exist, try to construct relative path using the original file path (current file opened in editor)
let relativePath = pathModule.resolve(this.lastCompiledFile.slice(0, this.lastCompiledFile.lastIndexOf('/')), path)
if (relativePath.indexOf('/') === 0) relativePath = relativePath.slice(1)
// @ts-ignore
const relativePathExists = await this.call('fileManager', 'exists', relativePath)
if (relativePathExists) return relativePath
else {
if (path.startsWith('circomlib')) {
// try to resolve include import from github if it is a circomlib dependency
const splitInclude = path.split('/')
const version = splitInclude[1].match(/v[0-9]+.[0-9]+.[0-9]+/g)
if (version && version[0]) {
path = `/.deps/https/raw.githubusercontent.com/iden3/circomlib/${version[0]}/circuits/${splitInclude.slice(2).join('/')}`
} else {
path = `/.deps/https/raw.githubusercontent.com/iden3/circomlib/master/circuits/${splitInclude.slice(1).join('/')}`
}
// @ts-ignore
const exists = await this.call('fileManager', 'exists', path)
if (exists) return path
else throw new Error(`Report path ${path} do no exist in the Remix FileSystem`)
} else {
throw new Error(`Report path ${path} do no exist in the Remix FileSystem`)
}
}
}
}
}

@ -0,0 +1,87 @@
import { compiler_list } from 'circom_wasm'
import {Dispatch} from 'react'
import { CircomPluginClient } from '../services/circomPluginClient'
export type CompilerStatus = "compiling" | "generating" | "computing" | "idle" | "errored" | "warning"
export interface ICircuitAppContext {
appState: AppState
dispatch: Dispatch<Actions>,
plugin: CircomPluginClient
}
export interface ActionPayloadTypes {
SET_COMPILER_VERSION: string,
SET_FILE_PATH: string,
SET_COMPILER_STATUS: CompilerStatus,
SET_PRIME_VALUE: PrimeValue,
SET_AUTO_COMPILE: boolean,
SET_HIDE_WARNINGS: boolean,
SET_SIGNAL_INPUTS: string[],
SET_COMPILER_FEEDBACK: string | CompilerReport[]
SET_FILE_PATH_TO_ID: Record<number, string>
}
export interface Action<T extends keyof ActionPayloadTypes> {
type: T
payload: ActionPayloadTypes[T]
}
export type Actions = {[A in keyof ActionPayloadTypes]: Action<A>}[keyof ActionPayloadTypes]
export interface AppState {
version: string,
versionList: typeof compiler_list.wasm_builds,
filePath: string,
filePathToId: Record<string, string>,
status: CompilerStatus,
primeValue: PrimeValue,
autoCompile: boolean,
hideWarnings: boolean,
signalInputs: string[],
feedback: string | CompilerReport[]
}
export type CompilationConfig = {
prime: PrimeValue,
version: string
}
export type PrimeValue = "bn128" | "bls12381" | "goldilocks"
export type CompilerFeedbackProps = {
feedback: string | CompilerReport[],
filePathToId: Record<string, string>,
openErrorLocation: (location: string, startRange: string) => void,
hideWarnings: boolean
}
export type CompilerReport = {
type: "Error" | "Bug" | "Help" | "Note" | "Warning" | "Unknown",
message: string,
labels: {
style: "Primary" | "Secondary" | "Unknown",
file_id: string,
range: {
start: string,
end: string
},
message: string
}[],
notes: string[]
}
export type FeedbackAlertProps = {
message: string,
location: string
}
export type ConfigurationsProps = {
setPrimeValue: (prime: PrimeValue) => void,
primeValue: PrimeValue
}
export type CompileOptionsProps = {
setCircuitAutoCompile: (value: boolean) => void,
setCircuitHideWarnings: (value: boolean) => void,
autoCompile: boolean,
hideWarnings: boolean
}

@ -0,0 +1,88 @@
body {
font-size : .8rem;
}
.circuit_section {
padding: 12px 24px 16px;
}
.circuit_label {
margin-bottom: 2px;
font-size: 11px;
line-height: 12px;
text-transform: uppercase;
}
.circuit_warnings_box {
display: flex;
align-items: center;
}
.circuit_warnings_box label {
margin: 0;
}
.circuit_config_section:hover {
cursor: pointer;
}
.circuit_config_section {
font-size: 1rem;
}
.circuit_config {
display: flex;
align-items: center;
}
.circuit_config label {
margin: 0;
}
.circuit_inner_label {
margin-bottom: 2px;
font-size: 11px;
line-height: 12px;
text-transform: uppercase;
}
.circuit_errors_box {
padding-left: 5px;
padding-right: 5px;
word-break: break-word;
}
.circuit_feedback.success,
.circuit_feedback.error,
.circuit_feedback.warning {
white-space: pre-line;
word-wrap: break-word;
cursor: pointer;
position: relative;
margin: 0.5em 0 1em 0;
border-radius: 5px;
line-height: 20px;
padding: 8px 15px;
}
.circuit_feedback.success pre,
.circuit_feedback.error pre,
.circuit_feedback.warning pre {
white-space: pre-line;
overflow-y: hidden;
background-color: transparent;
margin: 0;
font-size: 12px;
border: 0 none;
padding: 0;
border-radius: 0;
}
.circuit_feedback.success .close,
.circuit_feedback.error .close,
.circuit_feedback.warning .close {
visibility: hidden;
white-space: pre-line;
font-weight: bold;
position: absolute;
color: hsl(0, 0%, 0%); /* black in style-guide.js */
top: 0;
right: 0;
padding: 0.5em;
}
.circuit_feedback.success a,
.circuit_feedback.error a,
.circuit_feedback.warning a {
bottom: 0;
right: 0;
}

@ -0,0 +1,27 @@
pragma circom 2.1.4;
include "circomlib/poseidon.circom";
// include "https://github.com/0xPARC/circom-secp256k1/blob/master/circuits/bigint.circom";
template Example () {
signal input a;
signal input b;
signal output c;
var unused = 4;
c <== a * b;
assert(a > 2);
component hash = Poseidon(2);
hash.inputs[0] <== a;
hash.inputs[1] <== b;
log("hash", hash.out);
}
component main { public [ a ] } = Example();
/* INPUT = {
"a": "5",
"b": "77"
} */

@ -4,7 +4,9 @@ template Multiplier2() {
signal input a;
signal input b;
signal output c;
signal output d;
c <== a*b;
d <== c*b;
}
component main = Multiplier2();

@ -6,10 +6,14 @@
<base href="./" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"/>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"/> -->
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> -->
<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="snarkjs.min.js"> </script>
<script src="https://kit.fontawesome.com/41dd021e94.js" crossorigin="anonymous"></script>
</body>
</html>

@ -1,15 +1,15 @@
{
"name": "circuit-compiler",
"kind": "provider",
"displayName": "Circuit Compiler",
"displayName": "Circom ZKP compiler",
"events": [],
"version": "2.0.0",
"methods": ["init", "parse"],
"methods": ["init", "parse", "compile", "generateR1cs"],
"canActivate": [],
"url": "",
"description": "Enables circuit compilation and computing a witness for ZK proofs",
"icon": "https://docs.circom.io/assets/images/favicon.png",
"location": "hiddenPanel",
"icon": "assets/img/circom-icon-bw-800b.webp",
"location": "sidePanel",
"documentation": "",
"repo": "https://github.com/ethereum/remix-project/tree/master/apps/circuit-compiler",
"maintainedBy": "Remix",

File diff suppressed because one or more lines are too long

@ -4,17 +4,14 @@ import init from '../helpers/init'
module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'Should create new file': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="homeTabNewFile"]')
.click('*[data-id="homeTabNewFile"]')
.waitForElementContainsText('*[data-id$="/blank"]', '', 60000)
.sendKeys('*[data-id$="/blank"] .remixui_items', 'newTestFile')
.sendKeys('*[data-id$="/blank"] .remixui_items', browser.Keys.ENTER)
.waitForElementVisible('li[data-id="treeViewLitreeViewItemnewTestFile.sol"]')
}
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done)
},
'Should start coding': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="homeTabStartCoding"]')
.click('*[data-id="homeTabStartCoding"]')
.waitForElementVisible('div[data-id="treeViewDivtreeViewItemcontracts/helloWorld.sol"]')
}
}

@ -235,7 +235,7 @@ const sources = [
content:
`
pragma solidity ^0.8.0;
contract helloWorld {
contract HelloWorld {
string public message;
fallback () external {
@ -249,7 +249,7 @@ const sources = [
},
'checkBalance.sol': {
content: `pragma solidity ^0.8.0;
contract checkBalance {
contract CheckBalance {
constructor () payable {}
function sendSomeEther(uint256 num) public {

@ -22,9 +22,11 @@ const profile = {
icon: 'assets/img/fileManager.webp',
permission: true,
version: packageJson.version,
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir',
'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'getFolder', 'setFile', 'switchFile', 'refresh',
'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory'],
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'writeFileNoRewrite',
'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile',
'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath',
'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory'
],
kind: 'file-system'
}
const errorMsg = {
@ -37,7 +39,6 @@ const errorMsg = {
const createError = (err) => {
return new Error(`${errorMsg[err.code]} ${err.message || ''}`)
}
class FileManager extends Plugin {
mode: string
openedFiles: any
@ -201,15 +202,15 @@ class FileManager extends Plugin {
* @param {string} data content to write on the file
* @returns {void}
*/
async writeFile(path, data) {
async writeFile(path, data, options?) {
try {
path = this.normalize(path)
path = this.limitPluginScope(path)
if (await this.exists(path)) {
await this._handleIsFile(path, `Cannot write file ${path}`)
return await this.setFileContent(path, data)
return await this.setFileContent(path, data, options)
} else {
const ret = await this.setFileContent(path, data)
const ret = await this.setFileContent(path, data, options)
this.emit('fileAdded', path)
return ret
}
@ -249,19 +250,43 @@ class FileManager extends Plugin {
throw new Error(e)
}
}
/**
* Set the content of a specific file, does nnot rewrite file if it exists but creates a new unique name
* @param {string} path path of the file
* @param {string} data content to write on the file
* @returns {void}
*/
async writeFileNoRewrite(path, data) {
try {
path = this.normalize(path)
path = this.limitPluginScope(path)
if (await this.exists(path)) {
const newPath = await helper.createNonClashingNameAsync(path, this)
const content = await this.setFileContent(newPath, data)
return {newContent: content, newPath}
} else {
const ret = await this.setFileContent(path, data)
this.emit('fileAdded', path)
return {newContent: ret, newpath: path}
}
} catch (e) {
throw new Error(e)
}
}
/**
* Return the content of a specific file
* @param {string} path path of the file
* @returns {string} content of the file
*/
async readFile(path) {
async readFile(path, options?) {
try {
path = this.normalize(path)
path = this.limitPluginScope(path)
await this._handleExists(path, `Cannot read file ${path}`)
await this._handleIsFile(path, `Cannot read file ${path}`)
return this.getFileContent(path)
return this.getFileContent(path, options)
} catch (e) {
throw new Error(e)
}
@ -579,7 +604,7 @@ class FileManager extends Plugin {
return path ? path[1] : '/'
}
getFileContent(path) {
getFileContent(path, options?) {
const provider = this.fileProviderOf(path)
if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` })
@ -589,11 +614,11 @@ class FileManager extends Plugin {
provider.get(path, (err, content) => {
if (err) reject(err)
resolve(content)
})
}, options)
})
}
async setFileContent(path, content) {
async setFileContent(path, content, options?) {
if (this.currentRequest) {
const canCall = await this.askUserPermission(`writeFile`, `modifying ${path} ...`)
const required = this.appManager.isRequired(this.currentRequest.from)
@ -602,10 +627,10 @@ class FileManager extends Plugin {
this.call('notification', 'toast', fileChangedToastMsg(this.currentRequest.from, path))
}
}
return await this._setFileInternal(path, content)
return await this._setFileInternal(path, content, options)
}
_setFileInternal(path, content) {
_setFileInternal(path, content, options?) {
const provider = this.fileProviderOf(path)
if (!provider) throw createError({ code: 'ENOENT', message: `${path} not available` })
// TODO : Add permission
@ -616,7 +641,7 @@ class FileManager extends Plugin {
this.syncEditor(path)
this.emit('fileSaved', path)
resolve(true)
})
}, options)
})
}

@ -87,12 +87,12 @@ class FileProvider {
cb()
}
async get (path, cb) {
async get (path, cb, options = { encoding: 'utf8' }) {
cb = cb || function () { /* do nothing. */ }
path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here
var unprefixedpath = this.removePrefix(path)
try {
const content = await window.remixFileSystem.readFile(unprefixedpath, 'utf8')
const content = await window.remixFileSystem.readFile(unprefixedpath, options)
if (cb) cb(null, content)
return content
} catch (err) {
@ -101,17 +101,17 @@ class FileProvider {
}
}
async set (path, content, cb) {
async set (path, content, cb, options = { encoding: 'utf8' }) {
cb = cb || function () { /* do nothing. */ }
var unprefixedpath = this.removePrefix(path)
const exists = await window.remixFileSystem.exists(unprefixedpath)
if (exists && await window.remixFileSystem.readFile(unprefixedpath, 'utf8') === content) {
if (exists && await window.remixFileSystem.readFile(unprefixedpath, options) === content) {
if (cb) cb()
return null
}
await this.createDir(path.substr(0, path.lastIndexOf('/')))
try {
await window.remixFileSystem.writeFile(unprefixedpath, content, 'utf8')
await window.remixFileSystem.writeFile(unprefixedpath, content, options)
} catch (e) {
if (cb) cb(e)
return false

@ -30,7 +30,19 @@ const { SlitherHandle } = require('../files/slither-handle.js')
const profile = {
name: 'filePanel',
displayName: 'File explorer',
methods: ['createNewFile', 'uploadFile', 'getCurrentWorkspace', 'getAvailableWorkspaceName', 'getWorkspaces', 'createWorkspace', 'setWorkspace', 'registerContextMenuItem', 'renameWorkspace', 'deleteWorkspace'],
methods: [
'createNewFile',
'uploadFile',
'getCurrentWorkspace',
'getAvailableWorkspaceName',
'getWorkspaces',
'createWorkspace',
'switchToWorkspace',
'setWorkspace',
'registerContextMenuItem',
'renameWorkspace',
'deleteWorkspace',
],
events: ['setWorkspace', 'workspaceRenamed', 'workspaceDeleted', 'workspaceCreated'],
icon: 'assets/img/fileManager.webp',
description: 'Remix IDE file explorer',
@ -41,7 +53,7 @@ const profile = {
maintainedBy: 'Remix'
}
module.exports = class Filepanel extends ViewPlugin {
constructor (appManager) {
constructor(appManager) {
super(profile)
this.registry = Registry.getInstance()
this.fileProviders = this.registry.get('fileproviders').api
@ -60,13 +72,17 @@ module.exports = class Filepanel extends ViewPlugin {
this.currentWorkspaceMetadata = null
}
render () {
return <div id='fileExplorerView'><FileSystemProvider plugin={this} /></div>
render() {
return (
<div id="fileExplorerView">
<FileSystemProvider plugin={this} />
</div>
)
}
/**
* @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] }
* typically:
* typically:
* group 0 for file manipulations
* group 1 for download operations
* group 2 for running operations (script for instance)
@ -77,7 +93,7 @@ module.exports = class Filepanel extends ViewPlugin {
* group 7 for generating resource files (UML, documentation, ...)
* @param callback (...args) => void
*/
registerContextMenuItem (item) {
registerContextMenuItem(item) {
return new Promise((resolve, reject) => {
this.emit('registerContextMenuItemReducerEvent', item, (err, data) => {
if (err) reject(err)
@ -86,7 +102,7 @@ module.exports = class Filepanel extends ViewPlugin {
})
}
removePluginActions (plugin) {
removePluginActions(plugin) {
return new Promise((resolve, reject) => {
this.emit('removePluginActionsReducerEvent', plugin, (err, data) => {
if (err) reject(err)
@ -95,30 +111,30 @@ module.exports = class Filepanel extends ViewPlugin {
})
}
getCurrentWorkspace () {
getCurrentWorkspace() {
return this.currentWorkspaceMetadata
}
getWorkspaces () {
getWorkspaces() {
return this.workspaces
}
getAvailableWorkspaceName (name) {
if(!this.workspaces) return name
getAvailableWorkspaceName(name) {
if (!this.workspaces) return name
let index = 1
let workspace = this.workspaces.find(workspace => workspace.name === name + ' - ' + index)
let workspace = this.workspaces.find((workspace) => workspace.name === name + ' - ' + index)
while (workspace) {
index++
workspace = this.workspaces.find(workspace => workspace.name === name + ' - ' + index)
workspace = this.workspaces.find((workspace) => workspace.name === name + ' - ' + index)
}
return name + ' - ' + index
}
setWorkspaces (workspaces) {
setWorkspaces(workspaces) {
this.workspaces = workspaces
}
createNewFile () {
createNewFile() {
return new Promise((resolve, reject) => {
this.emit('createNewFileInputReducerEvent', '/', (err, data) => {
if (err) reject(err)
@ -127,7 +143,7 @@ module.exports = class Filepanel extends ViewPlugin {
})
}
uploadFile (target) {
uploadFile(target) {
return new Promise((resolve, reject) => {
return this.emit('uploadFileReducerEvent', '/', target, (err, data) => {
if (err) reject(err)
@ -136,7 +152,7 @@ module.exports = class Filepanel extends ViewPlugin {
})
}
createWorkspace (workspaceName, workspaceTemplateName, isEmpty) {
createWorkspace(workspaceName, workspaceTemplateName, isEmpty) {
return new Promise((resolve, reject) => {
this.emit('createWorkspaceReducerEvent', workspaceName, workspaceTemplateName, isEmpty, (err, data) => {
if (err) reject(err)
@ -145,7 +161,7 @@ module.exports = class Filepanel extends ViewPlugin {
})
}
renameWorkspace (oldName, workspaceName) {
renameWorkspace(oldName, workspaceName) {
return new Promise((resolve, reject) => {
this.emit('renameWorkspaceReducerEvent', oldName, workspaceName, (err, data) => {
if (err) reject(err)
@ -154,7 +170,7 @@ module.exports = class Filepanel extends ViewPlugin {
})
}
deleteWorkspace (workspaceName) {
deleteWorkspace(workspaceName) {
return new Promise((resolve, reject) => {
this.emit('deleteWorkspaceReducerEvent', workspaceName, (err, data) => {
if (err) reject(err)
@ -163,25 +179,52 @@ module.exports = class Filepanel extends ViewPlugin {
})
}
setWorkspace (workspace) {
const workspaceProvider = this.fileProviders.workspace
saveRecent(workspaceName) {
if (!localStorage.getItem('recentWorkspaces')) {
localStorage.setItem('recentWorkspaces', JSON.stringify([ workspaceName ]))
} else {
let recents = JSON.parse(localStorage.getItem('recentWorkspaces'))
// checking if we have a duplication
if (!recents.find((el) => {
return el === workspaceName
})) {
recents = ([workspaceName, ...recents])
recents = recents.filter((el) => { return el != "" })
localStorage.setItem('recentWorkspaces', JSON.stringify(recents))
}
}
}
this.currentWorkspaceMetadata = { name: workspace.name, isLocalhost: workspace.isLocalhost, absolutePath: `${workspaceProvider.workspacesPath}/${workspace.name}` }
if (workspace.name !== " - connect to localhost - ") {
setWorkspace(workspace) {
const workspaceProvider = this.fileProviders.workspace
const current = this.currentWorkspaceMetadata
this.currentWorkspaceMetadata = {
name: workspace.name,
isLocalhost: workspace.isLocalhost,
absolutePath: `${workspaceProvider.workspacesPath}/${workspace.name}`,
}
if (this.currentWorkspaceMetadata.name !== current) {
this.saveRecent(workspace.name)
}
if (workspace.name !== ' - connect to localhost - ') {
localStorage.setItem('currentWorkspace', workspace.name)
}
this.emit('setWorkspace', workspace)
}
workspaceRenamed (oldName, workspaceName) {
switchToWorkspace(workspaceName) {
this.emit('switchToWorkspace', workspaceName)
}
workspaceRenamed(oldName, workspaceName) {
this.emit('workspaceRenamed', oldName, workspaceName)
}
workspaceDeleted (workspace) {
workspaceDeleted(workspace) {
this.emit('workspaceDeleted', workspace)
}
workspaceCreated (workspace) {
workspaceCreated(workspace) {
this.emit('workspaceCreated', workspace)
}
/** end section */

@ -104,16 +104,22 @@ export abstract class InjectedProvider extends Plugin implements IProvider {
if (error.data && error.data.originalError && error.data.originalError.data) {
resolve({
jsonrpc: '2.0',
error: error.data.originalError.message,
errorData: error.data.originalError.data,
error: error.data.originalError,
id: data.id
})
} else
} else if (error.data && error.data.message) {
resolve({
jsonrpc: '2.0',
error: error.data && error.data.message ? error.data.message : error.message,
error: error.data && error.data,
id: data.id
})
} else {
resolve({
jsonrpc: '2.0',
error,
id: data.id
})
}
}
}
}

@ -0,0 +1,15 @@
{
"circuit.compiler": "Compiler",
"circuit.autoCompile": "Auto compile",
"circuit.hideWarnings": "Hide warnings",
"circuit.advancedConfigurations": "Advanced Configurations",
"circuit.compilerConfiguration": "Compiler configuration",
"circuit.prime": "Prime",
"circuit.useConfigurationFile": "Use configuration file",
"circuit.compile": "Compile",
"circuit.noFileSelected": "no file selected",
"circuit.generateR1cs": "Generate R1CS",
"circuit.computeWitness": "Compute Witness",
"circuit.signalInput": "Signal Input",
"circuit.compute": "Compute"
}

@ -103,6 +103,7 @@
"filePanel.mintable": "Mintable",
"filePanel.burnable": "Burnable",
"filePanel.pausable": "Pausable",
"filePanel.semaphore": "Semaphore",
"filePanel.transparent": "Transparent",
"filePanel.initGitRepoTitle": "Check option to initialize workspace as a new git repository",
"filePanel.switchToBranchTitle1": "Checkout new branch from remote branch",

@ -56,7 +56,10 @@
"home.searchDocumentation": "Search Documentation",
"home.files": "Files",
"home.newFile": "New File",
"home.startCoding": "Start Coding",
"home.startCodingPlayground": "Open a playground for prototyping and simply learning",
"home.openFile": "Open File",
"home.openFileTooltip": "Open a File from your File System",
"home.accessFileSystem": "Access File System",
"home.loadFrom": "Load from",
"home.resources": "Resources",

@ -13,6 +13,7 @@ import permissionHandlerJson from './permissionHandler.json';
import solUmlGenJson from './solUmlGen.json'
import remixAppJson from './remixApp.json'
import remixUiTabsJson from './remixUiTabs.json'
import circuitJson from './circuit.json';
export default {
...debuggerJson,
@ -30,4 +31,5 @@ export default {
...solUmlGenJson,
...remixAppJson,
...remixUiTabsJson,
...circuitJson
}

@ -0,0 +1,15 @@
{
"circuit.compiler": "Compiler",
"circuit.autoCompile": "Auto compile",
"circuit.hideWarnings": "Hide warnings",
"circuit.advancedConfigurations": "Advanced Configurations",
"circuit.compilerConfiguration": "Compiler configuration",
"circuit.prime": "Prime",
"circuit.useConfigurationFile": "Use configuration file",
"circuit.compile": "Compile",
"circuit.noFileSelected": "no file selected",
"circuit.generateR1cs": "Generate R1CS",
"circuit.computeWitness": "Compute Witness",
"circuit.signalInput": "Signal Input",
"circuit.compute": "Compute"
}

@ -0,0 +1,15 @@
{
"circuit.compiler": "Compiler",
"circuit.autoCompile": "Auto compile",
"circuit.hideWarnings": "Hide warnings",
"circuit.advancedConfigurations": "Advanced Configurations",
"circuit.compilerConfiguration": "Compiler configuration",
"circuit.prime": "Prime",
"circuit.useConfigurationFile": "Use configuration file",
"circuit.compile": "Compile",
"circuit.noFileSelected": "no file selected",
"circuit.generateR1cs": "Generate R1CS",
"circuit.computeWitness": "Compute Witness",
"circuit.signalInput": "Signal Input",
"circuit.compute": "Compute"
}

@ -0,0 +1,16 @@
{
"circuit-compiler.displayName": "Circuit 编译器",
"circuit.compiler": "Compiler",
"circuit.autoCompile": "自动编译",
"circuit.hideWarnings": "隐藏警告",
"circuit.advancedConfigurations": "高级配置",
"circuit.compilerConfiguration": "编译器配置",
"circuit.prime": "质数",
"circuit.useConfigurationFile": "使用配置文件",
"circuit.compile": "编译",
"circuit.noFileSelected": "未选中文件",
"circuit.generateR1cs": "生成R1CS",
"circuit.computeWitness": "证人计算器",
"circuit.signalInput": "输入信号",
"circuit.compute": "计算"
}

@ -14,6 +14,7 @@ import solUmlGenJson from './solUmlGen.json'
import remixAppJson from './remixApp.json'
import remixUiTabsJson from './remixUiTabs.json'
import enJson from '../en';
import circuitJson from './circuit.json';
// There may have some un-translated content. Always fill in the gaps with EN JSON.
// No need for a defaultMessage prop when render a FormattedMessage component.
@ -33,4 +34,5 @@ export default Object.assign({}, enJson, {
...solUmlGenJson,
...remixAppJson,
...remixUiTabsJson,
...circuitJson
})

@ -133,8 +133,7 @@ export class RunTab extends ViewPlugin {
}
},
provider: {
async sendAsync (payload) {
sendAsync (payload) {
return udapp.call(name, 'sendAsync', payload)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

File diff suppressed because one or more lines are too long

@ -853,16 +853,6 @@ export class Blockchain extends Plugin {
try {
this.txRunner.rawRun(tx, confirmationCb, continueCb, promptCb, async (error, result) => {
if (error) {
if (typeof error !== 'string') {
if (error.message) error = error.message
else {
try {
error = 'error: ' + JSON.stringify(error)
} catch (e) {
console.log(e)
}
}
}
return reject(error)
}
@ -880,18 +870,7 @@ export class Blockchain extends Plugin {
return resolve({result, tx})
})
} catch (err) {
let error = err
if (error && typeof error !== 'string') {
if (error.message) error = error.message
else {
try {
error = 'error: ' + JSON.stringify(error)
} catch (e) {
console.log(e)
}
}
}
return reject(error)
return reject(err)
}
})
}
@ -962,14 +941,11 @@ export class Blockchain extends Plugin {
cb(null, txResult, address, returnValue)
} catch (error) {
if (this.isInjectedWeb3()) {
let errorObj = error.replace('Returned error: ', '')
errorObj = JSON.parse(errorObj)
if (errorObj.errorData) {
const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas')
const injectedError = txExecution.checkError({ errorMessage: errorObj.error, errorData: errorObj.errorData }, compiledContracts)
cb(injectedError.message)
} else
cb(error)
const errorMessage = error.innerError ? error.innerError.message : error.message
const errorData = error.innerError ? error.innerError.data : error.data
const compiledContracts = await this.call('compilerArtefacts', 'getAllContractDatas')
const injectedError = txExecution.checkError({ errorMessage, errorData }, compiledContracts)
cb(injectedError.message)
} else
cb(error)
}

@ -86,20 +86,16 @@ export class ExecutionContext {
else if (id === 11155111) name = 'Sepolia'
else name = 'Custom'
if (id === '1') {
web3.eth.getBlock(0, (error, block) => {
if (error) console.log('cant query first block')
if (id === 1) {
web3.eth.getBlock(0).then((block) => {
if (block && block.hash !== this.mainNetGenesisHash) name = 'Custom'
callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
})
}).catch((error) => callback(error))
} else {
callback(err, { id, name, lastBlock: this.lastBlock, currentFork: this.currentFork })
}
}
const res = web3.eth.net.getId(cb)
if(res && typeof res.then ==='function'){
res.then(id=>cb(null,id)).catch(err=>cb(err))
}
web3.eth.net.getId().then(id=>cb(null,parseInt(id))).catch(err=>cb(err))
}
}

@ -10,12 +10,16 @@ export class InjectedProvider {
}
getAccounts (cb) {
return this.executionContext.web3().eth.getAccounts(cb)
return this.executionContext.web3().eth.getAccounts()
.then(accounts => cb(null, accounts))
.catch(err => {
cb(err.message)
})
}
newAccount (passwordPromptCb, cb) {
passwordPromptCb((passphrase) => {
this.executionContext.web3().eth.personal.newAccount(passphrase, cb)
this.executionContext.web3().eth.personal.newAccount(passphrase).then((result) => cb(null, result)).catch(error => cb(error))
})
}
@ -29,16 +33,16 @@ export class InjectedProvider {
}
getGasPrice (cb) {
this.executionContext.web3().eth.getGasPrice(cb)
this.executionContext.web3().eth.getGasPrice().then((result => cb(null, result)))
}
signMessage (message, account, _passphrase, cb) {
const messageHash = hashPersonalMessage(Buffer.from(message))
try {
message = isHexString(message) ? message : Web3.utils.utf8ToHex(message)
this.executionContext.web3().eth.personal.sign(message, account, (error, signedData) => {
this.executionContext.web3().eth.personal.sign(message, account).then((error, signedData) => {
cb(error, '0x' + messageHash.toString('hex'), signedData)
})
}).catch((error => cb(error)))
} catch (e) {
cb(e.message)
}

@ -100,7 +100,7 @@ export class VMProvider {
}
getGasPrice (cb) {
this.web3.eth.getGasPrice(cb)
this.web3.eth.getGasPrice().then((result => cb(null, result))).catch((error) => cb(error))
}
signMessage (message, account, _passphrase, cb) {

@ -5,8 +5,7 @@ import {IframePlugin} from '@remixproject/engine-web'
const _paq = (window._paq = window._paq || [])
// requiredModule removes the plugin from the plugin manager list on UI
const requiredModules = [
// services + layout views + system views
const requiredModules = [ // services + layout views + system views
'manager',
'config',
'compilerArtefacts',
@ -74,7 +73,8 @@ const requiredModules = [
'compilationDetails',
'contractflattener',
'solidity-script',
'openaigpt'
'openaigpt',
'home'
]
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
@ -113,7 +113,8 @@ export function isNative(name) {
'injected-ephemery-testnet-provider',
'injected',
'doc-gen',
'doc-viewer'
'doc-viewer',
'circuit-compiler'
]
return nativePlugins.includes(name) || requiredModules.includes(name)
}

@ -81,7 +81,7 @@ export function checkError (execResult, compiledContracts) {
}
const exceptionError = execResult.errorMessage || ''
const error = `Error occured: ${execResult.errorMessage}.\n`
let msg
let msg = ''
if (exceptionError === errorCode.INVALID_OPCODE) {
msg = '\t\n\tThe execution might have thrown.\n'
ret.error = true

@ -69,7 +69,10 @@ export class TxRunnerWeb3 {
cb(null, res.transactionHash)
} catch (e) {
console.log(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
cb(null, e.receipt.transactionHash)
// in case the receipt is available, we consider that only the execution failed but the transaction went through.
// So we don't consider this to be an error.
if (e.receipt) cb(null, e.receipt.transactionHash)
else cb(e, null)
}
},
() => {
@ -82,7 +85,10 @@ export class TxRunnerWeb3 {
cb(null, res.transactionHash)
} catch (e) {
console.log(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
cb(null, e.receipt.transactionHash)
// in case the receipt is available, we consider that only the execution failed but the transaction went through.
// So we don't consider this to be an error.
if (e.receipt) cb(null, e.receipt.transactionHash)
else cb(e, null)
}
}
}
@ -143,7 +149,7 @@ export class TxRunnerWeb3 {
}, (error) => {
callback(error)
})
})
}, callback)
})
.catch(err => {
if (err && err.message.indexOf('Invalid JSON RPC response') !== -1) {
@ -163,7 +169,7 @@ export class TxRunnerWeb3 {
}, (error) => {
callback(error)
})
})
}, callback)
})
})
}

@ -81,7 +81,7 @@ export const getPathIcon = (path: string) => {
? 'small fak fa-ts-logo' : path.endsWith('.tsc')
? 'fad fa-brackets-curly' : path.endsWith('.cairo')
? 'small fak fa-cairo' : path.endsWith('.circom')
? 'fak fa-circom-plug1' : 'far fa-file'
? 'fak fa-circom' : 'far fa-file'
}
export const isNumeric = (value) => {

@ -1,10 +1,11 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, {useState, useRef, useReducer} from 'react'
import {FormattedMessage} from 'react-intl'
import {ModalDialog} from '@remix-ui/modal-dialog' // eslint-disable-line
import {Toaster} from '@remix-ui/toaster' // eslint-disable-line
import React, { useState, useRef, useReducer, useEffect } from 'react'
import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
const _paq = (window._paq = window._paq || []) // eslint-disable-line
import {CustomTooltip} from '@remix-ui/helper'
import { CustomTooltip } from '@remix-ui/helper'
import { TEMPLATE_NAMES } from '@remix-ui/workspace'
interface HomeTabFileProps {
plugin: any
@ -25,7 +26,7 @@ const loadingReducer = (state = loadingInitialState, action) => {
}
}
function HomeTabFile({plugin}: HomeTabFileProps) {
function HomeTabFile({ plugin }: HomeTabFileProps) {
const [state, setState] = useState<{
searchInput: string
showModalDialog: boolean
@ -37,18 +38,55 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
}
importSource: string
toasterMsg: string
recentWorkspaces: Array<string>
}>({
searchInput: '',
showModalDialog: false,
modalInfo: {title: '', loadItem: '', examples: [], prefix: ''},
modalInfo: { title: '', loadItem: '', examples: [], prefix: '' },
importSource: '',
toasterMsg: ''
toasterMsg: '',
recentWorkspaces: []
})
const [, dispatch] = useReducer(loadingReducer, loadingInitialState)
const inputValue = useRef(null)
useEffect(() => {
plugin.on('filePanel', 'setWorkspace', async () => {
let recents = JSON.parse(localStorage.getItem('recentWorkspaces'))
if (!recents) {
recents = []
} else {
setState((prevState) => {
return { ...prevState, recentWorkspaces: recents.slice(0, recents.length <= 3 ? recents.length : 3) }
})
}
})
const deleteSavedWorkspace = (name) => {
const recents = JSON.parse(localStorage.getItem('recentWorkspaces'))
let newRecents = recents
if (!recents) {
newRecents = []
} else {
newRecents = recents.filter((el) => { return el !== name})
localStorage.setItem('recentWorkspaces', JSON.stringify(newRecents))
}
setState((prevState) => {
return { ...prevState, recentWorkspaces: newRecents.slice(0, newRecents.length <= 3 ? newRecents.length : 3) }
})
}
plugin.on('filePanel', 'workspaceDeleted', async (deletedName) => {
deleteSavedWorkspace(deletedName)
})
return () => {
plugin.off('filePanel', 'setWorkspace')
plugin.off('filePanel', 'workspaceDeleted')
}
}, [plugin])
const processLoading = (type: string) => {
_paq.push(['trackEvent', 'hometab', 'filesSection', 'importFrom' + type])
const contentImport = plugin.contentImport
@ -57,12 +95,12 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
if ((type === 'ipfs' || type === 'IPFS') && startsWith !== 'ipfs' && startsWith !== 'IPFS') {
setState((prevState) => {
return {...prevState, importSource: startsWith + state.importSource}
return { ...prevState, importSource: startsWith + state.importSource }
})
}
contentImport.import(
state.modalInfo.prefix + state.importSource,
(loadingMsg) => dispatch({tooltip: loadingMsg}),
(loadingMsg) => dispatch({ tooltip: loadingMsg }),
async (error, content, cleanUrl, type, url) => {
if (error) {
toast(error.message || error)
@ -80,22 +118,47 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
}
)
setState((prevState) => {
return {...prevState, showModalDialog: false, importSource: ''}
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const toast = (message: string) => {
setState((prevState) => {
return {...prevState, toasterMsg: message}
return { ...prevState, toasterMsg: message }
})
}
const createNewFile = async () => {
_paq.push(['trackEvent', 'hometab', 'filesSection', 'createNewFile'])
const startCoding = async () => {
_paq.push(['trackEvent', 'hometab', 'filesSection', 'startCoding'])
plugin.verticalIcons.select('filePanel')
await plugin.call('filePanel', 'createNewFile')
}
const wName = 'Playground'
const workspaces = await plugin.call('filePanel', 'getWorkspaces')
let createFile = true
if (!workspaces.find((workspace) => workspace.name === wName)) {
await plugin.call('filePanel', 'createWorkspace', wName, 'playground')
createFile = false
}
await plugin.call('filePanel', 'switchToWorkspace', { name: wName, isLocalHost: false })
await plugin.call('filePanel', 'switchToWorkspace', { name: wName, isLocalHost: false }) // calling once is not working.
const content = `// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12 <0.9.0;
contract HelloWorld {
function print() public pure returns (string memory) {
return "Hello World!";
}
}
`
if (createFile) {
const { newPath } = await plugin.call('fileManager', 'writeFileNoRewrite', '/contracts/helloWorld.sol', content)
await plugin.call('fileManager', 'open', newPath)
} else {
await plugin.call('fileManager', 'open', '/contracts/helloWorld.sol')
}
}
const uploadFile = async (target) => {
_paq.push(['trackEvent', 'hometab', 'filesSection', 'uploadFile'])
await plugin.call('filePanel', 'uploadFile', target)
@ -120,8 +183,8 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
title: title,
loadItem: loadItem,
examples: examples,
prefix
}
prefix,
},
}
})
}
@ -129,10 +192,15 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
const hideFullMessage = () => {
//eslint-disable-line
setState((prevState) => {
return {...prevState, showModalDialog: false, importSource: ''}
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const handleSwichToRecentWorkspace = async (e, workspaceName) => {
e.preventDefault()
await plugin.call('filePanel', 'switchToWorkspace', { name: workspaceName, isLocalhost: false })
}
const examples = state.modalInfo.examples.map((urlEl, key) => (
<div key={key} className="p-1 user-select-auto">
<a>{urlEl}</a>
@ -161,15 +229,15 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
{state.modalInfo.prefix && <span className="text-nowrap align-self-center mr-2">ipfs://</span>}
<input
ref={inputValue}
type="text"
name="prompt_text"
id="inputPrompt_text"
type='text'
name='prompt_text'
id='inputPrompt_text'
className="w-100 mt-1 form-control"
data-id="homeTabModalDialogCustomPromptText"
value={state.importSource}
onInput={(e) => {
setState((prevState) => {
return {...prevState, importSource: inputValue.current.value}
return { ...prevState, importSource: inputValue.current.value }
})
}}
/>
@ -178,40 +246,82 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
</ModalDialog>
<Toaster message={state.toasterMsg} />
<div className="justify-content-start mt-1 p-2 d-flex flex-column" id="hTFileSection">
<label style={{fontSize: '1.2rem'}}>
<label style={{ fontSize: '1.2rem' }}>
<FormattedMessage id="home.files" />
</label>
<div className="dflex">
<button className="btn btn-primary p-2 mr-2 border my-1" data-id="homeTabNewFile" style={{width: 'fit-content'}} onClick={() => createNewFile()}>
<FormattedMessage id="home.newFile" />
</button>
<label className="btn p-2 mr-2 border my-1" style={{width: 'fit-content', cursor: 'pointer'}} htmlFor="openFileInput">
<FormattedMessage id="home.openFile" />
</label>
<input
title="open file"
type="file"
id="openFileInput"
onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}}
multiple
/>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.connectToLocalhost" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}>
<FormattedMessage id="home.accessFileSystem" />
</button>
</CustomTooltip>
<div className="d-flex flex-column">
<div className="d-flex flex-row">
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='home.startCodingPlayground' />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button className="btn btn-primary text-nowrap p-2 mr-2 border my-1" data-id="homeTabStartCoding" style={{ width: 'fit-content' }} onClick={() => startCoding()}>
<FormattedMessage id="home.startCoding" />
</button>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id='home.openFileTooltip' />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<span>
<label className="btn text-nowrap p-2 mr-2 border my-1" style={{ width: 'fit-content', cursor: 'pointer' }} htmlFor="openFileInput">
<FormattedMessage id="home.openFile" />
</label>
<input
title="open file"
type="file"
id="openFileInput"
onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}}
multiple
/>
</span>
</CustomTooltip>
<CustomTooltip
placement={'top'}
tooltipId="overlay-tooltip"
tooltipClasses="text-nowrap"
tooltipText={<FormattedMessage id="home.connectToLocalhost" />}
tooltipTextClasses="border bg-light text-dark p-1 pr-3"
>
<button className="btn text-nowrap p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}>
<FormattedMessage id="home.accessFileSystem" />
</button>
</CustomTooltip>
</div>
{(state.recentWorkspaces[0] || state.recentWorkspaces[1] || state.recentWorkspaces[2]) && (
<div className="d-flex flex-column">
<label style={{ fontSize: '0.8rem' }} className="mt-3">
Recent workspaces
</label>
{state.recentWorkspaces[0] && state.recentWorkspaces[0] !== '' && (
<a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[0])}>
{state.recentWorkspaces[0]}
</a>
)}
{state.recentWorkspaces[1] && state.recentWorkspaces[1] !== '' && (
<a className="cursor-pointer mb-1 ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[1])}>
{state.recentWorkspaces[1]}
</a>
)}
{state.recentWorkspaces[2] && state.recentWorkspaces[2] !== '' && (
<a className="cursor-pointer ml-2" href="#" onClick={(e) => handleSwichToRecentWorkspace(e, state.recentWorkspaces[2])}>
{state.recentWorkspaces[2]}
</a>
)}
</div>
)}
</div>
<label style={{fontSize: '0.8rem'}} className="pt-2">
<label style={{ fontSize: '0.8rem' }} className="pt-3">
<FormattedMessage id="home.loadFrom" />
</label>
<div className="d-flex">
@ -221,7 +331,7 @@ function HomeTabFile({plugin}: HomeTabFileProps) {
onClick={() =>
showFullMessage('GitHub', 'github URL', [
'https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol',
'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'
'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol',
])
}
>

@ -107,18 +107,18 @@ function HomeTabGetStarted({plugin}: HomeTabGetStartedProps) {
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="Gnosis Safe MultiSig"
description={intl.formatMessage({
id: 'home.gnosisSafeMultisigTemplateDesc'
})}
callback={() => createWorkspace('gnosisSafeMultisig')}
description={
intl.formatMessage({ id: 'home.gnosisSafeMultisigTemplateDesc' })
}
callback={() => createWorkspace("gnosisSafeMultisig")}
/>
<WorkspaceTemplate
gsID="sUTLogo"
workspaceTitle="0xProject ERC20"
description={intl.formatMessage({
id: 'home.zeroxErc20TemplateDesc'
})}
callback={() => createWorkspace('zeroxErc20')}
description={
intl.formatMessage({ id: 'home.zeroxErc20TemplateDesc' })
}
callback={() => createWorkspace("zeroxErc20")}
/>
<WorkspaceTemplate
gsID="sourcifyLogo"
@ -132,7 +132,7 @@ function HomeTabGetStarted({plugin}: HomeTabGetStartedProps) {
description={intl.formatMessage({
id: 'home.ozerc721TemplateDesc'
})}
callback={() => createWorkspace('ozerc721')}
callback={() => createWorkspace("ozerc721")}
/>
<WorkspaceTemplate
gsID="sUTLogo"
@ -140,7 +140,7 @@ function HomeTabGetStarted({plugin}: HomeTabGetStartedProps) {
description={intl.formatMessage({
id: 'home.ozerc1155TemplateDesc'
})}
callback={() => createWorkspace('ozerc1155')}
callback={() => createWorkspace("ozerc1155")}
/>
<WorkspaceTemplate
gsID="solhintLogo"
@ -148,7 +148,7 @@ function HomeTabGetStarted({plugin}: HomeTabGetStartedProps) {
description={intl.formatMessage({
id: 'home.remixDefaultTemplateDesc'
})}
callback={() => createWorkspace('remixDefault')}
callback={() => createWorkspace("remixDefault")}
/>
</Carousel>
</ThemeContext.Provider>

@ -265,7 +265,7 @@ function LocalPluginForm({closeModal, visible, pluginManager}: LocalPluginFormPr
checked={location === 'sidePanel'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')}
/>
<label className="form-check-label" htmlFor="sidePanel">
<label className="form-check-label" htmlFor="localPluginRadioButtonsidePanelSidePanel">
<FormattedMessage id="pluginManager.localForm.sidePanel" />
</label>
</div>
@ -280,7 +280,7 @@ function LocalPluginForm({closeModal, visible, pluginManager}: LocalPluginFormPr
checked={location === 'mainPanel'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')}
/>
<label className="form-check-label" htmlFor="mainPanel">
<label className="form-check-label" htmlFor="localPluginRadioButtonsidePanelMainPanel">
<FormattedMessage id="pluginManager.localForm.mainPanel" />
</label>
</div>
@ -295,7 +295,7 @@ function LocalPluginForm({closeModal, visible, pluginManager}: LocalPluginFormPr
checked={location === 'none'}
onChange={(e) => setLocation(e.target.value as 'sidePanel' | 'mainPanel' | 'none')}
/>
<label className="form-check-label" htmlFor="none">
<label className="form-check-label" htmlFor="localPluginRadioButtonsidePanelNone">
<FormattedMessage id="pluginManager.localForm.none" />
</label>
</div>

@ -124,7 +124,7 @@ export const continueHandler = (dispatch: React.Dispatch<any>, gasEstimationProm
dispatch(displayNotification('Gas estimation failed', gasEstimationPrompt(msg), 'Send Transaction', 'Cancel Transaction', () => {
continueTxExecution()
}, () => {
cancelCb()
cancelCb(error)
}))
} else {
continueTxExecution()

@ -179,6 +179,9 @@ export const TabsUI = (props: TabsUIProps) => {
} else if (tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul') {
await props.plugin.call('solidity', 'compile', path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'circom') {
await props.plugin.call('circuit-compiler', 'compile', path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
}
}}
>
@ -189,7 +192,7 @@ export const TabsUI = (props: TabsUIProps) => {
<span>
{tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' ? (
<FormattedMessage id="remixUiTabs.tooltipText1" />
) : tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul' ? (
) : tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul' || tabsState.currentExt === 'circom' ? (
<FormattedMessage id="remixUiTabs.tooltipText2" />
) : (
<FormattedMessage id="remixUiTabs.tooltipText3" />

@ -1,7 +1,7 @@
import { fileDecoration } from '@remix-ui/file-decorators'
import { extractParentFromKey } from '@remix-ui/helper'
import React from 'react'
import { action, WorkspaceTemplate } from '../types'
import { action, FileTree, WorkspaceTemplate } from '../types'
import { ROOT_PATH } from '../utils/constants'
import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode, setFileDecorationSuccess } from './payload'
import { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace'
@ -40,6 +40,10 @@ export const listenOnPluginEvents = (filePanelPlugin) => {
uploadFile(target, dir, cb)
})
plugin.on('filePanel', 'switchToWorkspace', async (workspace) => {
await switchToWorkspace(workspace.name)
})
plugin.on('fileDecorator', 'fileDecoratorsChanged', async (items: fileDecoration[]) => {
setFileDecorators(items)
})
@ -173,8 +177,8 @@ const folderAdded = async (folderPath: string) => {
const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(folderPath) || ROOT_PATH
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
@ -196,8 +200,8 @@ const fileRemoved = async (removePath: string) => {
const fileRenamed = async (oldPath: string) => {
const provider = plugin.fileManager.currentFileProvider()
const path = extractParentFromKey(oldPath) || ROOT_PATH
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)

@ -9,6 +9,7 @@ import { QueryParams } from '@remix-project/remix-lib'
import { fetchContractFromEtherscan } from '@remix-project/core-plugin' // eslint-disable-line
import JSZip from 'jszip'
import isElectron from 'is-electron'
import { Actions, FileTree } from '../types'
export * from './events'
export * from './workspace'
@ -16,7 +17,7 @@ export * from './workspace'
const queryParams = new QueryParams()
const _paq = window._paq = window._paq || []
let plugin, dispatch: React.Dispatch<any>
let plugin, dispatch: React.Dispatch<Actions>
export type UrlParametersType = {
gist: string,
@ -43,7 +44,7 @@ const basicWorkspaceInit = async (workspaces: { name: string; isGitRepo: boolean
}
}
export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<any>) => {
export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.Dispatch<Actions>) => {
if (filePanelPlugin) {
plugin = filePanelPlugin
dispatch = reducerDispatch
@ -144,18 +145,18 @@ export const initWorkspace = (filePanelPlugin) => async (reducerDispatch: React.
export const fetchDirectory = async (path: string) => {
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
dispatch(fetchDirectoryRequest(promise))
promise.then((fileTree) => {
dispatch(fetchDirectoryRequest())
promise.then((fileTree: FileTree) => {
dispatch(fetchDirectorySuccess(path, fileTree))
}).catch((error) => {
dispatch(fetchDirectoryError({ error }))
}).catch((error: ErrorEvent) => {
dispatch(fetchDirectoryError(error.message))
})
return promise
}

@ -1,292 +1,300 @@
import { fileDecoration } from '@remix-ui/file-decorators'
import { action } from '../types'
import { Action, ActionPayloadTypes, FileTree, WorkspaceElement, action } from '../types'
export const setCurrentWorkspace = (workspace: { name: string; isGitRepo: boolean; }) => {
export const setCurrentWorkspace = (workspace: { name: string; isGitRepo: boolean; }): Action<'SET_CURRENT_WORKSPACE'> => {
return {
type: 'SET_CURRENT_WORKSPACE',
payload: workspace
}
}
export const setWorkspaces = (workspaces: { name: string; isGitRepo: boolean; }[]) => {
export const setWorkspaces = (workspaces: { name: string; isGitRepo: boolean; }[]): Action<'SET_WORKSPACES'> => {
return {
type: 'SET_WORKSPACES',
payload: workspaces
}
}
export const setMode = (mode: 'browser' | 'localhost') => {
export const setMode = (mode: 'browser' | 'localhost'): Action<'SET_MODE'> => {
return {
type: 'SET_MODE',
payload: mode
}
}
export const fetchDirectoryError = (error: any) => {
export const fetchDirectoryError = (error: string): Action<'FETCH_DIRECTORY_ERROR'> => {
return {
type: 'FETCH_DIRECTORY_ERROR',
payload: error
}
}
export const fetchDirectoryRequest = (promise: Promise<any>) => {
export const fetchDirectoryRequest = (): Action<'FETCH_DIRECTORY_REQUEST'> => {
return {
type: 'FETCH_DIRECTORY_REQUEST',
payload: promise
payload: undefined
}
}
export const fetchDirectorySuccess = (path: string, fileTree) => {
export const fetchDirectorySuccess = (path: string, fileTree: FileTree): Action<'FETCH_DIRECTORY_SUCCESS'> => {
return {
type: 'FETCH_DIRECTORY_SUCCESS',
payload: { path, fileTree }
}
}
export const displayNotification = (title: string, message: string, labelOk: string, labelCancel: string, actionOk?: (...args) => void, actionCancel?: (...args) => void) => {
export const displayNotification = (title: string, message: string, labelOk: string, labelCancel: string, actionOk?: (...args) => void, actionCancel?: (...args) => void): Action<'DISPLAY_NOTIFICATION'> => {
return {
type: 'DISPLAY_NOTIFICATION',
payload: { title, message, labelOk, labelCancel, actionOk, actionCancel }
}
}
export const hideNotification = () => {
export const hideNotification = (): Action<'HIDE_NOTIFICATION'> => {
return {
type: 'HIDE_NOTIFICATION'
type: 'HIDE_NOTIFICATION',
payload: null
}
}
export const fileAddedSuccess = (filePath: string) => {
export const fileAddedSuccess = (filePath: string): Action<'FILE_ADDED_SUCCESS'> => {
return {
type: 'FILE_ADDED_SUCCESS',
payload: filePath
}
}
export const folderAddedSuccess = (path: string, folderPath: string, fileTree) => {
export const folderAddedSuccess = (path: string, folderPath: string, fileTree: FileTree): Action<'FOLDER_ADDED_SUCCESS'> => {
return {
type: 'FOLDER_ADDED_SUCCESS',
payload: { path, folderPath, fileTree }
}
}
export const fileRemovedSuccess = (removePath: string) => {
export const fileRemovedSuccess = (removePath: string): Action<'FILE_REMOVED_SUCCESS'> => {
return {
type: 'FILE_REMOVED_SUCCESS',
payload: removePath
}
}
export const fileRenamedSuccess = (path: string, oldPath: string, fileTree) => {
export const fileRenamedSuccess = (path: string, oldPath: string, fileTree: FileTree): Action<'FILE_RENAMED_SUCCESS'> => {
return {
type: 'FILE_RENAMED_SUCCESS',
payload: { path, oldPath, fileTree }
}
}
export const rootFolderChangedSuccess = (path: string) => {
export const rootFolderChangedSuccess = (path: string): Action<'ROOT_FOLDER_CHANGED'> => {
return {
type: 'ROOT_FOLDER_CHANGED',
payload: path
}
}
export const addInputFieldSuccess = (path: string, fileTree, type: 'file' | 'folder' | 'gist') => {
export const addInputFieldSuccess = (path: string, fileTree: FileTree, type: 'file' | 'folder'): Action<'ADD_INPUT_FIELD'> => {
return {
type: 'ADD_INPUT_FIELD',
payload: { path, fileTree, type }
}
}
export const removeInputFieldSuccess = (path: string) => {
export const removeInputFieldSuccess = (path: string): Action<'REMOVE_INPUT_FIELD'> => {
return {
type: 'REMOVE_INPUT_FIELD',
payload: { path }
}
}
export const setReadOnlyMode = (mode: boolean) => {
export const setReadOnlyMode = (mode: boolean): Action<'SET_READ_ONLY_MODE'> => {
return {
type: 'SET_READ_ONLY_MODE',
payload: mode
}
}
export const createWorkspaceError = (error: any) => {
export const createWorkspaceError = (error: string): Action<'CREATE_WORKSPACE_ERROR'> => {
return {
type: 'CREATE_WORKSPACE_ERROR',
payload: error
}
}
export const createWorkspaceRequest = (promise: Promise<any>) => {
export const createWorkspaceRequest = (): Action<'CREATE_WORKSPACE_REQUEST'> => {
return {
type: 'CREATE_WORKSPACE_REQUEST',
payload: promise
payload: null
}
}
export const createWorkspaceSuccess = (workspaceName: { name: string; isGitRepo: boolean; branches?: { remote: any; name: string; }[], currentBranch?: string }) => {
export const createWorkspaceSuccess = (workspaceName: ActionPayloadTypes['CREATE_WORKSPACE_SUCCESS']): Action<'CREATE_WORKSPACE_SUCCESS'> => {
return {
type: 'CREATE_WORKSPACE_SUCCESS',
payload: workspaceName
}
}
export const fetchWorkspaceDirectoryError = (error: any) => {
export const fetchWorkspaceDirectoryError = (error: string): Action<'FETCH_WORKSPACE_DIRECTORY_ERROR'> => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_ERROR',
payload: error
}
}
export const fetchWorkspaceDirectoryRequest = (promise: Promise<any>) => {
export const fetchWorkspaceDirectoryRequest = (): Action<'FETCH_WORKSPACE_DIRECTORY_REQUEST'> => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_REQUEST',
payload: promise
payload: null
}
}
export const fetchWorkspaceDirectorySuccess = (path: string, fileTree) => {
export const fetchWorkspaceDirectorySuccess = (path: string, fileTree: FileTree): Action<'FETCH_WORKSPACE_DIRECTORY_SUCCESS'> => {
return {
type: 'FETCH_WORKSPACE_DIRECTORY_SUCCESS',
payload: { path, fileTree }
}
}
export const setRenameWorkspace = (oldName: string, workspaceName: string) => {
export const setRenameWorkspace = (oldName: string, workspaceName: string): Action<'RENAME_WORKSPACE'> => {
return {
type: 'RENAME_WORKSPACE',
payload: { oldName, workspaceName }
}
}
export const setDeleteWorkspace = (workspaceName: string) => {
export const setDeleteWorkspace = (workspaceName: string): Action<'DELETE_WORKSPACE'> => {
return {
type: 'DELETE_WORKSPACE',
payload: workspaceName
}
}
export const displayPopUp = (message: string) => {
export const displayPopUp = (message: string): Action<'DISPLAY_POPUP_MESSAGE'> => {
return {
type: 'DISPLAY_POPUP_MESSAGE',
payload: message
}
}
export const hidePopUp = () => {
export const hidePopUp = (): Action<'HIDE_POPUP_MESSAGE'> => {
return {
type: 'HIDE_POPUP_MESSAGE'
type: 'HIDE_POPUP_MESSAGE',
payload: null
}
}
export const focusElement = (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => {
export const focusElement = (elements: { key: string, type: WorkspaceElement }[]): Action<'SET_FOCUS_ELEMENT'> => {
return {
type: 'SET_FOCUS_ELEMENT',
payload: elements
}
}
export const removeFocus = (name: string) => {
export const removeFocus = (name: string): Action<'REMOVE_FOCUS_ELEMENT'> => {
return {
type: 'REMOVE_FOCUS_ELEMENT',
payload: name
}
}
export const setContextMenuItem = (item: action) => {
export const setContextMenuItem = (item: action): Action<'SET_CONTEXT_MENU_ITEM'> => {
return {
type: 'SET_CONTEXT_MENU_ITEM',
payload: item
}
}
export const removeContextMenuItem = (plugin) => {
export const removeContextMenuItem = (plugin: { name: string }): Action<'REMOVE_CONTEXT_MENU_ITEM'> => {
return {
type: 'REMOVE_CONTEXT_MENU_ITEM',
payload: plugin
}
}
export const setExpandPath = (paths: string[]) => {
export const setExpandPath = (paths: string[]): Action<'SET_EXPAND_PATH'> => {
return {
type: 'SET_EXPAND_PATH',
payload: paths
}
}
export const loadLocalhostError = (error: any) => {
export const loadLocalhostError = (error: string): Action<'LOAD_LOCALHOST_ERROR'> => {
return {
type: 'LOAD_LOCALHOST_ERROR',
payload: error
}
}
export const loadLocalhostRequest = () => {
export const loadLocalhostRequest = (): Action<'LOAD_LOCALHOST_REQUEST'> => {
return {
type: 'LOAD_LOCALHOST_REQUEST'
type: 'LOAD_LOCALHOST_REQUEST',
payload: null
}
}
export const loadLocalhostSuccess = () => {
export const loadLocalhostSuccess = (): Action<'LOAD_LOCALHOST_SUCCESS'> => {
return {
type: 'LOAD_LOCALHOST_SUCCESS'
type: 'LOAD_LOCALHOST_SUCCESS',
payload: null
}
}
export const fsInitializationCompleted = () => {
export const fsInitializationCompleted = (): Action<'FS_INITIALIZATION_COMPLETED'> => {
return {
type: 'FS_INITIALIZATION_COMPLETED'
type: 'FS_INITIALIZATION_COMPLETED',
payload: null
}
}
export const setFileDecorationSuccess = (items: fileDecoration[]) => {
export const setFileDecorationSuccess = (items: fileDecoration[]): Action<'SET_FILE_DECORATION_SUCCESS'> => {
return {
type: 'SET_FILE_DECORATION_SUCCESS',
payload: items
}
}
export const cloneRepositoryRequest = () => {
export const cloneRepositoryRequest = (): Action<'CLONE_REPOSITORY_REQUEST'> => {
return {
type: 'CLONE_REPOSITORY_REQUEST'
type: 'CLONE_REPOSITORY_REQUEST',
payload: null
}
}
export const cloneRepositorySuccess = () => {
export const cloneRepositorySuccess = (): Action<'CLONE_REPOSITORY_SUCCESS'> => {
return {
type: 'CLONE_REPOSITORY_SUCCESS'
type: 'CLONE_REPOSITORY_SUCCESS',
payload: null
}
}
export const cloneRepositoryFailed = () => {
export const cloneRepositoryFailed = (): Action<'CLONE_REPOSITORY_FAILED'> => {
return {
type: 'CLONE_REPOSITORY_FAILED'
type: 'CLONE_REPOSITORY_FAILED',
payload: null
}
}
export const setCurrentWorkspaceBranches = (branches?: { remote: any, name: string }[]) => {
export const setCurrentWorkspaceBranches = (branches?: { remote: any, name: string }[]): Action<'SET_CURRENT_WORKSPACE_BRANCHES'> => {
return {
type: 'SET_CURRENT_WORKSPACE_BRANCHES',
payload: branches
}
}
export const setCurrentWorkspaceCurrentBranch = (currentBranch?: string) => {
export const setCurrentWorkspaceCurrentBranch = (currentBranch?: string): Action<'SET_CURRENT_WORKSPACE_CURRENT_BRANCH'> => {
return {
type: 'SET_CURRENT_WORKSPACE_CURRENT_BRANCH',
payload: currentBranch
}
}
export const setCurrentWorkspaceIsGitRepo = (isRepo: boolean) => {
export const setCurrentWorkspaceIsGitRepo = (isRepo: boolean): Action<'SET_CURRENT_WORKSPACE_IS_GITREPO'> => {
return {
type: 'SET_CURRENT_WORKSPACE_IS_GITREPO',
payload: isRepo
}
}
export const setGitConfig = (config: {username: string, token: string, email: string}) => {
export const setGitConfig = (config: {username: string, token: string, email: string}): Action<'SET_GIT_CONFIG'> => {
return {
type: 'SET_GIT_CONFIG',
payload: config

@ -2,10 +2,33 @@ import React from 'react'
import { bufferToHex } from '@ethereumjs/util'
import { hash } from '@remix-project/remix-lib'
import axios, { AxiosResponse } from 'axios'
import { addInputFieldSuccess, cloneRepositoryFailed, cloneRepositoryRequest, cloneRepositorySuccess, createWorkspaceError, createWorkspaceRequest, createWorkspaceSuccess, displayNotification, displayPopUp, fetchWorkspaceDirectoryError, fetchWorkspaceDirectoryRequest, fetchWorkspaceDirectorySuccess, hideNotification, setCurrentWorkspace, setCurrentWorkspaceBranches, setCurrentWorkspaceCurrentBranch, setDeleteWorkspace, setMode, setReadOnlyMode, setRenameWorkspace, setCurrentWorkspaceIsGitRepo, setGitConfig } from './payload'
import {
addInputFieldSuccess,
cloneRepositoryFailed,
cloneRepositoryRequest,
cloneRepositorySuccess,
createWorkspaceError,
createWorkspaceRequest,
createWorkspaceSuccess,
displayNotification,
displayPopUp,
fetchWorkspaceDirectoryError,
fetchWorkspaceDirectoryRequest,
fetchWorkspaceDirectorySuccess,
hideNotification,
setCurrentWorkspace,
setCurrentWorkspaceBranches,
setCurrentWorkspaceCurrentBranch,
setDeleteWorkspace,
setMode,
setReadOnlyMode,
setRenameWorkspace,
setCurrentWorkspaceIsGitRepo,
setGitConfig,
} from './payload'
import { addSlash, checkSlash, checkSpecialChars } from '@remix-ui/helper'
import { JSONStandardInput, WorkspaceTemplate } from '../types'
import { FileTree, JSONStandardInput, WorkspaceTemplate } from '../types'
import { QueryParams } from '@remix-project/remix-lib'
import * as templateWithContent from '@remix-project/remix-ws-templates'
import { ROOT_PATH, slitherYml, solTestYml, tsSolTestYml } from '../utils/constants'
@ -16,14 +39,15 @@ import { AppModal, ModalTypes } from '@remix-ui/app'
import { contractDeployerScripts, etherscanScripts } from '@remix-project/remix-ws-templates'
declare global {
interface Window { remixFileSystemCallback: IndexedDBStorage; }
interface Window {
remixFileSystemCallback: IndexedDBStorage
}
}
const LOCALHOST = ' - connect to localhost - '
const NO_WORKSPACE = ' - none - '
const queryParams = new QueryParams()
const _paq = window._paq = window._paq || [] //eslint-disable-line
const _paq = (window._paq = window._paq || []) //eslint-disable-line
let plugin, dispatch: React.Dispatch<any>
export const setPlugin = (filePanelPlugin, reducerDispatch) => {
@ -58,8 +82,8 @@ export const setPlugin = (filePanelPlugin, reducerDispatch) => {
export const addInputField = async (type: 'file' | 'folder', path: string, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => {
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve, reject) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve, reject) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) {
cb && cb(error)
return reject(error)
@ -70,22 +94,32 @@ export const addInputField = async (type: 'file' | 'folder', path: string, cb?:
})
})
promise.then((files) => {
dispatch(addInputFieldSuccess(path, files, type))
}).catch((error) => {
console.error(error)
})
promise
.then((files) => {
dispatch(addInputFieldSuccess(path, files, type))
})
.catch((error) => {
console.error(error)
})
return promise
}
const removeSlash = (s: string) => {
return s.replace(/^\/+/, "")
}
export const createWorkspace = async (workspaceName: string, workspaceTemplateName: WorkspaceTemplate, opts = null, isEmpty = false, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void, isGitRepo: boolean = false, createCommit: boolean = true) => {
return s.replace(/^\/+/, '')
}
export const createWorkspace = async (
workspaceName: string,
workspaceTemplateName: WorkspaceTemplate,
opts = null,
isEmpty = false,
cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void,
isGitRepo: boolean = false,
createCommit: boolean = true
) => {
await plugin.fileManager.closeAllFiles()
const promise = createWorkspaceTemplate(workspaceName, workspaceTemplateName)
dispatch(createWorkspaceRequest(promise))
dispatch(createWorkspaceRequest())
promise.then(async () => {
dispatch(createWorkspaceSuccess({ name: workspaceName, isGitRepo }))
await plugin.setWorkspace({ name: workspaceName, isLocalhost: false })
@ -121,7 +155,7 @@ export const createWorkspace = async (workspaceName: string, workspaceTemplateNa
await plugin.call('dGitProvider', 'commit', {
author: {
name,
email
email,
},
message: `Initial commit: remix template ${workspaceTemplateName}`,
})
@ -136,10 +170,14 @@ export const createWorkspace = async (workspaceName: string, workspaceTemplateNa
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
}
if (workspaceTemplateName === 'semaphore') {
const isCircomActive = await plugin.call('manager', 'isActive', 'circuit-compiler')
if (!isCircomActive) await plugin.call('manager', 'activatePlugin', 'circuit-compiler')
}
// this call needs to be here after the callback because it calls dGitProvider which also calls this function and that would cause an infinite loop
await plugin.setWorkspaces(await getWorkspaces())
}).catch((error) => {
dispatch(createWorkspaceError({ error }))
dispatch(createWorkspaceError(error.message))
cb && cb(error)
})
return promise
@ -148,18 +186,17 @@ export const createWorkspace = async (workspaceName: string, workspaceTemplateNa
export const createWorkspaceTemplate = async (workspaceName: string, template: WorkspaceTemplate = 'remixDefault') => {
if (!workspaceName) throw new Error('workspace name cannot be empty')
if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed')
if (await workspaceExists(workspaceName) && template === 'remixDefault') throw new Error('workspace already exists')
if ((await workspaceExists(workspaceName)) && template === 'remixDefault') throw new Error('workspace already exists')
else {
const workspaceProvider = plugin.fileProviders.workspace
await workspaceProvider.createWorkspace(workspaceName)
}
}
export type UrlParametersType = {
gist: string,
code: string,
url: string,
gist: string
code: string
url: string
language: string
}
@ -171,7 +208,8 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
case 'code-template':
// creates a new workspace code-sample and loads code from url params.
try {
let path = ''; let content
let path = ''
let content
if (params.code) {
const hashed = bufferToHex(hash.keccakFromString(params.code))
@ -188,7 +226,7 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
try {
content = JSON.parse(content) as any
if (content.language && content.language === "Solidity" && content.sources) {
if (content.language && content.language === 'Solidity' && content.sources) {
const standardInput: JSONStandardInput = content as JSONStandardInput
for (const [fname, source] of Object.entries(standardInput.sources)) {
await workspaceProvider.set(fname, source.content)
@ -216,7 +254,18 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
const data = response.data as { files: any }
if (!data.files) {
return dispatch(displayNotification('Gist load error', 'No files found', 'OK', null, () => { dispatch(hideNotification()) }, null))
return dispatch(
displayNotification(
'Gist load error',
'No files found',
'OK',
null,
() => {
dispatch(hideNotification())
},
null
)
)
}
const obj = {}
@ -227,11 +276,22 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
})
plugin.fileManager.setBatchFiles(obj, 'workspace', true, (errorLoadingFile) => {
if (errorLoadingFile) {
dispatch(displayNotification('', errorLoadingFile.message || errorLoadingFile, 'OK', null, () => { }, null))
dispatch(displayNotification('', errorLoadingFile.message || errorLoadingFile, 'OK', null, () => {}, null))
}
})
} catch (e) {
dispatch(displayNotification('Gist load error', e.message, 'OK', null, () => { dispatch(hideNotification()) }, null))
dispatch(
displayNotification(
'Gist load error',
e.message,
'OK',
null,
() => {
dispatch(hideNotification())
},
null
)
)
console.error(e)
}
break
@ -251,7 +311,18 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
}
}
} catch (e) {
dispatch(displayNotification('Workspace load error', e.message, 'OK', null, () => { dispatch(hideNotification()) }, null))
dispatch(
displayNotification(
'Workspace load error',
e.message,
'OK',
null,
() => {
dispatch(hideNotification())
},
null
)
)
console.error(e)
}
break
@ -269,20 +340,21 @@ export const workspaceExists = async (name: string) => {
export const fetchWorkspaceDirectory = async (path: string) => {
if (!path) return
const provider = plugin.fileManager.currentFileProvider()
const promise = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree) => {
const promise: Promise<FileTree> = new Promise((resolve) => {
provider.resolveDirectory(path, (error, fileTree: FileTree) => {
if (error) console.error(error)
resolve(fileTree)
})
})
dispatch(fetchWorkspaceDirectoryRequest(promise))
promise.then((fileTree) => {
dispatch(fetchWorkspaceDirectorySuccess(path, fileTree))
}).catch((error) => {
dispatch(fetchWorkspaceDirectoryError({ error }))
})
dispatch(fetchWorkspaceDirectoryRequest())
promise
.then((fileTree) => {
dispatch(fetchWorkspaceDirectorySuccess(path, fileTree))
})
.catch((error) => {
dispatch(fetchWorkspaceDirectoryError(error.message))
})
return promise
}
@ -290,6 +362,7 @@ export const renameWorkspace = async (oldName: string, workspaceName: string, cb
await renameWorkspaceFromProvider(oldName, workspaceName)
await dispatch(setRenameWorkspace(oldName, workspaceName))
await plugin.setWorkspace({ name: workspaceName, isLocalhost: false })
await plugin.deleteWorkspace(oldName)
await plugin.workspaceRenamed(oldName, workspaceName)
cb && cb(null, workspaceName)
}
@ -314,7 +387,9 @@ export const deleteWorkspace = async (workspaceName: string, cb?: (err: Error, r
}
export const deleteAllWorkspaces = async () => {
await (await getWorkspaces()).map(async workspace => {
await (
await getWorkspaces()
).map(async (workspace) => {
await deleteWorkspaceFromProvider(workspace.name)
await dispatch(setDeleteWorkspace(workspace.name))
plugin.workspaceDeleted(workspace.name)
@ -351,7 +426,6 @@ export const switchToWorkspace = async (name: string) => {
if (isGitRepo) {
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
}
dispatch(setMode('browser'))
@ -365,18 +439,18 @@ const loadFile = (name, file, provider, cb?): void => {
fileReader.onload = async function (event) {
if (checkSpecialChars(file.name)) {
return dispatch(displayNotification('File Upload Failed', 'Special characters are not allowed', 'Close', null, async () => { }))
return dispatch(displayNotification('File Upload Failed', 'Special characters are not allowed', 'Close', null, async () => {}))
}
try {
await provider.set(name, event.target.result)
} catch (error) {
return dispatch(displayNotification('File Upload Failed', 'Failed to create file ' + name, 'Close', null, async () => { }))
return dispatch(displayNotification('File Upload Failed', 'Failed to create file ' + name, 'Close', null, async () => {}))
}
const config = plugin.registry.get('config').api
const editor = plugin.registry.get('editor').api
if ((config.get('currentFile') === name) && (editor.currentContent() !== event.target.result)) {
if (config.get('currentFile') === name && editor.currentContent() !== event.target.result) {
editor.setText(name, event.target.result)
}
}
@ -389,11 +463,11 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error,
// the files module. Please ask the user here if they want to overwrite
// a file and then just use `files.add`. The file explorer will
// pick that up via the 'fileAdded' event from the files module.
[...target.files].forEach(async (file) => {
;[...target.files].forEach(async (file) => {
const workspaceProvider = plugin.fileProviders.workspace
const name = targetFolder === '/' ? file.name : `${targetFolder}/${file.name}`
if (!await workspaceProvider.exists(name)) {
if (!(await workspaceProvider.exists(name))) {
loadFile(name, file, workspaceProvider, cb)
} else {
const modalContent: AppModal = {
@ -407,7 +481,7 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error,
loadFile(name, file, workspaceProvider, cb)
},
cancelFn: () => {},
hideFn: () => {}
hideFn: () => {},
}
plugin.call('notification', 'modal', modalContent)
}
@ -415,10 +489,10 @@ export const uploadFile = async (target, targetFolder: string, cb?: (err: Error,
}
export const uploadFolder = async (target, targetFolder: string, cb?: (err: Error, result?: string | number | boolean | Record<string, any>) => void) => {
for(const file of [...target.files]) {
for (const file of [...target.files]) {
const workspaceProvider = plugin.fileProviders.workspace
const name = targetFolder === '/' ? file.webkitRelativePath : `${targetFolder}/${file.webkitRelativePath}`
if (!await workspaceProvider.exists(name)) {
if (!(await workspaceProvider.exists(name))) {
loadFile(name, file, workspaceProvider, cb)
} else {
const modalContent: AppModal = {
@ -432,51 +506,53 @@ export const uploadFolder = async (target, targetFolder: string, cb?: (err: Erro
loadFile(name, file, workspaceProvider, cb)
},
cancelFn: () => {},
hideFn: () => {}
hideFn: () => {},
}
plugin.call('notification', 'modal', modalContent)
}
}
}
export const getWorkspaces = async (): Promise<{ name: string, isGitRepo: boolean, branches?: { remote: any; name: string; }[], currentBranch?: string }[]> | undefined => {
export const getWorkspaces = async (): Promise<{ name: string; isGitRepo: boolean; branches?: { remote: any; name: string }[]; currentBranch?: string }[]> | undefined => {
try {
const workspaces: { name: string, isGitRepo: boolean, branches?: { remote: any; name: string; }[], currentBranch?: string }[] = await new Promise((resolve, reject) => {
const workspaces: { name: string; isGitRepo: boolean; branches?: { remote: any; name: string }[]; currentBranch?: string }[] = await new Promise((resolve, reject) => {
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
plugin.fileProviders.browser.resolveDirectory('/' + workspacesPath, (error, items) => {
if (error) {
return reject(error)
}
Promise.all(Object.keys(items)
.filter((item) => items[item].isDirectory)
.map(async (folder) => {
const isGitRepo: boolean = await plugin.fileProviders.browser.exists('/' + folder + '/.git')
if (isGitRepo) {
let branches = []
let currentBranch = null
branches = await getGitRepoBranches(folder)
currentBranch = await getGitRepoCurrentBranch(folder)
return {
name: folder.replace(workspacesPath + '/', ''),
isGitRepo,
branches,
currentBranch
}
} else {
return {
name: folder.replace(workspacesPath + '/', ''),
isGitRepo
Promise.all(
Object.keys(items)
.filter((item) => items[item].isDirectory)
.map(async (folder) => {
const isGitRepo: boolean = await plugin.fileProviders.browser.exists('/' + folder + '/.git')
if (isGitRepo) {
let branches = []
let currentBranch = null
branches = await getGitRepoBranches(folder)
currentBranch = await getGitRepoCurrentBranch(folder)
return {
name: folder.replace(workspacesPath + '/', ''),
isGitRepo,
branches,
currentBranch,
}
} else {
return {
name: folder.replace(workspacesPath + '/', ''),
isGitRepo,
}
}
}
})).then(workspacesList => resolve(workspacesList))
})
).then((workspacesList) => resolve(workspacesList))
})
})
await plugin.setWorkspaces(workspaces)
return workspaces
} catch (e) { }
} catch (e) {}
}
export const cloneRepository = async (url: string) => {
@ -491,37 +567,40 @@ export const cloneRepository = async (url: string) => {
const promise = plugin.call('dGitProvider', 'clone', repoConfig, repoName, true)
dispatch(cloneRepositoryRequest())
promise.then(async () => {
const isActive = await plugin.call('manager', 'isActive', 'dgit')
promise
.then(async () => {
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
await fetchWorkspaceDirectory(ROOT_PATH)
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const branches = await getGitRepoBranches(workspacesPath + '/' + repoName)
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
await fetchWorkspaceDirectory(ROOT_PATH)
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const branches = await getGitRepoBranches(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceBranches(branches))
const currentBranch = await getGitRepoCurrentBranch(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceBranches(branches))
const currentBranch = await getGitRepoCurrentBranch(workspacesPath + '/' + repoName)
dispatch(setCurrentWorkspaceCurrentBranch(currentBranch))
dispatch(cloneRepositorySuccess())
}).catch(() => {
const cloneModal = {
id: 'cloneGitRepository',
title: 'Clone Git Repository',
message: 'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin',
modalType: 'modal',
okLabel: 'OK',
okFn: async () => {
await deleteWorkspace(repoName)
dispatch(cloneRepositoryFailed())
},
hideFn: async () => {
await deleteWorkspace(repoName)
dispatch(cloneRepositoryFailed())
dispatch(setCurrentWorkspaceCurrentBranch(currentBranch))
dispatch(cloneRepositorySuccess())
})
.catch(() => {
const cloneModal = {
id: 'cloneGitRepository',
title: 'Clone Git Repository',
message:
'An error occurred: Please check that you have the correct URL for the repo. If the repo is private, you need to add your github credentials (with the valid token permissions) in Settings plugin',
modalType: 'modal',
okLabel: 'OK',
okFn: async () => {
await deleteWorkspace(repoName)
dispatch(cloneRepositoryFailed())
},
hideFn: async () => {
await deleteWorkspace(repoName)
dispatch(cloneRepositoryFailed())
},
}
}
plugin.call('notification', 'modal', cloneModal)
})
plugin.call('notification', 'modal', cloneModal)
})
} catch (e) {
dispatch(displayPopUp('An error occured: ' + e))
}
@ -535,7 +614,6 @@ export const checkGit = async () => {
dispatch(setCurrentWorkspaceCurrentBranch(currentBranch))
}
export const getRepositoryTitle = async (url: string) => {
const urlArray = url.split('/')
let name = urlArray.length > 0 ? urlArray[urlArray.length - 1] : ''
@ -556,25 +634,24 @@ export const getRepositoryTitle = async (url: string) => {
}
export const getGitRepoBranches = async (workspacePath: string) => {
const gitConfig: { fs: IndexedDBStorage, dir: string } = {
const gitConfig: { fs: IndexedDBStorage; dir: string } = {
fs: window.remixFileSystemCallback,
dir: addSlash(workspacePath)
dir: addSlash(workspacePath),
}
const branches: { remote: any; name: string; }[] = await plugin.call('dGitProvider', 'branches', { ...gitConfig })
const branches: { remote: any; name: string }[] = await plugin.call('dGitProvider', 'branches', { ...gitConfig })
return branches
}
export const getGitRepoCurrentBranch = async (workspaceName: string) => {
const gitConfig: { fs: IndexedDBStorage, dir: string } = {
const gitConfig: { fs: IndexedDBStorage; dir: string } = {
fs: window.remixFileSystemCallback,
dir: addSlash(workspaceName)
dir: addSlash(workspaceName),
}
const currentBranch: string = await plugin.call('dGitProvider', 'currentbranch', { ...gitConfig })
return currentBranch
}
export const showAllBranches = async () => {
console.log('showAllBranches')
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
plugin.call('menuicons', 'select', 'dgit')
@ -613,28 +690,34 @@ export const switchBranch = async (branch: string) => {
okLabel: 'Force Checkout',
okFn: async () => {
dispatch(cloneRepositoryRequest())
plugin.call('dGitProvider', 'checkout', { ref: branch, force: true }, false).then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
dispatch(cloneRepositorySuccess())
}).catch(() => {
dispatch(cloneRepositoryFailed())
})
plugin
.call('dGitProvider', 'checkout', { ref: branch, force: true }, false)
.then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
dispatch(cloneRepositorySuccess())
})
.catch(() => {
dispatch(cloneRepositoryFailed())
})
},
cancelLabel: 'Cancel',
cancelFn: () => { },
hideFn: () => { }
cancelFn: () => {},
hideFn: () => {},
}
plugin.call('notification', 'modal', cloneModal)
} else {
dispatch(cloneRepositoryRequest())
plugin.call('dGitProvider', 'checkout', { ref: branch, force: true }, false).then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
dispatch(cloneRepositorySuccess())
}).catch(() => {
dispatch(cloneRepositoryFailed())
})
plugin
.call('dGitProvider', 'checkout', { ref: branch, force: true }, false)
.then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
dispatch(cloneRepositorySuccess())
})
.catch(() => {
dispatch(cloneRepositoryFailed())
})
}
}
@ -642,48 +725,49 @@ export const createNewBranch = async (branch: string) => {
const promise = plugin.call('dGitProvider', 'branch', { ref: branch, checkout: true }, false)
dispatch(cloneRepositoryRequest())
promise.then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const workspaceName = plugin.fileProviders.workspace.workspace
const branches = await getGitRepoBranches(workspacesPath + '/' + workspaceName)
dispatch(setCurrentWorkspaceBranches(branches))
dispatch(cloneRepositorySuccess())
}).catch(() => {
dispatch(cloneRepositoryFailed())
})
promise
.then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const workspaceName = plugin.fileProviders.workspace.workspace
const branches = await getGitRepoBranches(workspacesPath + '/' + workspaceName)
dispatch(setCurrentWorkspaceBranches(branches))
dispatch(cloneRepositorySuccess())
})
.catch(() => {
dispatch(cloneRepositoryFailed())
})
return promise
}
export const createSolidityGithubAction = async () => {
const path = '.github/workflows/run-solidity-unittesting.yml'
await plugin.call('fileManager', 'writeFile', path , solTestYml)
await plugin.call('fileManager', 'writeFile', path, solTestYml)
plugin.call('fileManager', 'open', path)
}
export const createTsSolGithubAction = async () => {
const path = '.github/workflows/run-js-test.yml'
await plugin.call('fileManager', 'writeFile', path , tsSolTestYml)
await plugin.call('fileManager', 'writeFile', path, tsSolTestYml)
plugin.call('fileManager', 'open', path)
}
export const createSlitherGithubAction = async () => {
const path = '.github/workflows/run-slither-action.yml'
await plugin.call('fileManager', 'writeFile', path , slitherYml)
await plugin.call('fileManager', 'writeFile', path, slitherYml)
plugin.call('fileManager', 'open', path)
}
const scriptsRef = {
'deployer': contractDeployerScripts,
'etherscan': etherscanScripts
deployer: contractDeployerScripts,
etherscan: etherscanScripts,
}
export const createHelperScripts = async (script: string) => {
if (!scriptsRef[script]) return
await scriptsRef[script](plugin)
plugin.call('notification', 'toast', 'scripts added in the "scripts" folder')
@ -703,38 +787,44 @@ export const checkoutRemoteBranch = async (branch: string, remote: string) => {
okLabel: 'Force Checkout',
okFn: async () => {
dispatch(cloneRepositoryRequest())
plugin.call('dGitProvider', 'checkout', { ref: branch, remote, force: true }, false).then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const workspaceName = plugin.fileProviders.workspace.workspace
const branches = await getGitRepoBranches(workspacesPath + '/' + workspaceName)
dispatch(setCurrentWorkspaceBranches(branches))
dispatch(cloneRepositorySuccess())
}).catch(() => {
dispatch(cloneRepositoryFailed())
})
plugin
.call('dGitProvider', 'checkout', { ref: branch, remote, force: true }, false)
.then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const workspaceName = plugin.fileProviders.workspace.workspace
const branches = await getGitRepoBranches(workspacesPath + '/' + workspaceName)
dispatch(setCurrentWorkspaceBranches(branches))
dispatch(cloneRepositorySuccess())
})
.catch(() => {
dispatch(cloneRepositoryFailed())
})
},
cancelLabel: 'Cancel',
cancelFn: () => { },
hideFn: () => { }
cancelFn: () => {},
hideFn: () => {},
}
plugin.call('notification', 'modal', cloneModal)
} else {
dispatch(cloneRepositoryRequest())
plugin.call('dGitProvider', 'checkout', { ref: branch, remote, force: true }, false).then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const workspaceName = plugin.fileProviders.workspace.workspace
const branches = await getGitRepoBranches(workspacesPath + '/' + workspaceName)
dispatch(setCurrentWorkspaceBranches(branches))
dispatch(cloneRepositorySuccess())
}).catch(() => {
dispatch(cloneRepositoryFailed())
})
plugin
.call('dGitProvider', 'checkout', { ref: branch, remote, force: true }, false)
.then(async () => {
await fetchWorkspaceDirectory(ROOT_PATH)
dispatch(setCurrentWorkspaceCurrentBranch(branch))
const workspacesPath = plugin.fileProviders.workspace.workspacesPath
const workspaceName = plugin.fileProviders.workspace.workspace
const branches = await getGitRepoBranches(workspacesPath + '/' + workspaceName)
dispatch(setCurrentWorkspaceBranches(branches))
dispatch(cloneRepositorySuccess())
})
.catch(() => {
dispatch(cloneRepositoryFailed())
})
}
}

@ -3,7 +3,7 @@ import {useIntl} from 'react-intl'
import {TreeView} from '@remix-ui/tree-view' // eslint-disable-line
import {FileExplorerMenu} from './file-explorer-menu' // eslint-disable-line
import {FileExplorerContextMenu} from './file-explorer-context-menu' // eslint-disable-line
import {FileExplorerProps, FileType, WorkSpaceState} from '../types'
import {FileExplorerProps, FileType, WorkSpaceState, WorkspaceElement} from '../types'
import '../css/file-explorer.css'
import {checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath} from '@remix-ui/helper'
@ -151,7 +151,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
)
}
const handleClickFile = (path: string, type: 'folder' | 'file' | 'gist') => {
const handleClickFile = (path: string, type: WorkspaceElement) => {
if (!state.ctrlKey) {
props.dispatchHandleClickFile(path, type)
} else {

@ -1,6 +1,6 @@
// eslint-disable-next-line no-use-before-define
import React, {SyntheticEvent, useEffect, useState} from 'react'
import {FileType} from '../types'
import {FileType, WorkspaceElement} from '../types'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import {TreeView, TreeViewItem} from '@remix-ui/tree-view'
import {getPathIcon} from '@remix-ui/helper'
@ -14,7 +14,7 @@ export interface RenderFileProps {
file: FileType
index: number
focusEdit: {element: string; type: string; isNew: boolean; lastEdit: string}
focusElement: {key: string; type: 'file' | 'folder' | 'gist'}[]
focusElement: {key: string; type: WorkspaceElement}[]
focusContext: {element: string; x: number; y: number; type: string}
ctrlKey: boolean
expandPath: string[]

@ -1,12 +1,8 @@
import {extractNameFromKey} from '@remix-ui/helper'
import {action, FileType} from '../types'
import {action, Actions, FileType, WorkspaceElement} from '../types'
import * as _ from 'lodash'
import {fileDecoration} from '@remix-ui/file-decorators'
import {ROOT_PATH} from '../utils/constants'
interface Action {
type: string
payload: any
}
export interface BrowserState {
browser: {
currentWorkspace: string
@ -63,7 +59,7 @@ export interface BrowserState {
readonly: boolean
popup: string
focusEdit: string
focusElement: {key: string; type: 'file' | 'folder' | 'gist'}[]
focusElement: {key: string; type: WorkspaceElement}[]
initializingFS: boolean
gitConfig: {username: string; email: string; token: string}
}
@ -121,15 +117,10 @@ export const browserInitialState: BrowserState = {
gitConfig: {username: '', email: '', token: ''}
}
export const browserReducer = (state = browserInitialState, action: Action) => {
export const browserReducer = (state = browserInitialState, action: Actions) => {
switch (action.type) {
case 'SET_CURRENT_WORKSPACE': {
const payload = action.payload as {
name: string
isGitRepo: boolean
branches?: {remote: any; name: string}[]
currentBranch?: string
}
const payload = action.payload
const workspaces = state.browser.workspaces.find(
({name}) => name === payload.name
)
@ -147,12 +138,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_WORKSPACES': {
const payload = action.payload as {
name: string
isGitRepo: boolean
branches?: {remote: any; name: string}[]
currentBranch?: string
}[]
const payload = action.payload
return {
...state,
@ -164,7 +150,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_MODE': {
const payload = action.payload as 'browser' | 'localhost'
const payload = action.payload
return {
...state,
@ -191,7 +177,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FETCH_DIRECTORY_SUCCESS': {
const payload = action.payload as {path: string; fileTree}
const payload = action.payload
return {
...state,
@ -255,7 +241,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FETCH_WORKSPACE_DIRECTORY_SUCCESS': {
const payload = action.payload as {path: string; fileTree}
const payload = action.payload
return {
...state,
@ -302,14 +288,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'DISPLAY_NOTIFICATION': {
const payload = action.payload as {
title: string
message: string
actionOk: () => void
actionCancel: () => void
labelOk: string
labelCancel: string
}
const payload = action.payload
return {
...state,
@ -335,7 +314,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FILE_ADDED_SUCCESS': {
const payload = action.payload as string
const payload = action.payload
return {
...state,
@ -365,11 +344,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FOLDER_ADDED_SUCCESS': {
const payload = action.payload as {
path: string
folderPath: string
fileTree
}
const payload = action.payload
return {
...state,
@ -404,7 +379,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FILE_REMOVED_SUCCESS': {
const payload = action.payload as string
const payload = action.payload
return {
...state,
@ -446,11 +421,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'ADD_INPUT_FIELD': {
const payload = action.payload as {
path: string
fileTree
type: 'file' | 'folder'
}
const payload = action.payload
return {
...state,
@ -473,7 +444,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'REMOVE_INPUT_FIELD': {
const payload = action.payload as {path: string; fileTree}
const payload = action.payload
return {
...state,
@ -496,7 +467,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_READ_ONLY_MODE': {
const payload = action.payload as boolean
const payload = action.payload
return {
...state,
@ -505,11 +476,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'FILE_RENAMED_SUCCESS': {
const payload = action.payload as {
path: string
oldPath: string
fileTree
}
const payload = action.payload
return {
...state,
@ -543,12 +510,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'CREATE_WORKSPACE_SUCCESS': {
const payload = action.payload as {
name: string
isGitRepo: boolean
branches?: {remote: any; name: string}[]
currentBranch?: string
}
const payload = action.payload
const workspaces = state.browser.workspaces.find(
({name}) => name === payload.name
)
@ -581,7 +543,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'RENAME_WORKSPACE': {
const payload = action.payload as {oldName: string; workspaceName: string}
const payload = action.payload
let renamedWorkspace
const workspaces = state.browser.workspaces.filter(
({name, isGitRepo, branches, currentBranch}) => {
@ -611,7 +573,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'DELETE_WORKSPACE': {
const payload = action.payload as string
const payload = action.payload
const workspaces = state.browser.workspaces.filter(
({name}) => name && name !== payload
)
@ -626,7 +588,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'DISPLAY_POPUP_MESSAGE': {
const payload = action.payload as string
const payload = action.payload
return {
...state,
@ -642,10 +604,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_FOCUS_ELEMENT': {
const payload = action.payload as {
key: string
type: 'file' | 'folder' | 'gist'
}[]
const payload = action.payload
return {
...state,
@ -654,7 +613,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'REMOVE_FOCUS_ELEMENT': {
const payload: string = action.payload
const payload = action.payload
return {
...state,
@ -665,7 +624,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CONTEXT_MENU_ITEM': {
const payload = action.payload as action
const payload = action.payload
return {
...state,
@ -801,7 +760,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CURRENT_WORKSPACE_BRANCHES': {
const payload: {remote: any; name: string}[] = action.payload
const payload = action.payload
return {
...state,
@ -817,7 +776,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CURRENT_WORKSPACE_CURRENT_BRANCH': {
const payload: string = action.payload
const payload = action.payload
return {
...state,
@ -833,7 +792,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_CURRENT_WORKSPACE_IS_GITREPO': {
const payload: boolean = action.payload
const payload = action.payload
return {
...state,
@ -849,7 +808,7 @@ export const browserReducer = (state = browserInitialState, action: Action) => {
}
case 'SET_GIT_CONFIG': {
const payload: {username: string; token: string; email: string} =
const payload =
action.payload
return {
...state,
@ -1138,7 +1097,7 @@ const addContextMenuItem = (
const removeContextMenuItem = (
state: BrowserState,
plugin
plugin: {name: string}
): {
registeredMenuItems: action[]
removedMenuItems: action[]

@ -1,7 +1,7 @@
import React, {useState, useEffect, useRef, useContext, SyntheticEvent, ChangeEvent, KeyboardEvent, MouseEvent} from 'react' // eslint-disable-line
import React, {useState, useEffect, useRef, useContext, ChangeEvent} from 'react' // eslint-disable-line
import {FormattedMessage, useIntl} from 'react-intl'
import {Dropdown} from 'react-bootstrap'
import {CustomIconsToggle, CustomMenu, CustomToggle, CustomTooltip, extractNameFromKey, extractParentFromKey} from '@remix-ui/helper'
import {CustomIconsToggle, CustomMenu, CustomToggle, extractNameFromKey, extractParentFromKey} from '@remix-ui/helper'
import {FileExplorer} from './components/file-explorer' // eslint-disable-line
import {FileSystemContext} from './contexts'
import './css/remix-ui-workspace.css'
@ -385,7 +385,6 @@ export function Workspace() {
// @ts-ignore
uupsRadioRef.current.checked = false
} else displayOzCustomRef.current.style.display = 'none'
// @ts-ignore
let displayName = TEMPLATE_NAMES[(workspaceCreateTemplateInput.current && workspaceCreateTemplateInput.current.value) || 'remixDefault']
displayName = global.plugin.getAvailableWorkspaceName(displayName)
@ -729,6 +728,11 @@ export function Workspace() {
{intl.formatMessage({id: 'filePanel.multiSigWallet'})}
</option>
</optgroup>
<optgroup style={{fontSize: 'medium'}} label="Circom ZKP">
<option style={{fontSize: 'small'}} value="semaphore">
{intl.formatMessage({id: 'filePanel.semaphore'})}
</option>
</optgroup>
</select>
<div id="ozcustomization" data-id="ozCustomization" ref={displayOzCustomRef} style={{display: 'none'}} className="mb-2">
@ -899,10 +903,10 @@ export function Workspace() {
</span>
) : null}
<span className="d-flex">
<label className="pl-1 form-check-label" htmlFor="workspacesSelect" style={{wordBreak: 'keep-all'}}>
<FormattedMessage id="filePanel.workspace" />
<label className="pl-2 form-check-label" style={{wordBreak: 'keep-all'}}>
<FormattedMessage id='filePanel.workspace' />
</label>
</span>
</span>
</div>
<div className='mx-2'>
<Dropdown id="workspacesSelect" data-id="workspacesSelect" onToggle={toggleDropdown} show={showDropdown}>

@ -5,19 +5,19 @@ import { fileDecoration } from '@remix-ui/file-decorators'
import { RemixAppManager } from 'libs/remix-ui/plugin-manager/src/types'
import { ViewPlugin } from '@remixproject/engine-web'
export type action = { name: string, type?: Array<'folder' | 'gist' | 'file' | 'workspace'>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean, group: number }
export type action = { name: string, type?: Array<WorkspaceElement>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean, group: number }
export interface JSONStandardInput {
language: "Solidity";
settings?: any,
language: 'Solidity'
settings?: any
sources: {
[globalName: string]: {
keccak256?: string;
content: string;
},
};
keccak256?: string
content: string
}
}
}
export type MenuItems = action[]
export type WorkspaceTemplate = 'gist-template' | 'code-template' | 'remixDefault' | 'blank' | 'ozerc20' | 'zeroxErc20' | 'ozerc721'
export type WorkspaceTemplate = 'gist-template' | 'code-template' | 'remixDefault' | 'blank' | 'ozerc20' | 'zeroxErc20' | 'ozerc721' | 'playground' | 'semaphore'
export interface WorkspaceProps {
plugin: FilePanelType
}
@ -38,42 +38,42 @@ export interface Modal {
}
export interface FileType {
path: string,
name: string,
isDirectory: boolean,
type: 'folder' | 'file' | 'gist',
path: string
name: string
isDirectory: boolean
type: 'folder' | 'file' | 'gist'
child?: File[]
}
export interface FilePanelType extends ViewPlugin {
setWorkspace: ({ name, isLocalhost }, setEvent: boolean) => void,
createWorkspace: (name: string, workspaceTemplateName: string) => void,
renameWorkspace: (oldName: string, newName: string) => void
compileContractForUml: (path: string) => void
workspaceRenamed: ({ name }) => void,
workspaceCreated: ({ name }) => void,
workspaceDeleted: ({ name }) => void,
workspace?: any // workspace provider,
browser?: any // browser provider
localhost?: any // localhost provider
fileManager? : any
appManager: RemixAppManager
registry?: any // registry
pluginApi?: any
request: {
createWorkspace: () => void,
setWorkspace: (workspaceName: string) => void,
createNewFile: () => void,
uploadFile: (target: EventTarget & HTMLInputElement) => void,
getCurrentWorkspace: () => void
} // api request,
workspaces: any,
registeredMenuItems: MenuItems // menu items
removedMenuItems: MenuItems
initialWorkspace: string,
resetNewFile: () => void,
getWorkspaces: () => string[]
}
setWorkspace: ({ name, isLocalhost }, setEvent: boolean) => void
createWorkspace: (name: string, workspaceTemplateName: string) => void
renameWorkspace: (oldName: string, newName: string) => void
compileContractForUml: (path: string) => void
workspaceRenamed: ({ name }) => void
workspaceCreated: ({ name }) => void
workspaceDeleted: ({ name }) => void
workspace?: any // workspace provider,
browser?: any // browser provider
localhost?: any // localhost provider
fileManager?: any
appManager: RemixAppManager
registry?: any // registry
pluginApi?: any
request: {
createWorkspace: () => void
setWorkspace: (workspaceName: string) => void
createNewFile: () => void
uploadFile: (target: EventTarget & HTMLInputElement) => void
getCurrentWorkspace: () => void
} // api request,
workspaces: any
registeredMenuItems: MenuItems // menu items
removedMenuItems: MenuItems
initialWorkspace: string
resetNewFile: () => void
getWorkspaces: () => string[]
}
/* eslint-disable-next-line */
export interface FileExplorerProps {
@ -88,7 +88,7 @@ export interface FileExplorerProps {
focusEdit: string,
hideIconsMenu: React.Dispatch<React.SetStateAction<boolean>>,
showIconsMenu: boolean,
focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[],
focusElement: { key: string, type: WorkspaceElement }[],
dispatchCreateNewFile: (path: string, rootDir: string) => Promise<void>,
// eslint-disable-next-line no-undef
modal:(title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
@ -105,8 +105,8 @@ export interface FileExplorerProps {
dispatchRunScript: (path: string) => Promise<void>,
dispatchPublishToGist: (path?: string, type?: string) => Promise<void>,
dispatchEmitContextMenuEvent: (cmd: customAction) => Promise<void>,
dispatchHandleClickFile: (path: string, type: 'file' | 'folder' | 'gist') => Promise<void>,
dispatchSetFocusElement: (elements: { key: string, type: 'file' | 'folder' | 'gist' }[]) => Promise<void>,
dispatchHandleClickFile: (path: string, type: WorkspaceElement) => Promise<void>,
dispatchSetFocusElement: (elements: { key: string, type: WorkspaceElement }[]) => Promise<void>,
dispatchFetchDirectory:(path: string) => Promise<void>,
dispatchRemoveInputField:(path: string) => Promise<void>,
dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise<void>,
@ -114,7 +114,7 @@ export interface FileExplorerProps {
dispatchMoveFile: (src: string, dest: string) => Promise<void>,
dispatchMoveFolder: (src: string, dest: string) => Promise<void>,
handlePasteClick: (dest: string, destType: string) => void
handleCopyClick: (path: string, type: 'folder' | 'gist' | 'file' | 'workspace') => void
handleCopyClick: (path: string, type: WorkspaceElement) => void
addMenuItems: (items: MenuItems) => void
removeMenuItems: (items: MenuItems) => void
handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void
@ -128,41 +128,41 @@ export interface FileExplorerProps {
}
type Placement = import('react-overlays/usePopper').Placement
export interface FileExplorerMenuProps {
title: string,
menuItems: string[],
createNewFile: (folder?: string) => void,
createNewFolder: (parentFolder?: string) => void,
publishToGist: (path?: string) => void,
uploadFile: (target: EventTarget & HTMLInputElement) => void
uploadFolder: (target: EventTarget & HTMLInputElement) => void
tooltipPlacement?: Placement
title: string
menuItems: string[]
createNewFile: (folder?: string) => void
createNewFolder: (parentFolder?: string) => void
publishToGist: (path?: string) => void
uploadFile: (target: EventTarget & HTMLInputElement) => void
uploadFolder: (target: EventTarget & HTMLInputElement) => void
tooltipPlacement?: Placement
}
export interface FileExplorerContextMenuProps {
actions: action[],
createNewFile: (folder?: string) => void,
createNewFolder: (parentFolder?: string) => void,
deletePath: (path: string | string[]) => void,
renamePath: (path: string, type: string) => void,
downloadPath: (path: string) => void,
hideContextMenu: () => void,
publishToGist?: (path?: string, type?: string) => void,
pushChangesToGist?: (path?: string, type?: string) => void,
publishFolderToGist?: (path?: string, type?: string) => void,
publishFileToGist?: (path?: string, type?: string) => void,
runScript?: (path: string) => void,
emit?: (cmd: customAction) => void,
pageX: number,
pageY: number,
path: string,
type: string,
focus: {key:string, type:string}[],
onMouseOver?: (...args) => void,
copy?: (path: string, type: string) => void,
paste?: (destination: string, type: string) => void
copyFileName?: (path: string, type: string) => void
copyPath?: (path: string, type: string) => void
generateUml?: (path: string) => Promise<void>
uploadFile?: (target: EventTarget & HTMLInputElement) => void
actions: action[]
createNewFile: (folder?: string) => void
createNewFolder: (parentFolder?: string) => void
deletePath: (path: string | string[]) => void
renamePath: (path: string, type: string) => void
downloadPath: (path: string) => void
hideContextMenu: () => void
publishToGist?: (path?: string, type?: string) => void
pushChangesToGist?: (path?: string, type?: string) => void
publishFolderToGist?: (path?: string, type?: string) => void
publishFileToGist?: (path?: string, type?: string) => void
runScript?: (path: string) => void
emit?: (cmd: customAction) => void
pageX: number
pageY: number
path: string
type: string
focus: { key: string; type: string }[]
onMouseOver?: (...args) => void
copy?: (path: string, type: string) => void
paste?: (destination: string, type: string) => void
copyFileName?: (path: string, type: string) => void
copyPath?: (path: string, type: string) => void
generateUml?: (path: string) => Promise<void>
uploadFile?: (target: EventTarget & HTMLInputElement) => void
}
export interface WorkSpaceState {
@ -171,7 +171,7 @@ export interface WorkSpaceState {
actions: {
id: string
name: string
type?: Array<'folder' | 'gist' | 'file' | 'workspace'>
type?: Array<WorkspaceElement>
path?: string[]
extension?: string[]
pattern?: string[]
@ -190,9 +190,9 @@ export interface WorkSpaceState {
showContextMenu: boolean
reservedKeywords: string[]
copyElement: CopyElementType[]
}
}
export type FileFocusContextType = {
export type FileFocusContextType = {
element: string
x: number
y: number
@ -201,5 +201,107 @@ export type FileFocusContextType = {
export type CopyElementType = {
key: string
type: 'folder' | 'gist' | 'file' | 'workspace'
}
type: WorkspaceElement
}
export type FileTree = {
[x: string]: {
isDirectory: boolean
}
}
export interface ActionPayloadTypes {
SET_CURRENT_WORKSPACE: {
name: string
isGitRepo: boolean
branches?: {remote: string | undefined; name: string}[]
currentBranch?: string
},
SET_WORKSPACES: {
name: string
isGitRepo: boolean
branches?: {remote: string | undefined; name: string}[]
currentBranch?: string
}[],
SET_MODE: 'browser' | 'localhost',
FETCH_DIRECTORY_REQUEST: undefined | null,
FETCH_DIRECTORY_SUCCESS: { path: string; fileTree: FileTree },
FETCH_DIRECTORY_ERROR: string,
FETCH_WORKSPACE_DIRECTORY_REQUEST: undefined | null,
FETCH_WORKSPACE_DIRECTORY_SUCCESS: { path: string; fileTree: FileTree },
FETCH_WORKSPACE_DIRECTORY_ERROR: string,
DISPLAY_NOTIFICATION: {
title: string
message: string
actionOk: () => void
actionCancel: () => void
labelOk: string
labelCancel: string
},
HIDE_NOTIFICATION: undefined | null,
FILE_ADDED_SUCCESS: string,
FOLDER_ADDED_SUCCESS: {
path: string
folderPath: string
fileTree: FileTree
},
FILE_REMOVED_SUCCESS: string,
ROOT_FOLDER_CHANGED: string,
ADD_INPUT_FIELD: {
path: string
fileTree: FileTree
type: 'file' | 'folder'
},
REMOVE_INPUT_FIELD: { path: string; },
SET_READ_ONLY_MODE: boolean,
FILE_RENAMED_SUCCESS: {
path: string
oldPath: string
fileTree: FileTree
},
CREATE_WORKSPACE_REQUEST: undefined | null,
CREATE_WORKSPACE_SUCCESS: {
name: string
isGitRepo: boolean
branches?: { remote: string | undefined; name: string }[]
currentBranch?: string
},
CREATE_WORKSPACE_ERROR: string,
RENAME_WORKSPACE: { oldName: string; workspaceName: string },
DELETE_WORKSPACE: string,
DISPLAY_POPUP_MESSAGE: string,
HIDE_POPUP_MESSAGE: undefined | null,
SET_FOCUS_ELEMENT: {
key: string
type: WorkspaceElement
}[],
REMOVE_FOCUS_ELEMENT: string,
SET_CONTEXT_MENU_ITEM: action,
REMOVE_CONTEXT_MENU_ITEM: { name: string },
SET_EXPAND_PATH: string[],
LOAD_LOCALHOST_REQUEST: undefined | null,
LOAD_LOCALHOST_SUCCESS: undefined | null,
LOAD_LOCALHOST_ERROR: string,
CLONE_REPOSITORY_REQUEST: undefined | null,
CLONE_REPOSITORY_SUCCESS: undefined | null,
CLONE_REPOSITORY_FAILED: undefined | null,
FS_INITIALIZATION_COMPLETED: undefined | null,
SET_FILE_DECORATION_SUCCESS: fileDecoration[],
SET_CURRENT_WORKSPACE_BRANCHES: { remote: string | undefined; name: string }[],
SET_CURRENT_WORKSPACE_CURRENT_BRANCH: string,
SET_CURRENT_WORKSPACE_IS_GITREPO: boolean,
SET_GIT_CONFIG: {
username: string;
token: string;
email: string
}
}
export interface Action<T extends keyof ActionPayloadTypes> {
type: T,
payload: ActionPayloadTypes[T]
}
export type Actions = {[A in keyof ActionPayloadTypes]: Action<A>}[keyof ActionPayloadTypes]
export type WorkspaceElement = 'folder' | 'gist' | 'file' | 'workspace'

@ -80,5 +80,7 @@ export const TEMPLATE_NAMES = {
'ozerc721': 'OpenZeppelin ERC721',
'ozerc1155': 'OpenZeppelin ERC1155',
'zeroxErc20': '0xProject ERC20',
'gnosisSafeMultisig': 'Gnosis Safe'
'gnosisSafeMultisig': 'Gnosis Safe',
'playground': 'Playground',
'semaphore': 'Semaphore'
}

@ -5,6 +5,8 @@ export { default as ozerc721 } from './templates/ozerc721'
export { default as ozerc1155 } from './templates/ozerc1155'
export { default as zeroxErc20 } from './templates/zeroxErc20'
export { default as gnosisSafeMultisig } from './templates/gnosisSafeMultisig'
export { default as playground } from './templates/playground'
export { default as semaphore } from './templates/semaphore'
export { contractDeployerScripts } from './script-templates/contract-deployer'
export { etherscanScripts } from './script-templates/etherscan'

@ -23,6 +23,12 @@ export default async (opts) => {
// If no options is selected, opts.upgradeable will be undefined
// We do not show test file for upgradeable contract
// @ts-ignore
if (!opts || opts.upgradeable === undefined || !opts.upgradeable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
if (!opts || opts.upgradeable === undefined || !opts.upgradeable) {
// @ts-ignore
if (erc1155.defaults.mintable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_mintable_test.sol')).default
// @ts-ignore
else filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
}
return filesObj
}

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol";
import "remix_accounts.sol";
import "../contracts/MyToken.sol";
contract MyTokenTest {
MyToken s;
function beforeAll () public {
address acc0 = TestsAccounts.getAccount(0);
// acc0 will be set as initial owner
s = new MyToken(acc0);
}
function testSetURI () public {
string memory uri = "https://testuri.io/token";
s.setURI(uri);
Assert.equal(s.uri(1), uri, "uri did not match");
}
}

@ -29,7 +29,6 @@ export default async (opts) => {
if (erc20.defaults.mintable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_mintable_test.sol')).default
// @ts-ignore
else filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
}
return filesObj
}

@ -7,14 +7,16 @@ import "../contracts/MyToken.sol";
contract MyTokenTest is MyToken {
address acc0;
address acc0 = TestsAccounts.getAccount(0);
address acc1;
address acc2;
address acc3;
address acc4;
// acc0 will be set as initial owner
constructor() MyToken(acc0) {}
function beforeAll() public {
acc0 = TestsAccounts.getAccount(0);
acc1 = TestsAccounts.getAccount(1);
acc2 = TestsAccounts.getAccount(2);
acc3 = TestsAccounts.getAccount(3);

@ -24,6 +24,11 @@ export default async (opts) => {
// If no options is selected, opts.upgradeable will be undefined
// We do not show test file for upgradeable contract
// @ts-ignore
if (!opts || opts.upgradeable === undefined || !opts.upgradeable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
if (!opts || opts.upgradeable === undefined || !opts.upgradeable) {
// @ts-ignore
if (erc721.defaults.mintable) filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_mintable_test.sol')).default
// @ts-ignore
else filesObj['tests/MyToken_test.sol'] = (await import('raw-loader!./tests/MyToken_test.sol')).default
}
return filesObj
}

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "remix_tests.sol";
import "remix_accounts.sol";
import "../contracts/MyToken.sol";
contract MyTokenTest {
MyToken s;
function beforeAll () public {
address acc0 = TestsAccounts.getAccount(0);
// acc0 will be set as initial owner
s = new MyToken(acc0);
}
function testTokenNameAndSymbol () public {
Assert.equal(s.name(), "MyToken", "token name did not match");
Assert.equal(s.symbol(), "MTK", "token symbol did not match");
}
}

@ -0,0 +1,38 @@
{
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 80,
"tabWidth": 4,
"useTabs": false,
"singleQuote": false,
"bracketSpacing": false
}
},
{
"files": "*.yml",
"options": {}
},
{
"files": "*.yaml",
"options": {}
},
{
"files": "*.toml",
"options": {}
},
{
"files": "*.json",
"options": {}
},
{
"files": "*.js",
"options": {}
},
{
"files": "*.ts",
"options": {}
}
]
}

@ -0,0 +1,28 @@
REMIX DEFAULT WORKSPACE
Remix default workspace is present when:
i. Remix loads for the very first time
ii. A new workspace is created with 'Default' template
iii. There are no files existing in the File Explorer
This workspace contains 3 directories:
1. 'contracts': Holds three contracts with increasing levels of complexity.
2. 'scripts': Contains four typescript files to deploy a contract. It is explained below.
3. 'tests': Contains one Solidity test file for 'Ballot' contract & one JS test file for 'Storage' contract.
SCRIPTS
The 'scripts' folder has four typescript files which help to deploy the 'Storage' contract using 'web3.js' and 'ethers.js' libraries.
For the deployment of any other contract, just update the contract's name from 'Storage' to the desired contract and provide constructor arguments accordingly
in the file `deploy_with_ethers.ts` or `deploy_with_web3.ts`
In the 'tests' folder there is a script containing Mocha-Chai unit tests for 'Storage' contract.
To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.
Output from script will appear in remix terminal.
Please note, require/import is supported in a limited manner for Remix supported modules.
For now, modules supported by Remix are ethers, web3, swarmgw, chai, multihashes, remix and hardhat only for hardhat.ethers object/plugin.
For unsupported modules, an error like this will be thrown: '<module_name> module require is not supported by Remix IDE' will be shown.

@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12 <0.9.0;
contract HelloWorld {
function print() public pure returns (string memory) {
return "Hello World!";
}
}

@ -0,0 +1,16 @@
export default async () => {
return {
// @ts-ignore
'contracts/helloWorld.sol': (await import('raw-loader!./contracts/helloWorld.sol')).default,
// @ts-ignore
'scripts/deploy_with_ethers.ts': (await import('!!raw-loader!./scripts/deploy_with_ethers.ts')).default,
// @ts-ignore
'scripts/deploy_with_web3.ts': (await import('!!raw-loader!./scripts/deploy_with_web3.ts')).default,
// @ts-ignore
'scripts/ethers-lib.ts': (await import('!!raw-loader!./scripts/ethers-lib.ts')).default,
// @ts-ignore
'scripts/web3-lib.ts': (await import('!!raw-loader!./scripts/web3-lib.ts')).default,
// @ts-ignore
'.prettierrc.json': (await import('raw-loader!./.prettierrc')).default,
}
}

@ -0,0 +1,14 @@
// This script can be used to deploy the "Storage" contract using ethers.js library.
// Please make sure to compile "./contracts/1_Storage.sol" file before running this script.
// And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S
import { deploy } from './ethers-lib'
(async () => {
try {
const result = await deploy('HelloWorld', [])
console.log(`address: ${result.address}`)
} catch (e) {
console.log(e.message)
}
})()

@ -0,0 +1,14 @@
// This script can be used to deploy the "Storage" contract using Web3 library.
// Please make sure to compile "./contracts/1_Storage.sol" file before running this script.
// And use Right click -> "Run" from context menu of the file to run the script. Shortcut: Ctrl+Shift+S
import { deploy } from './web3-lib'
(async () => {
try {
const result = await deploy('HelloWorld', [])
console.log(`address: ${result.address}`)
} catch (e) {
console.log(e.message)
}
})()

@ -0,0 +1,29 @@
import { ethers } from 'ethers'
/**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {Number} accountIndex account index from the exposed account
* @return {Contract} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, accountIndex?: number): Promise<ethers.Contract> => {
console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
// 'web3Provider' is a remix global variable object
const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner(accountIndex)
const factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer)
const contract = await factory.deploy(...args)
// The contract is NOT deployed yet; we must wait until it is mined
await contract.deployed()
return contract
}

@ -0,0 +1,36 @@
import Web3 from 'web3'
import { Contract, ContractSendMethod, Options } from 'web3-eth-contract'
/**
* Deploy the given contract
* @param {string} contractName name of the contract to deploy
* @param {Array<any>} args list of constructor' parameters
* @param {string} from account used to send the transaction
* @param {number} gas gas limit
* @return {Options} deployed contract
*/
export const deploy = async (contractName: string, args: Array<any>, from?: string, gas?: number): Promise<Options> => {
const web3 = new Web3(web3Provider)
console.log(`deploying ${contractName}`)
// Note that the script needs the ABI which is generated from the compilation artifact.
// Make sure contract is compiled and artifacts are generated
const artifactsPath = `browser/contracts/artifacts/${contractName}.json`
const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
const accounts = await web3.eth.getAccounts()
const contract: Contract = new web3.eth.Contract(metadata.abi)
const contractSend: ContractSendMethod = contract.deploy({
data: metadata.data.bytecode.object,
arguments: args
})
const newContractInstance = await contractSend.send({
from: from || accounts[0],
gas: gas || 1500000
})
return newContractInstance.options
}

@ -0,0 +1,9 @@
REMIX CIRCOM WORKSPACE
Welcome to the Remix Circom Workspace. This workspace becomes available when you create a new workspace using the 'Circom' template.
Directory Structure
The workspace comprises two main directories:
circuits: Contains sample semaphore contracts. These can be compiled to generate a witness.
scripts: Provides a sample script designed for a trusted setup using snarkjs. This script also aids in generating Solidity code, which is essential for on-chain deployment.

@ -0,0 +1,90 @@
pragma circom 2.0.0;
include "circomlib/poseidon.circom";
include "./tree.circom";
template CalculateSecret() {
signal input identityNullifier;
signal input identityTrapdoor;
signal output out;
component poseidon = Poseidon(2);
poseidon.inputs[0] <== identityNullifier;
poseidon.inputs[1] <== identityTrapdoor;
out <== poseidon.out;
}
template CalculateIdentityCommitment() {
signal input secret;
signal output out;
component poseidon = Poseidon(1);
poseidon.inputs[0] <== secret;
out <== poseidon.out;
}
template CalculateNullifierHash() {
signal input externalNullifier;
signal input identityNullifier;
signal output out;
component poseidon = Poseidon(2);
poseidon.inputs[0] <== externalNullifier;
poseidon.inputs[1] <== identityNullifier;
out <== poseidon.out;
}
// credits to : https://github.com/semaphore-protocol/semaphore
// The current Semaphore smart contracts require nLevels <= 32 and nLevels >= 16.
template Semaphore(nLevels) {
signal input identityNullifier;
signal input identityTrapdoor;
signal input treePathIndices[nLevels];
signal input treeSiblings[nLevels];
signal input signalHash;
signal input externalNullifier;
signal output root;
signal output nullifierHash;
component calculateSecret = CalculateSecret();
calculateSecret.identityNullifier <== identityNullifier;
calculateSecret.identityTrapdoor <== identityTrapdoor;
signal secret;
secret <== calculateSecret.out;
component calculateIdentityCommitment = CalculateIdentityCommitment();
calculateIdentityCommitment.secret <== secret;
component calculateNullifierHash = CalculateNullifierHash();
calculateNullifierHash.externalNullifier <== externalNullifier;
calculateNullifierHash.identityNullifier <== identityNullifier;
component inclusionProof = MerkleTreeInclusionProof(nLevels);
inclusionProof.leaf <== calculateIdentityCommitment.out;
for (var i = 0; i < nLevels; i++) {
inclusionProof.siblings[i] <== treeSiblings[i];
inclusionProof.pathIndices[i] <== treePathIndices[i];
}
root <== inclusionProof.root;
// Dummy square to prevent tampering signalHash.
signal signalHashSquared;
signalHashSquared <== signalHash * signalHash;
nullifierHash <== calculateNullifierHash.out;
}
component main {public [signalHash, externalNullifier]} = Semaphore(20);

@ -0,0 +1,11 @@
pragma circom 2.0.0;
template Multiplier2() {
signal input a;
signal input b;
signal output c;
c <== a*b;
}
component main = Multiplier2();

@ -0,0 +1,40 @@
pragma circom 2.0.0;
include "circomlib/poseidon.circom";
include "circomlib/mux1.circom";
template MerkleTreeInclusionProof(nLevels) {
signal input leaf;
signal input pathIndices[nLevels];
signal input siblings[nLevels];
signal output root;
component poseidons[nLevels];
component mux[nLevels];
signal hashes[nLevels + 1];
hashes[0] <== leaf;
for (var i = 0; i < nLevels; i++) {
pathIndices[i] * (1 - pathIndices[i]) === 0;
poseidons[i] = Poseidon(2);
mux[i] = MultiMux1(2);
mux[i].c[0][0] <== hashes[i];
mux[i].c[0][1] <== siblings[i];
mux[i].c[1][0] <== siblings[i];
mux[i].c[1][1] <== hashes[i];
mux[i].s <== pathIndices[i];
poseidons[i].inputs[0] <== mux[i].out[0];
poseidons[i].inputs[1] <== mux[i].out[1];
hashes[i + 1] <== poseidons[i].out;
}
root <== hashes[nLevels];
}

@ -0,0 +1,18 @@
export default async () => {
return {
// @ts-ignore
'circuits/semaphore.circom': (await import('raw-loader!./circuits/semaphore.circom')).default,
// @ts-ignore
'circuits/simple.circom': (await import('!!raw-loader!./circuits/simple.circom')).default,
// @ts-ignore
'circuits/tree.circom': (await import('!!raw-loader!./circuits/tree.circom')).default,
// @ts-ignore
'scripts/run_setup.ts': (await import('!!raw-loader!./scripts/run_setup.ts')).default,
// @ts-ignore
'scripts/run_verification.ts': (await import('!!raw-loader!./scripts/run_verification.ts')).default,
// @ts-ignore
'templates/groth16_verifier.sol.ejs': (await import('!!raw-loader!./templates/groth16_verifier.sol.ejs')).default,
// @ts-ignore
'README.txt': (await import('raw-loader!./README.txt')).default
}
}

@ -0,0 +1,72 @@
import { ethers, BigNumber } from 'ethers'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const snarkjs = require('snarkjs');
const logger = {
info: (...args) => console.log(...args),
debug: (...args) => console.log(...args)
};
/**
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
* @param message The message to be hashed.
* @returns The message digest.
*/
function hash(message: any): bigint {
message = BigNumber.from(message).toTwos(256).toHexString()
message = ethers.utils.zeroPad(message, 32)
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8)
}
(async () => {
try {
// @ts-ignore
await remix.call('circuit-compiler', 'generateR1cs', 'circuits/semaphore.circom');
const ptau_final = "https://ipfs-cluster.ethdevops.io/ipfs/QmTiT4eiYz5KF7gQrDsgfCSTRv3wBPYJ4bRN1MmTRshpnW";
// @ts-ignore
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true);
// @ts-ignore
const r1cs = new Uint8Array(r1csBuffer);
const zkey_0 = { type: "mem" };
const zkey_1 = { type: "mem" };
const zkey_final = { type: "mem" };
console.log('newZkey')
await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_0);
console.log('contribute')
await snarkjs.zKey.contribute(zkey_0, zkey_1, "p2_C1", "pa_Entropy1");
console.log('beacon')
await snarkjs.zKey.beacon(zkey_1, zkey_final, "B3", "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", 10);
console.log('verifyFromR1cs')
const verifyFromR1csResult = await snarkjs.zKey.verifyFromR1cs(r1cs, ptau_final, zkey_final);
console.assert(verifyFromR1csResult);
console.log('verifyFromInit')
const verifyFromInit = await snarkjs.zKey.verifyFromInit(zkey_0, ptau_final, zkey_final);
console.assert(verifyFromInit);
console.log('exportVerificationKey')
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final)
await remix.call('fileManager', 'writeFile', './zk/build/verification_key.json', JSON.stringify(vKey))
const templates = {
groth16: await remix.call('fileManager', 'readFile', 'templates/groth16_verifier.sol.ejs')
}
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates)
await remix.call('fileManager', 'writeFile', './zk/build/zk_verifier.sol', solidityContract)
console.log('buffer', (zkey_final as any).data.length)
await remix.call('fileManager', 'writeFile', './zk/build/zk_setup.txt', JSON.stringify(Array.from(((zkey_final as any).data))))
console.log('setup done.')
} catch (e) {
console.error(e.message)
}
})()

@ -0,0 +1,102 @@
import { ethers, BigNumber } from 'ethers'
import { IncrementalMerkleTree } from "@zk-kit/incremental-merkle-tree"
import { poseidon } from "circomlibjs" // v0.0.8
// eslint-disable-next-line @typescript-eslint/no-var-requires
const snarkjs = require('snarkjs');
const logger = {
info: (...args) => console.log(...args),
debug: (...args) => console.log(...args),
error: (...args) => console.error(...args),
}
/**
* Creates a keccak256 hash of a message compatible with the SNARK scalar modulus.
* @param message The message to be hashed.
* @returns The message digest.
*/
function hash(message: any): bigint {
message = BigNumber.from(message).toTwos(256).toHexString()
message = ethers.utils.zeroPad(message, 32)
return BigInt(ethers.utils.keccak256(message)) >> BigInt(8)
}
(async () => {
try {
// @ts-ignore
const r1csBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.r1cs', true);
// @ts-ignore
const r1cs = new Uint8Array(r1csBuffer);
// @ts-ignore
const wasmBuffer = await remix.call('fileManager', 'readFile', 'circuits/.bin/semaphore.wasm', true);
// @ts-ignore
const wasm = new Uint8Array(wasmBuffer);
const zkey_final = {
type: "mem",
data: new Uint8Array(JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/zk_setup.txt')))
}
const wtns = { type: "mem" };
const vKey = JSON.parse(await remix.call('fileManager', 'readFile', './zk/build/verification_key.json'))
// build list of identity commitments
const secrets = []
const identityCommitments = []
for (let k = 0; k < 2; k++) {
const identityTrapdoor = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32)))
const identityNullifier = BigInt(ethers.utils.hexlify(ethers.utils.randomBytes(32)))
secrets.push({identityTrapdoor, identityNullifier})
const secret = poseidon([identityNullifier, identityTrapdoor])
const identityCommitment = poseidon([secret])
identityCommitments.push(identityCommitment)
}
//console.log('incremental tree', identityCommitments.map((x) => x.toString()))
let tree
try {
tree = new IncrementalMerkleTree(poseidon, 20, BigInt(0), 2, identityCommitments) // Binary tree.
} catch (e) {
console.error(e.message)
return
}
const index = tree.indexOf(identityCommitments[0])
console.log(index.toString())
const proof1 = tree.createProof(0)
console.log('prepare signals for id ', identityCommitments[0].toString(), tree.indexOf(identityCommitments[0]), proof1.siblings.map((x)=> x.toString()))
const signals = {
identityTrapdoor: secrets[0].identityTrapdoor,
identityNullifier: secrets[0].identityNullifier,
treePathIndices: proof1.pathIndices,
treeSiblings: proof1.siblings,
externalNullifier: hash(42),
signalHash: hash(ethers.utils.formatBytes32String("Hello World"))
}
console.log('calculate')
await snarkjs.wtns.calculate(signals, wasm, wtns);
console.log('check')
await snarkjs.wtns.check(r1cs, wtns, logger);
console.log('prove')
const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns);
const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, logger);
console.log('zk proof validity', verified);
proof1.root.toString() === publicSignals[0] ? console.log('merkle proof valid') : console.log('merkle proof invalid')
} catch (e) {
console.error(e.message)
}
})()

@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-3.0
/*
Copyright 2021 0KIMS association.
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
snarkJS is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
snarkJS is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity >=0.7.0 <0.9.0;
contract Groth16Verifier {
// Scalar field size
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Base field size
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
// Verification Key data
uint256 constant alphax = <%= vk_alpha_1[0] %>;
uint256 constant alphay = <%= vk_alpha_1[1] %>;
uint256 constant betax1 = <%= vk_beta_2[0][1] %>;
uint256 constant betax2 = <%= vk_beta_2[0][0] %>;
uint256 constant betay1 = <%= vk_beta_2[1][1] %>;
uint256 constant betay2 = <%= vk_beta_2[1][0] %>;
uint256 constant gammax1 = <%= vk_gamma_2[0][1] %>;
uint256 constant gammax2 = <%= vk_gamma_2[0][0] %>;
uint256 constant gammay1 = <%= vk_gamma_2[1][1] %>;
uint256 constant gammay2 = <%= vk_gamma_2[1][0] %>;
uint256 constant deltax1 = <%= vk_delta_2[0][1] %>;
uint256 constant deltax2 = <%= vk_delta_2[0][0] %>;
uint256 constant deltay1 = <%= vk_delta_2[1][1] %>;
uint256 constant deltay2 = <%= vk_delta_2[1][0] %>;
<% for (let i=0; i<IC.length; i++) { %>
uint256 constant IC<%=i%>x = <%=IC[i][0]%>;
uint256 constant IC<%=i%>y = <%=IC[i][1]%>;
<% } %>
// Memory data
uint16 constant pVk = 0;
uint16 constant pPairing = 128;
uint16 constant pLastMem = 896;
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[<%=IC.length-1%>] calldata _pubSignals) public view returns (bool) {
assembly {
function checkField(v) {
if iszero(lt(v, q)) {
mstore(0, 0)
return(0, 0x20)
}
}
// G1 function to multiply a G1 value(x,y) to value in an address
function g1_mulAccC(pR, x, y, s) {
let success
let mIn := mload(0x40)
mstore(mIn, x)
mstore(add(mIn, 32), y)
mstore(add(mIn, 64), s)
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
mstore(add(mIn, 64), mload(pR))
mstore(add(mIn, 96), mload(add(pR, 32)))
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
}
function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
let _pPairing := add(pMem, pPairing)
let _pVk := add(pMem, pVk)
mstore(_pVk, IC0x)
mstore(add(_pVk, 32), IC0y)
// Compute the linear combination vk_x
<% for (let i = 1; i <= nPublic; i++) { %>
g1_mulAccC(_pVk, IC<%=i%>x, IC<%=i%>y, calldataload(add(pubSignals, <%=(i-1)*32%>)))
<% } %>
// -A
mstore(_pPairing, calldataload(pA))
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
// B
mstore(add(_pPairing, 64), calldataload(pB))
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))
// alpha1
mstore(add(_pPairing, 192), alphax)
mstore(add(_pPairing, 224), alphay)
// beta2
mstore(add(_pPairing, 256), betax1)
mstore(add(_pPairing, 288), betax2)
mstore(add(_pPairing, 320), betay1)
mstore(add(_pPairing, 352), betay2)
// vk_x
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))
// gamma2
mstore(add(_pPairing, 448), gammax1)
mstore(add(_pPairing, 480), gammax2)
mstore(add(_pPairing, 512), gammay1)
mstore(add(_pPairing, 544), gammay2)
// C
mstore(add(_pPairing, 576), calldataload(pC))
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))
// delta2
mstore(add(_pPairing, 640), deltax1)
mstore(add(_pPairing, 672), deltax2)
mstore(add(_pPairing, 704), deltay1)
mstore(add(_pPairing, 736), deltay2)
let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)
isOk := and(success, mload(_pPairing))
}
let pMem := mload(0x40)
mstore(0x40, add(pMem, pLastMem))
// Validate that all evaluations ∈ F
<% for (let i=0; i<IC.length; i++) { %>
checkField(calldataload(add(_pubSignals, <%=i*32%>)))
<% } %>
// Validate all evaluations
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
mstore(0, isValid)
return(0, 0x20)
}
}
}

@ -150,7 +150,7 @@
"brace": "^0.8.0",
"change-case": "^4.1.1",
"chokidar": "^2.1.8",
"circom_wasm": "^0.0.2",
"circom_wasm": "^0.1.0",
"color-support": "^1.1.3",
"commander": "^9.4.1",
"core-js": "^3.6.5",
@ -202,6 +202,7 @@
"remark-gfm": "^3.0.1",
"rss-parser": "^3.12.0",
"signale": "^1.4.0",
"snarkjs": "^0.7.0",
"sol2uml": "^2.4.3",
"string-similarity": "^4.0.4",
"svg2pdf.js": "^2.2.1",

@ -2616,6 +2616,19 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@iden3/bigarray@0.0.2":
version "0.0.2"
resolved "https://registry.yarnpkg.com/@iden3/bigarray/-/bigarray-0.0.2.tgz#6fc4ba5be18daf8a26ee393f2fb62b80d98c05e9"
integrity sha512-Xzdyxqm1bOFF6pdIsiHLLl3HkSLjbhqJHVyqaTxXt3RqXBEnmsUmEW47H7VOi/ak7TdkRpNkxjyK5Zbkm+y52g==
"@iden3/binfileutils@0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@iden3/binfileutils/-/binfileutils-0.0.11.tgz#9ffbbcc1279f2b2182bb6dcff4eee8a5b2167911"
integrity sha512-LylnJoZ0CTdgErnKY8OxohvW4K+p6UHD3sxt+3P9AmMyBQjYR4IpoqoYZZ+9aMj89cmCQ21UvdhndAx04er3NA==
dependencies:
fastfile "0.0.20"
ffjavascript "^0.2.48"
"@isomorphic-git/idb-keyval@3.3.2":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@isomorphic-git/idb-keyval/-/idb-keyval-3.3.2.tgz#c0509a6c5987d8a62efb3e47f2815bcc5eda2489"
@ -7660,7 +7673,7 @@ axobject-query@^2.2.0:
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
b4a@^1.6.4:
b4a@^1.0.1, b4a@^1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9"
integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==
@ -8432,6 +8445,17 @@ before-after-hook@^2.0.0:
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e"
integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==
bfj@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.1.0.tgz#c5177d522103f9040e1b12980fe8c38cf41d3f8b"
integrity sha512-I6MMLkn+anzNdCUp9hMRyui1HaNEUCco50lxbvNS4+EyXg8lN3nJ48PjPWtbH8UVS9CuMoaKE9U2V3l29DaRQw==
dependencies:
bluebird "^3.7.2"
check-types "^11.2.3"
hoopy "^0.1.4"
jsonpath "^1.1.1"
tryer "^1.0.1"
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@ -8497,6 +8521,14 @@ bl@^4.0.0, bl@^4.0.3, bl@^4.1.0:
inherits "^2.0.4"
readable-stream "^3.4.0"
blake2b-wasm@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz#9115649111edbbd87eb24ce7c04b427e4e2be5be"
integrity sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==
dependencies:
b4a "^1.0.1"
nanoassert "^2.0.0"
blakejs@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702"
@ -8521,7 +8553,7 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
bluebird@^3.0.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5:
bluebird@^3.0.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5, bluebird@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@ -9525,6 +9557,11 @@ check-error@1.0.2, check-error@^1.0.2:
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==
check-types@^11.2.3:
version "11.2.3"
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.3.tgz#1ffdf68faae4e941fce252840b1787b8edc93b71"
integrity sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==
cheerio-select@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4"
@ -9683,10 +9720,17 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
inherits "^2.0.1"
safe-buffer "^5.0.1"
circom_wasm@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/circom_wasm/-/circom_wasm-0.0.2.tgz#9d24866b8289a5778999270823a4cb06e64145b5"
integrity sha512-SCMP6cxHHL7MLedDrTl+nGYyE6+kE5GepbxtZm65GlR0wUMD9eNOD1shwScWaDnmBOZTrImmNeTYZA5DWCmIww==
circom_runtime@0.1.22:
version "0.1.22"
resolved "https://registry.yarnpkg.com/circom_runtime/-/circom_runtime-0.1.22.tgz#f957c47662cdd03cd3fb76979c434c719a366373"
integrity sha512-V/XYZWBhbZY8SotkaGH4FbiDYAZ8a1Md++MBiKPDOuWS/NIJB+Q+XIiTC8zKMgoDaa9cd2OiTvsC9J6te7twNg==
dependencies:
ffjavascript "0.2.57"
circom_wasm@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/circom_wasm/-/circom_wasm-0.1.0.tgz#dda76c7ae9046ea6f1e1cd3754c017ad753bd5c1"
integrity sha512-F7ihfVGjfSz+01yFXLHjKocQFm8j9KBageqMw5+olFWB6+7CXHLjnUaFuU6u+7T0FsL7+JuP18jdcAVQEXoQgw==
circular-json@^0.3.0:
version "0.3.3"
@ -11250,7 +11294,7 @@ deep-extend@~0.4.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
integrity sha1-7+QRPQgIX05vlod1mBD4B0aeIlM=
deep-is@^0.1.3:
deep-is@^0.1.3, deep-is@~0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
@ -11839,6 +11883,13 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
ejs@^3.1.6:
version "3.1.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
dependencies:
jake "^10.8.5"
ejs@^3.1.7, ejs@^3.1.8:
version "3.1.8"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b"
@ -12248,6 +12299,18 @@ escape-string-regexp@^5.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
escodegen@^1.8.1:
version "1.14.3"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==
dependencies:
esprima "^4.0.1"
estraverse "^4.2.0"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.6.1"
escodegen@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17"
@ -12476,6 +12539,11 @@ espree@^9.4.0:
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.3.0"
esprima@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.2.tgz#76a0fd66fcfe154fd292667dc264019750b1657b"
integrity sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==
esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
@ -12495,7 +12563,7 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
estraverse@^4.1.1:
estraverse@^4.1.1, estraverse@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
@ -13133,7 +13201,7 @@ fast-levenshtein@^1.0.0:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9"
integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=
fast-levenshtein@^2.0.6:
fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
@ -13163,6 +13231,11 @@ fastest-levenshtein@^1.0.12:
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
fastfile@0.0.20:
version "0.0.20"
resolved "https://registry.yarnpkg.com/fastfile/-/fastfile-0.0.20.tgz#794a143d58cfda2e24c298e5ef619c748c8a1879"
integrity sha512-r5ZDbgImvVWCP0lA/cGNgQcZqR+aYdFx3u+CtJqUE510pBUVGMn4ulL/iRTI4tACTYsNJ736uzFxEBXesPAktA==
fastq@^1.6.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
@ -13226,6 +13299,33 @@ fetch-blob@^2.1.1, fetch-blob@^2.1.2:
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c"
integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==
ffjavascript@0.2.57:
version "0.2.57"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.57.tgz#ba1be96015b2688192e49f2f4de2cc5150fd8594"
integrity sha512-V+vxZ/zPNcthrWmqfe/1YGgqdkTamJeXiED0tsk7B84g40DKlrTdx47IqZuiygqAVG6zMw4qYuvXftIJWsmfKQ==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.0"
web-worker "^1.2.0"
ffjavascript@0.2.59:
version "0.2.59"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.59.tgz#b2f836082587fab333dfb181b909a188f80036f3"
integrity sha512-QssOEUv+wilz9Sg7Zaj6KWAm7QceOAEsFuEBTltUsDo1cjn11rA/LGYvzFBPbzNfxRlZxwgJ7uxpCQcdDlrNfw==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.1"
web-worker "^1.2.0"
ffjavascript@^0.2.48:
version "0.2.60"
resolved "https://registry.yarnpkg.com/ffjavascript/-/ffjavascript-0.2.60.tgz#4d8ae613d6bf4e98b3cc29ba10c626f5853854cf"
integrity sha512-T/9bnEL5xAZRDbQoEMf+pM9nrhK+C3JyZNmqiWub26EQorW7Jt+jR54gpqDhceA4Nj0YctPQwYnl8xa52/A26A==
dependencies:
wasmbuilder "0.0.16"
wasmcurves "0.2.2"
web-worker "^1.2.0"
fflate@^0.4.8:
version "0.4.8"
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
@ -14883,6 +14983,11 @@ homedir-polyfill@^1.0.1:
dependencies:
parse-passwd "^1.0.0"
hoopy@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
hosted-git-info@^2.1.4, hosted-git-info@^2.7.1:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
@ -17196,6 +17301,15 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.0.tgz#85fc245b1d9259acc6941960b905adf64e7de0e8"
integrity sha1-hfwkWx2SWazGlBlguQWt9k594Og=
jsonpath@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.1.1.tgz#0ca1ed8fb65bb3309248cc9d5466d12d5b0b9901"
integrity sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==
dependencies:
esprima "1.2.2"
static-eval "2.0.2"
underscore "1.12.1"
jspdf@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/jspdf/-/jspdf-2.5.1.tgz#00c85250abf5447a05f3b32ab9935ab4a56592cc"
@ -17537,6 +17651,14 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==
dependencies:
prelude-ls "~1.1.2"
type-check "~0.3.2"
license-webpack-plugin@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz#1e18442ed20b754b82f1adeff42249b81d11aec6"
@ -18033,6 +18155,11 @@ logform@^2.2.0:
safe-stable-stringify "^1.1.0"
triple-beam "^1.3.0"
logplease@^1.2.15:
version "1.2.15"
resolved "https://registry.yarnpkg.com/logplease/-/logplease-1.2.15.tgz#3da442e93751a5992cc19010a826b08d0293c48a"
integrity sha512-jLlHnlsPSJjpwUfcNyUxXCl33AYg2cHhIf9QhGL2T4iPT0XPB+xP1LRKFPgIg1M/sg9kAJvy94w9CzBNrfnstA==
longest-streak@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
@ -19574,6 +19701,11 @@ nanoassert@^1.1.0:
resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-1.1.0.tgz#4f3152e09540fde28c76f44b19bbcd1d5a42478d"
integrity sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=
nanoassert@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-2.0.0.tgz#a05f86de6c7a51618038a620f88878ed1e490c09"
integrity sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==
nanobench@^2.1.0, nanobench@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/nanobench/-/nanobench-2.1.1.tgz#c2f23fcce116d50b4998b1954ba114674c137269"
@ -20767,6 +20899,18 @@ optimist@^0.6.1:
minimist "~0.0.1"
wordwrap "~0.0.2"
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
dependencies:
deep-is "~0.1.3"
fast-levenshtein "~2.0.6"
levn "~0.3.0"
prelude-ls "~1.1.2"
type-check "~0.3.2"
word-wrap "~1.2.3"
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@ -21988,6 +22132,11 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
prepend-http@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
@ -22443,6 +22592,16 @@ quick-lru@^5.1.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
r1csfile@0.0.45:
version "0.0.45"
resolved "https://registry.yarnpkg.com/r1csfile/-/r1csfile-0.0.45.tgz#59d59a33f8b5280017fc00ee691d003a3d705fe0"
integrity sha512-YKIp4D441aZ6OoI9y+bfAyb2j4Cl+OFq/iiX6pPWDrL4ZO968h0dq0w07i65edvrTt7/G43mTnl0qEuLXyp/Yw==
dependencies:
"@iden3/bigarray" "0.0.2"
"@iden3/binfileutils" "0.0.11"
fastfile "0.0.20"
ffjavascript "0.2.57"
raf-schd@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
@ -24446,6 +24605,22 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
snarkjs@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/snarkjs/-/snarkjs-0.7.0.tgz#9b4d193a0535c1903e45f1508aa7ad74cd130844"
integrity sha512-Vu5W+0Va6X1xvlCllpZ2r3/S7MafnL6IrAv09lk/F+VNDHuHEHx3xopR9Kr70p2KpbBBJ/HB9VCDZWism8WGlA==
dependencies:
"@iden3/binfileutils" "0.0.11"
bfj "^7.0.2"
blake2b-wasm "^2.4.0"
circom_runtime "0.1.22"
ejs "^3.1.6"
fastfile "0.0.20"
ffjavascript "0.2.59"
js-sha3 "^0.8.0"
logplease "^1.2.15"
r1csfile "0.0.45"
sntp@1.x.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
@ -24868,6 +25043,13 @@ state-local@^1.0.6:
resolved "https://registry.yarnpkg.com/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5"
integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==
static-eval@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42"
integrity sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==
dependencies:
escodegen "^1.8.1"
static-extend@^0.1.1, static-extend@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@ -25997,6 +26179,11 @@ truncate-utf8-bytes@^1.0.0:
dependencies:
utf8-byte-length "^1.0.1"
tryer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
ts-loader@^9.2.6:
version "9.2.6"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.6.tgz#9937c4dd0a1e3dbbb5e433f8102a6601c6615d74"
@ -26166,6 +26353,13 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==
dependencies:
prelude-ls "~1.1.2"
type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
@ -26418,6 +26612,11 @@ undefsafe@^2.0.5:
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
underscore@1.12.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e"
integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==
undertaker-registry@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50"
@ -27072,6 +27271,32 @@ warning@^4.0.0, warning@^4.0.3:
dependencies:
loose-envify "^1.0.0"
wasmbuilder@0.0.16:
version "0.0.16"
resolved "https://registry.yarnpkg.com/wasmbuilder/-/wasmbuilder-0.0.16.tgz#f34c1f2c047d2f6e1065cbfec5603988f16d8549"
integrity sha512-Qx3lEFqaVvp1cEYW7Bfi+ebRJrOiwz2Ieu7ZG2l7YyeSJIok/reEQCQCuicj/Y32ITIJuGIM9xZQppGx5LrQdA==
wasmcurves@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/wasmcurves/-/wasmcurves-0.2.0.tgz#ccfc5a7d3778b6e0768b82a9336c80054f9bc0cf"
integrity sha512-3e2rbxdujOwaod657gxgmdhZNn+i1qKdHO3Y/bK+8E7bV8ttV/fu5FO4/WLBACF375cK0QDLOP+65Na63qYuWA==
dependencies:
wasmbuilder "0.0.16"
wasmcurves@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/wasmcurves/-/wasmcurves-0.2.1.tgz#416d15432a9c6a7b79ef6000eab1e8e7302624ad"
integrity sha512-9ciO7bUE5bgpbOcdK7IO3enrSVIKHwrQmPibok4GLJWaCA7Wyqc9PRYnu5HbiFv9NDFNqVKPtU5R6Is5KujBLg==
dependencies:
wasmbuilder "0.0.16"
wasmcurves@0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/wasmcurves/-/wasmcurves-0.2.2.tgz#ca444f6a6f6e2a5cbe6629d98ff478a62b4ccb2b"
integrity sha512-JRY908NkmKjFl4ytnTu5ED6AwPD+8VJ9oc94kdq7h5bIwbj0L4TDJ69mG+2aLs2SoCmGfqIesMWTEJjtYsoQXQ==
dependencies:
wasmbuilder "0.0.16"
watchify@^3.9.0:
version "3.11.1"
resolved "https://registry.yarnpkg.com/watchify/-/watchify-3.11.1.tgz#8e4665871fff1ef64c0430d1a2c9d084d9721881"
@ -27128,6 +27353,11 @@ web-streams-polyfill@^3.1.0:
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965"
integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==
web-worker@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da"
integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==
web3-core@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.0.tgz#96c5e8f7ec92ea573f388785548bf31c4089d508"
@ -27670,6 +27900,11 @@ word-wrap@^1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f"
integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==
word-wrap@~1.2.3:
version "1.2.5"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"

Loading…
Cancel
Save