Merge branch 'master' into desktope2e-remixai

pull/5100/head
STetsing 2 months ago committed by GitHub
commit 1c5c35403b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/pr-reminder.yml
  2. 31
      apps/circuit-compiler/src/app/actions/index.ts
  3. 2
      apps/circuit-compiler/src/app/components/container.tsx
  4. 25
      apps/circuit-compiler/src/app/components/witness.tsx
  5. 7
      apps/circuit-compiler/src/app/reducers/state.ts
  6. 7
      apps/circuit-compiler/src/app/services/circomPluginClient.ts
  7. 2
      apps/circuit-compiler/src/app/types/index.ts
  8. 2
      apps/etherscan/src/app/views/VerifyView.tsx
  9. 10
      apps/remix-ide-e2e/src/githttpbackend/package.json
  10. 0
      apps/remix-ide-e2e/src/githttpbackend/src/server.ts
  11. 25
      apps/remix-ide-e2e/src/githttpbackend/tsconfig.json
  12. 122
      apps/remix-ide-e2e/src/githttpbackend/yarn.lock
  13. 2
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  14. 120
      apps/remix-ide-e2e/src/tests/dgit_github.test.ts
  15. 6
      apps/remix-ide-e2e/src/tests/dgit_local.test.ts
  16. 2
      apps/remix-ide-e2e/src/tests/editorHoverContext.test.ts
  17. 35
      apps/remix-ide-e2e/src/tests/eip1153.test.ts
  18. 2
      apps/remix-ide-e2e/src/tests/erc721.test.ts
  19. 27
      apps/remix-ide-e2e/src/tests/file_explorer_multiselect.test.ts
  20. 14
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  21. 12
      apps/remix-ide-e2e/src/tests/vyper_api.test.ts
  22. 15
      apps/remix-ide-e2e/src/tests/workspace.test.ts
  23. 12
      apps/remix-ide-e2e/src/tests/workspace_git.test.ts
  24. 32
      apps/remix-ide/ci/downloadsoljson.sh
  25. 7
      apps/remix-ide/src/app/components/vertical-icons.tsx
  26. 634
      apps/remix-ide/src/app/files/dgitProvider.ts
  27. 4
      apps/remix-ide/src/app/panels/file-panel.js
  28. 37
      apps/remix-ide/src/app/panels/layout.ts
  29. 13
      apps/remix-ide/src/app/plugins/electron/foundryPlugin.ts
  30. 13
      apps/remix-ide/src/app/plugins/electron/hardhatPlugin.ts
  31. 2
      apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts
  32. 23
      apps/remix-ide/src/app/plugins/templates-selection/templates-selection-plugin.tsx
  33. 292
      apps/remix-ide/src/app/plugins/templates-selection/templates.ts
  34. 42
      apps/remix-ide/src/app/providers/environment-explorer.tsx
  35. 2
      apps/remix-ide/src/app/providers/injected-custom-provider.tsx
  36. 2
      apps/remix-ide/src/app/providers/injected-ephemery-testnet-provider.tsx
  37. 6
      apps/remix-ide/src/app/tabs/compile-tab.js
  38. 2
      apps/remix-ide/src/app/tabs/debugger-tab.js
  39. 1
      apps/remix-ide/src/app/tabs/locales/en/circuit.json
  40. 4
      apps/remix-ide/src/app/tabs/locales/en/electron.json
  41. 10
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  42. 3
      apps/remix-ide/src/app/tabs/locales/en/git.json
  43. 3
      apps/remix-ide/src/app/tabs/locales/en/gitui.json
  44. 7
      apps/remix-ide/src/app/tabs/locales/en/home.json
  45. 6
      apps/remix-ide/src/app/tabs/locales/en/remixUiTabs.json
  46. 18
      apps/remix-ide/src/app/tabs/locales/en/settings.json
  47. 9
      apps/remix-ide/src/app/udapp/run-tab.js
  48. BIN
      apps/remix-ide/src/assets/img/remi-prof.webp
  49. 5
      apps/remix-ide/src/blockchain/blockchain.tsx
  50. 2
      apps/remix-ide/src/blockchain/execution-context.js
  51. 2
      apps/remix-ide/src/blockchain/providers/injected.ts
  52. 2
      apps/remix-ide/src/blockchain/providers/node.ts
  53. 2
      apps/remix-ide/src/blockchain/providers/vm.ts
  54. 14
      apps/remixdesktop/after-pack.js
  55. 12
      apps/remixdesktop/esbuild.js
  56. 14
      apps/remixdesktop/run_git_ui_isogit_tests.sh
  57. 24
      apps/remixdesktop/rundist_esbuild.bash
  58. 24
      apps/remixdesktop/rundist_tsc.bash
  59. 24
      apps/remixdesktop/rundist_webpack.bash
  60. 248
      apps/remixdesktop/src/plugins/foundryPlugin.ts
  61. 220
      apps/remixdesktop/src/plugins/hardhatPlugin.ts
  62. 9
      apps/remixdesktop/src/types/index.ts
  63. 195
      apps/remixdesktop/test/lib/git.ts
  64. 157
      apps/remixdesktop/test/tests/app/foundry.test.ts
  65. 203
      apps/remixdesktop/test/tests/app/git-ui.test.ts
  66. 181
      apps/remixdesktop/test/tests/app/git-ui_2.test.ts
  67. 153
      apps/remixdesktop/test/tests/app/git-ui_3.test.ts
  68. 200
      apps/remixdesktop/test/tests/app/git-ui_4.test.ts
  69. 255
      apps/remixdesktop/test/tests/app/github.test.ts
  70. 190
      apps/remixdesktop/test/tests/app/github_2.test.ts
  71. 180
      apps/remixdesktop/test/tests/app/github_3.test.ts
  72. 90
      apps/remixdesktop/test/tests/app/hardhat.test.ts
  73. 4
      apps/solidity-compiler/src/app/compiler.ts
  74. 8
      libs/ghaction-helper/package.json
  75. 2
      libs/ghaction-helper/src/methods.ts
  76. 10
      libs/remix-analyzer/package.json
  77. 1
      libs/remix-api/src/index.ts
  78. 2
      libs/remix-api/src/lib/plugins/filePanel-api.ts
  79. 5
      libs/remix-api/src/lib/plugins/fileSystem-api.ts
  80. 13
      libs/remix-api/src/lib/plugins/fs-api.ts
  81. 43
      libs/remix-api/src/lib/plugins/git-api.ts
  82. 10
      libs/remix-api/src/lib/plugins/terminal-api.ts
  83. 8
      libs/remix-api/src/lib/remix-api.ts
  84. 206
      libs/remix-api/src/lib/types/git.ts
  85. 8
      libs/remix-astwalker/package.json
  86. 14
      libs/remix-debug/package.json
  87. 2
      libs/remix-debug/src/cmdline/index.ts
  88. 2
      libs/remix-debug/src/init.ts
  89. 1
      libs/remix-git/index.ts
  90. 345
      libs/remix-git/src/isogit.ts
  91. 4
      libs/remix-lib/package.json
  92. 9
      libs/remix-lib/src/execution/txExecution.ts
  93. 14
      libs/remix-lib/src/execution/txRunner.ts
  94. 73
      libs/remix-lib/src/execution/txRunnerVM.ts
  95. 2
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  96. 1
      libs/remix-lib/src/index.ts
  97. 2
      libs/remix-lib/src/init.ts
  98. 1
      libs/remix-lib/src/types/ICompilerApi.ts
  99. 8
      libs/remix-simulator/package.json
  100. 24
      libs/remix-simulator/src/methods/transactions.ts
  101. Some files were not shown because too many files have changed in this diff Show More

@ -14,4 +14,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }} webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
freeze-date: '2024-09-09T18:00:00Z' freeze-date: '2024-10-07T18:00:00Z'

@ -3,7 +3,6 @@ import type { CircomPluginClient } from "../services/circomPluginClient"
import { ActionPayloadTypes, AppState, ICircuitAppContext } from "../types" import { ActionPayloadTypes, AppState, ICircuitAppContext } from "../types"
import { GROTH16_VERIFIER, PLONK_VERIFIER } from './constant' import { GROTH16_VERIFIER, PLONK_VERIFIER } from './constant'
import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper' import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper'
import { ethers } from 'ethers'
export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState) => { export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState) => {
try { try {
@ -19,12 +18,21 @@ export const compileCircuit = async (plugin: CircomPluginClient, appState: AppSt
} }
} }
export const computeWitness = async (plugin: CircomPluginClient, status: string, witnessValues: Record<string, string>) => { export const computeWitness = async (plugin: CircomPluginClient, appState: AppState, dispatch: ICircuitAppContext['dispatch'], status: string, witnessValues: Record<string, string>) => {
try { try {
if (status !== "computing") { if (status !== "computing") {
const input = JSON.stringify(witnessValues) const input = JSON.stringify(witnessValues)
const witness = await plugin.computeWitness(input)
await plugin.computeWitness(input) if (appState.exportWtnsJson) {
const wtns = await snarkjs.wtns.exportJson(witness)
const wtnsJson = wtns.map(wtn => wtn.toString())
const fileName = extractNameFromKey(appState.filePath)
const writePath = extractParentFromKey(appState.filePath) + `/.bin/${fileName.replace('.circom', '.wtn.json')}`
await plugin.call('fileManager', 'writeFile', writePath, JSON.stringify(wtnsJson, null, 2))
plugin._paq.push(['trackEvent', 'circuit-compiler', 'computeWitness', 'wtns.exportJson', writePath])
}
} else { } else {
console.log('Existing witness computation in progress') console.log('Existing witness computation in progress')
} }
@ -38,6 +46,8 @@ export const computeWitness = async (plugin: CircomPluginClient, status: string,
export const runSetupAndExport = async (plugin: CircomPluginClient, appState: AppState, dispatch: ICircuitAppContext['dispatch']) => { export const runSetupAndExport = async (plugin: CircomPluginClient, appState: AppState, dispatch: ICircuitAppContext['dispatch']) => {
try { try {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'exporting' }) dispatch({ type: 'SET_COMPILER_STATUS', payload: 'exporting' })
dispatch({ type: 'SET_SETUP_EXPORT_FEEDBACK', payload: null })
plugin.emit('statusChanged', { key: 'none' })
const ptau_final = `https://ipfs-cluster.ethdevops.io/ipfs/${appState.ptauList.find(ptau => ptau.name === appState.ptauValue)?.ipfsHash}` const ptau_final = `https://ipfs-cluster.ethdevops.io/ipfs/${appState.ptauList.find(ptau => ptau.name === appState.ptauValue)?.ipfsHash}`
await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue }) await plugin.generateR1cs(appState.filePath, { version: appState.version, prime: appState.primeValue })
@ -50,32 +60,38 @@ export const runSetupAndExport = async (plugin: CircomPluginClient, appState: Ap
const zkey_final = { type: "mem" } const zkey_final = { type: "mem" }
if (appState.provingScheme === 'groth16') { if (appState.provingScheme === 'groth16') {
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'provingScheme', 'groth16'])
await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK')) await snarkjs.zKey.newZKey(r1cs, ptau_final, zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK')) const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
if (appState.exportVerificationKey) { if (appState.exportVerificationKey) {
await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2)) await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2))
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'zKey.exportVerificationKey', `${extractParentFromKey(appState.filePath)}/groth16/zk/keys/verification_key.json`])
} }
if (appState.exportVerificationContract) { if (appState.exportVerificationContract) {
const templates = { groth16: GROTH16_VERIFIER } const templates = { groth16: GROTH16_VERIFIER }
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK')) const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/zk_verifier.sol`, solidityContract) await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/zk_verifier.sol`, solidityContract)
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'zKey.exportSolidityVerifier', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/zk_verifier.sol`])
} }
dispatch({ type: 'SET_ZKEY', payload: zkey_final }) dispatch({ type: 'SET_ZKEY', payload: zkey_final })
dispatch({ type: 'SET_VERIFICATION_KEY', payload: vKey }) dispatch({ type: 'SET_VERIFICATION_KEY', payload: vKey })
} else if (appState.provingScheme === 'plonk') { } else if (appState.provingScheme === 'plonk') {
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'provingScheme', 'plonk'])
await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK')) await snarkjs.plonk.setup(r1cs, ptau_final, zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK')) const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
if (appState.exportVerificationKey) { if (appState.exportVerificationKey) {
await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2)) await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/keys/verification_key.json`, JSON.stringify(vKey, null, 2))
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'zKey.exportVerificationKey', `${extractParentFromKey(appState.filePath)}/plonk/zk/keys/verification_key.json`])
} }
if (appState.exportVerificationContract) { if (appState.exportVerificationContract) {
const templates = { plonk: PLONK_VERIFIER } const templates = { plonk: PLONK_VERIFIER }
const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK')) const solidityContract = await snarkjs.zKey.exportSolidityVerifier(zkey_final, templates, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/zk_verifier.sol`, solidityContract) await plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/zk_verifier.sol`, solidityContract)
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'zKey.exportSolidityVerifier', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/zk_verifier.sol`])
} }
dispatch({ type: 'SET_ZKEY', payload: zkey_final }) dispatch({ type: 'SET_ZKEY', payload: zkey_final })
dispatch({ type: 'SET_VERIFICATION_KEY', payload: vKey }) dispatch({ type: 'SET_VERIFICATION_KEY', payload: vKey })
@ -83,6 +99,7 @@ export const runSetupAndExport = async (plugin: CircomPluginClient, appState: Ap
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })
dispatch({ type: 'SET_SETUP_EXPORT_STATUS', payload: 'done' }) dispatch({ type: 'SET_SETUP_EXPORT_STATUS', payload: 'done' })
} catch (e) { } catch (e) {
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'error', e.message])
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' }) dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' })
console.error(e) console.error(e)
} }
@ -91,6 +108,8 @@ export const runSetupAndExport = async (plugin: CircomPluginClient, appState: Ap
export const generateProof = async (plugin: CircomPluginClient, appState: AppState, dispatch: ICircuitAppContext['dispatch']) => { export const generateProof = async (plugin: CircomPluginClient, appState: AppState, dispatch: ICircuitAppContext['dispatch']) => {
try { try {
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'proving' }) dispatch({ type: 'SET_COMPILER_STATUS', payload: 'proving' })
dispatch({ type: 'SET_PROOF_FEEDBACK', payload: null })
plugin.emit('statusChanged', { key: 'none' })
const fileName = extractNameFromKey(appState.filePath) const fileName = extractNameFromKey(appState.filePath)
const r1csPath = extractParentFromKey(appState.filePath) + `/.bin/${fileName.replace('.circom', '.r1cs')}` const r1csPath = extractParentFromKey(appState.filePath) + `/.bin/${fileName.replace('.circom', '.r1cs')}`
// @ts-ignore // @ts-ignore
@ -110,21 +129,27 @@ export const generateProof = async (plugin: CircomPluginClient, appState: AppSta
const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK')) const { proof, publicSignals } = await snarkjs.groth16.prove(zkey_final, wtns, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK'))
const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK')) const verified = await snarkjs.groth16.verify(vKey, publicSignals, proof, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK'))
plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/proof.json`, JSON.stringify(proof, null, 2))
plugin.call('terminal', 'log', { type: 'log', value: 'zk proof validity ' + verified }) plugin.call('terminal', 'log', { type: 'log', value: 'zk proof validity ' + verified })
plugin._paq.push(['trackEvent', 'circuit-compiler', 'generateProof', 'groth16.prove', verified])
if (appState.exportVerifierCalldata) { if (appState.exportVerifierCalldata) {
const calldata = await snarkjs.groth16.exportSolidityCallData(proof, publicSignals) const calldata = await snarkjs.groth16.exportSolidityCallData(proof, publicSignals)
plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/verifierCalldata.json`, calldata) plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/verifierCalldata.json`, calldata)
plugin._paq.push(['trackEvent', 'circuit-compiler', 'generateProof', 'groth16.exportSolidityCallData', `${extractParentFromKey(appState.filePath)}/groth16/zk/build/verifierCalldata.json`])
} }
} else if (appState.provingScheme === 'plonk') { } else if (appState.provingScheme === 'plonk') {
const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK')) const { proof, publicSignals } = await snarkjs.plonk.prove(zkey_final, wtns, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK'))
const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK')) const verified = await snarkjs.plonk.verify(vKey, publicSignals, proof, zkLogger(plugin, dispatch, 'SET_PROOF_FEEDBACK'))
plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/proof.json`, JSON.stringify(proof, null, 2))
plugin.call('terminal', 'log', { type: 'log', value: 'zk proof validity ' + verified }) plugin.call('terminal', 'log', { type: 'log', value: 'zk proof validity ' + verified })
plugin._paq.push(['trackEvent', 'circuit-compiler', 'generateProof', 'plonk.prove', verified])
if (appState.exportVerifierCalldata) { if (appState.exportVerifierCalldata) {
const calldata = await snarkjs.plonk.exportSolidityCallData(proof, publicSignals) const calldata = await snarkjs.plonk.exportSolidityCallData(proof, publicSignals)
plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/verifierCalldata.json`, calldata) plugin.call('fileManager', 'writeFile', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/verifierCalldata.json`, calldata)
plugin._paq.push(['trackEvent', 'circuit-compiler', 'generateProof', 'plonk.exportSolidityCallData', `${extractParentFromKey(appState.filePath)}/plonk/zk/build/verifierCalldata.json`])
} }
} }
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' }) dispatch({ type: 'SET_COMPILER_STATUS', payload: 'idle' })

@ -140,7 +140,7 @@ export function Container () {
<RenderIf condition={circuitApp.appState.signalInputs.length > 0}> <RenderIf condition={circuitApp.appState.signalInputs.length > 0}>
<Toggler title='circuit.computeWitness' dataId='witness_toggler' show={!!circuitApp.appState.setupExportStatus}> <Toggler title='circuit.computeWitness' dataId='witness_toggler' show={!!circuitApp.appState.setupExportStatus}>
<> <>
<WitnessSection plugin={circuitApp.plugin} signalInputs={circuitApp.appState.signalInputs} status={circuitApp.appState.status} /> <WitnessSection />
<RenderIf condition={circuitApp.appState.status !== 'computing'}> <RenderIf condition={circuitApp.appState.status !== 'computing'}>
<CompilerFeedback feedback={circuitApp.appState.computeFeedback} filePathToId={circuitApp.appState.filePathToId} openErrorLocation={handleOpenErrorLocation} hideWarnings={circuitApp.appState.hideWarnings} askGPT={askGPT} /> <CompilerFeedback feedback={circuitApp.appState.computeFeedback} filePathToId={circuitApp.appState.filePathToId} openErrorLocation={handleOpenErrorLocation} hideWarnings={circuitApp.appState.hideWarnings} askGPT={askGPT} />
</RenderIf> </RenderIf>

@ -1,12 +1,14 @@
import { RenderIf, RenderIfNot } from "@remix-ui/helper"; import { RenderIf, RenderIfNot } from "@remix-ui/helper";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { CompilerStatus } from "../types";
import { computeWitness } from "../actions"; import { computeWitness } from "../actions";
import { useState } from "react"; import { useContext, useState } from "react";
import type { CircomPluginClient } from "../services/circomPluginClient";
import * as remixLib from '@remix-project/remix-lib' import * as remixLib from '@remix-project/remix-lib'
import { CircuitAppContext } from "../contexts";
export function WitnessSection ({ plugin, signalInputs, status }: {plugin: CircomPluginClient, signalInputs: string[], status: CompilerStatus}) { export function WitnessSection () {
const circuitApp = useContext(CircuitAppContext)
const { signalInputs, status, exportWtnsJson } = circuitApp.appState
const { plugin, dispatch, appState } = circuitApp
const [witnessValues, setWitnessValues] = useState<Record<string, string>>({}) const [witnessValues, setWitnessValues] = useState<Record<string, string>>({})
const handleSignalInput = (e: any) => { const handleSignalInput = (e: any) => {
@ -47,9 +49,22 @@ export function WitnessSection ({ plugin, signalInputs, status }: {plugin: Circo
</div> </div>
)) ))
} }
<div className="custom-control custom-checkbox">
<input
className="custom-control-input"
type="checkbox"
title="Export Witness As JSON"
id="circuitExportWtnsJson"
onChange={() => { dispatch({ type: 'SET_EXPORT_WTNS_JSON', payload: !exportWtnsJson }) }}
checked={exportWtnsJson}
/>
<label className="form-check-label custom-control-label pt-1" htmlFor="circuitExportWtnsJson">
<FormattedMessage id="circuit.exportWtnsJson" />
</label>
</div>
<button <button
className="btn btn-secondary btn-block d-block w-100 text-break mb-1 mt-1" className="btn btn-secondary btn-block d-block w-100 text-break mb-1 mt-1"
onClick={() => { computeWitness(plugin, status, witnessValues) }} onClick={() => { computeWitness(plugin, appState, dispatch, status, witnessValues) }}
disabled={(status === "compiling") || (status === "computing")} disabled={(status === "compiling") || (status === "computing")}
data-id="compute_witness_btn" data-id="compute_witness_btn"
> >

@ -23,6 +23,7 @@ export const appInitialState: AppState = {
exportVerificationContract: true, exportVerificationContract: true,
exportVerificationKey: true, exportVerificationKey: true,
exportVerifierCalldata: true, exportVerifierCalldata: true,
exportWtnsJson: false,
verificationKey: null, verificationKey: null,
zKey: null zKey: null
} }
@ -138,6 +139,12 @@ export const appReducer = (state = appInitialState, action: Actions): AppState =
setupExportStatus: action.payload setupExportStatus: action.payload
} }
case 'SET_EXPORT_WTNS_JSON':
return {
...state,
exportWtnsJson: action.payload
}
case 'SET_VERIFICATION_KEY': case 'SET_VERIFICATION_KEY':
return { return {
...state, ...state,

@ -20,7 +20,7 @@ export class CircomPluginClient extends PluginClient {
private lastParsedFiles: Record<string, string> = {} private lastParsedFiles: Record<string, string> = {}
private lastCompiledFile: string = '' private lastCompiledFile: string = ''
private compiler: typeof compilerV215 & typeof compilerV216 & typeof compilerV217 & typeof compilerV218 private compiler: typeof compilerV215 & typeof compilerV216 & typeof compilerV217 & typeof compilerV218
private _paq = { public _paq = {
push: (args) => { push: (args) => {
this.call('matomo' as any, 'track', args) this.call('matomo' as any, 'track', args)
} }
@ -237,7 +237,7 @@ export class CircomPluginClient extends PluginClient {
} }
} }
async computeWitness (input: string): Promise<void> { async computeWitness (input: string): Promise<Uint8Array> {
this.internalEvents.emit('circuit_computing_witness_start') this.internalEvents.emit('circuit_computing_witness_start')
this.emit('statusChanged', { key: 'loading', title: 'Computing...', type: 'info' }) this.emit('statusChanged', { key: 'loading', title: 'Computing...', type: 'info' })
const wasmPath = this.lastCompiledCircuitPath const wasmPath = this.lastCompiledCircuitPath
@ -249,9 +249,10 @@ export class CircomPluginClient extends PluginClient {
const witness = this.compiler ? await this.compiler.generate_witness(dataRead, input) : await generate_witness(dataRead, input) const witness = this.compiler ? await this.compiler.generate_witness(dataRead, input) : await generate_witness(dataRead, input)
// @ts-ignore // @ts-ignore
await this.call('fileManager', 'writeFile', wasmPath.replace('.wasm', '.wtn'), witness, true) await this.call('fileManager', 'writeFile', wasmPath.replace('.wasm', '.wtn'), witness, true)
this._paq.push(['trackEvent', 'circuit-compiler', 'computeWitness', 'Witness computing successful']) this._paq.push(['trackEvent', 'circuit-compiler', 'computeWitness', 'compiler.generate_witness', wasmPath.replace('.wasm', '.wtn')])
this.internalEvents.emit('circuit_computing_witness_done') this.internalEvents.emit('circuit_computing_witness_done')
this.emit('statusChanged', { key: 'succeed', title: 'witness computed successfully', type: 'success' }) this.emit('statusChanged', { key: 'succeed', title: 'witness computed successfully', type: 'success' })
return witness
} }
async resolveDependencies(filePath: string, fileContent: string, output?: Record<string, string>, depPath: string = '', blackPath: string[] = []): Promise<Record<string, string>> { async resolveDependencies(filePath: string, fileContent: string, output?: Record<string, string>, depPath: string = '', blackPath: string[] = []): Promise<Record<string, string>> {

@ -39,6 +39,7 @@ export interface ActionPayloadTypes {
SET_EXPORT_VERIFICATION_CONTRACT: boolean, SET_EXPORT_VERIFICATION_CONTRACT: boolean,
SET_EXPORT_VERIFICATION_KEY: boolean, SET_EXPORT_VERIFICATION_KEY: boolean,
SET_EXPORT_VERIFIER_CALLDATA: boolean, SET_EXPORT_VERIFIER_CALLDATA: boolean,
SET_EXPORT_WTNS_JSON: boolean,
SET_SETUP_EXPORT_STATUS: SetupExportStatus, SET_SETUP_EXPORT_STATUS: SetupExportStatus,
SET_VERIFICATION_KEY: Record<string, any>, SET_VERIFICATION_KEY: Record<string, any>,
SET_ZKEY: any SET_ZKEY: any
@ -71,6 +72,7 @@ export interface AppState {
exportVerificationContract: boolean, exportVerificationContract: boolean,
exportVerificationKey: boolean, exportVerificationKey: boolean,
exportVerifierCalldata: boolean, exportVerifierCalldata: boolean,
exportWtnsJson: boolean,
verificationKey: Record<string, any>, verificationKey: Record<string, any>,
zKey: Uint8Array zKey: Uint8Array
} }

@ -1,5 +1,5 @@
import React, {useEffect, useRef, useState} from 'react' import React, {useEffect, useRef, useState} from 'react'
import Web3 from 'web3' import { Web3 } from 'web3'
import {PluginClient} from '@remixproject/plugin' import {PluginClient} from '@remixproject/plugin'
import {CustomTooltip} from '@remix-ui/helper' import {CustomTooltip} from '@remix-ui/helper'

@ -1,13 +1,17 @@
{ {
"scripts": { "scripts": {
"start:server": "npx ts-node server.ts" "start:server": "tsc && node ./dist/server.js"
}, },
"dependencies": { "dependencies": {
"body-parser": "^1.20.2", "body-parser": "^1.20.3",
"child_process": "^1.0.2", "child_process": "^1.0.2",
"express": "^4.19.2", "express": "^4.20.0",
"git-http-backend": "^1.1.2", "git-http-backend": "^1.1.2",
"path": "^0.12.7", "path": "^0.12.7",
"zlib": "^1.0.5" "zlib": "^1.0.5"
},
"devDependencies": {
"@types/node": "^22.5.4",
"typescript": "^5.6.2"
} }
} }

@ -0,0 +1,25 @@
{
"compilerOptions": {
"target": "ES6", // Set the ECMAScript target version
"module": "commonjs", // Specify module code generation
"strict": true,
"noImplicitAny": false, // Enable all strict type-checking options
"esModuleInterop": true, // Emit additional code to make commonJS and ES modules work together
"skipLibCheck": true, // Skip type checking of all declaration files (.d.ts)
"forceConsistentCasingInFileNames": true, // Ensure file names are treated with case sensitivity
"outDir": "./dist", // Redirect output structure to the 'dist' directory
"rootDir": "./src", // Specify the root directory of input files
"sourceMap": true, // Create source map files
"types": [
"node"
], // Add node types
"moduleResolution": "node", // Ensure TypeScript resolves modules like Node.js
},
"include": [
"src/**/*" // Include all TypeScript files in the src directory
],
"exclude": [
"node_modules", // Exclude the node_modules folder
"**/*.test.ts" // Exclude test files
]
}

@ -2,6 +2,13 @@
# yarn lockfile v1 # yarn lockfile v1
"@types/node@^22.5.4":
version "22.5.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.4.tgz#83f7d1f65bc2ed223bdbf57c7884f1d5a4fa84e8"
integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==
dependencies:
undici-types "~6.19.2"
accepts@~1.3.8: accepts@~1.3.8:
version "1.3.8" version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
@ -15,10 +22,10 @@ array-flatten@1.1.1:
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
body-parser@1.20.2, body-parser@^1.20.2: body-parser@1.20.3, body-parser@^1.20.2:
version "1.20.2" version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies: dependencies:
bytes "3.1.2" bytes "3.1.2"
content-type "~1.0.5" content-type "~1.0.5"
@ -28,7 +35,25 @@ body-parser@1.20.2, body-parser@^1.20.2:
http-errors "2.0.0" http-errors "2.0.0"
iconv-lite "0.4.24" iconv-lite "0.4.24"
on-finished "2.4.1" on-finished "2.4.1"
qs "6.11.0" qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
body-parser@^1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.13.0"
raw-body "2.5.2" raw-body "2.5.2"
type-is "~1.6.18" type-is "~1.6.18"
unpipe "1.0.0" unpipe "1.0.0"
@ -112,6 +137,11 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
encodeurl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
es-define-property@^1.0.0: es-define-property@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
@ -134,37 +164,37 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^4.19.2: express@^4.20.0:
version "4.19.2" version "4.20.0"
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
dependencies: dependencies:
accepts "~1.3.8" accepts "~1.3.8"
array-flatten "1.1.1" array-flatten "1.1.1"
body-parser "1.20.2" body-parser "1.20.3"
content-disposition "0.5.4" content-disposition "0.5.4"
content-type "~1.0.4" content-type "~1.0.4"
cookie "0.6.0" cookie "0.6.0"
cookie-signature "1.0.6" cookie-signature "1.0.6"
debug "2.6.9" debug "2.6.9"
depd "2.0.0" depd "2.0.0"
encodeurl "~1.0.2" encodeurl "~2.0.0"
escape-html "~1.0.3" escape-html "~1.0.3"
etag "~1.8.1" etag "~1.8.1"
finalhandler "1.2.0" finalhandler "1.2.0"
fresh "0.5.2" fresh "0.5.2"
http-errors "2.0.0" http-errors "2.0.0"
merge-descriptors "1.0.1" merge-descriptors "1.0.3"
methods "~1.1.2" methods "~1.1.2"
on-finished "2.4.1" on-finished "2.4.1"
parseurl "~1.3.3" parseurl "~1.3.3"
path-to-regexp "0.1.7" path-to-regexp "0.1.10"
proxy-addr "~2.0.7" proxy-addr "~2.0.7"
qs "6.11.0" qs "6.11.0"
range-parser "~1.2.1" range-parser "~1.2.1"
safe-buffer "5.2.1" safe-buffer "5.2.1"
send "0.18.0" send "0.19.0"
serve-static "1.15.0" serve-static "1.16.0"
setprototypeof "1.2.0" setprototypeof "1.2.0"
statuses "2.0.1" statuses "2.0.1"
type-is "~1.6.18" type-is "~1.6.18"
@ -292,10 +322,10 @@ media-typer@0.3.0:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
merge-descriptors@1.0.1: merge-descriptors@1.0.3:
version "1.0.1" version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~1.1.2: methods@~1.1.2:
version "1.1.2" version "1.1.2"
@ -351,10 +381,10 @@ parseurl@~1.3.3:
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.7: path-to-regexp@0.1.10:
version "0.1.7" version "0.1.10"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path@^0.12.7: path@^0.12.7:
version "0.12.7" version "0.12.7"
@ -384,6 +414,13 @@ qs@6.11.0:
dependencies: dependencies:
side-channel "^1.0.4" side-channel "^1.0.4"
qs@6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
side-channel "^1.0.6"
range-parser@~1.2.1: range-parser@~1.2.1:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@ -428,10 +465,29 @@ send@0.18.0:
range-parser "~1.2.1" range-parser "~1.2.1"
statuses "2.0.1" statuses "2.0.1"
serve-static@1.15.0: send@0.19.0:
version "1.15.0" version "0.19.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.16.0:
version "1.16.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
dependencies: dependencies:
encodeurl "~1.0.2" encodeurl "~1.0.2"
escape-html "~1.0.3" escape-html "~1.0.3"
@ -455,7 +511,7 @@ setprototypeof@1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel@^1.0.4: side-channel@^1.0.4, side-channel@^1.0.6:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
@ -483,6 +539,16 @@ type-is@~1.6.18:
media-typer "0.3.0" media-typer "0.3.0"
mime-types "~2.1.24" mime-types "~2.1.24"
typescript@^5.6.2:
version "5.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==
undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
unpipe@1.0.0, unpipe@~1.0.0: unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"

@ -102,6 +102,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_remix_default') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_remix_default')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_remix_default' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(1000) .pause(1000)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')

@ -16,22 +16,45 @@ module.exports = {
'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) { 'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) {
browser. browser.
clickLaunchIcon('dgit') clickLaunchIcon('dgit')
.pause(1000)
.waitForElementVisible('*[data-id="initgit-btn"]') .waitForElementVisible('*[data-id="initgit-btn"]')
.click('*[data-id="initgit-btn"]') .click('*[data-id="initgit-btn"]')
.waitForElementNotPresent('*[data-id="initgit-btn"]')
},
'launch github login via FE #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.pause(1000)
.waitForElementVisible('*[data-id="filepanel-login-github"]')
.click('*[data-id="filepanel-login-github"]')
},
'login to github #group1 #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="gitubUsername"]')
.setValue('*[data-id="githubToken"]', process.env.dgit_token) .setValue('*[data-id="githubToken"]', process.env.dgit_token)
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git') .setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com') .setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]') .click('*[data-id="saveGitHubCredentials"]')
}, },
'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) { 'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) {
browser. browser
click('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="connected-as-bunsenstraat"]') .waitForElementVisible('*[data-id="connected-as-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-img-bunsenstraat"]') .waitForElementVisible('*[data-id="connected-img-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-link-bunsenstraat"]') .waitForElementVisible('*[data-id="connected-link-bunsenstraat"]')
.waitForElementVisible('*[data-id="remotes-panel"]')
},
'check the FE for the auth user #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="filepanel-connected-img-bunsenstraat"]')
}, },
'clone a repository #group1': function (browser: NightwatchBrowser) { 'clone a repository #group1': function (browser: NightwatchBrowser) {
browser browser
.clickLaunchIcon('dgit')
.click('*[data-id="clone-panel"]') .click('*[data-id="clone-panel"]')
.click({ .click({
selector: '//*[@data-id="clone-panel-content"]//*[@data-id="fetch-repositories"]', selector: '//*[@data-id="clone-panel-content"]//*[@data-id="fetch-repositories"]',
@ -101,6 +124,7 @@ module.exports = {
.click('*[data-id="remotes-panel"]') .click('*[data-id="remotes-panel"]')
.waitForElementVisible('*[data-id="remotes-panel-content"]') .waitForElementVisible('*[data-id="remotes-panel-content"]')
.pause(2000)
.waitForElementVisible({ .waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-origin-default"]', selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-origin-default"]',
locateStrategy: 'xpath' locateStrategy: 'xpath'
@ -191,10 +215,27 @@ module.exports = {
locateStrategy: 'xpath' locateStrategy: 'xpath'
}) })
}, },
'disconnect github #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="disconnect-github"]')
.pause(1000)
.click('*[data-id="disconnect-github"]')
.waitForElementNotPresent('*[data-id="connected-as-bunsenstraat"]')
},
'check the FE for the disconnected auth user #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementNotPresent('*[data-id="filepanel-connected-img-bunsenstraat"]')
.waitForElementVisible('*[data-id="filepanel-login-github"]')
},
'add a remote #group2': function (browser: NightwatchBrowser) { 'add a remote #group2': function (browser: NightwatchBrowser) {
browser browser
.pause(1000) .pause(1000)
.clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="remotes-panel"]')
.click('*[data-id="remotes-panel"]') .click('*[data-id="remotes-panel"]')
.click({ .click({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="fetch-repositories"]', selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="fetch-repositories"]',
@ -253,6 +294,7 @@ module.exports = {
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]", selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]",
locateStrategy: 'xpath' locateStrategy: 'xpath'
}) })
.pause(1000)
.getAttribute({ .getAttribute({
selector: '//*[@data-id="sourcecontrol-pull"]', selector: '//*[@data-id="sourcecontrol-pull"]',
locateStrategy: 'xpath' locateStrategy: 'xpath'
@ -310,4 +352,76 @@ module.exports = {
} }
}) })
}, },
// pagination test
'clone repo #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="clone-panel"]')
.click('*[data-id="clone-panel"]')
.waitForElementVisible('*[data-id="clone-url"]')
.setValue('*[data-id="clone-url"]', 'https://github.com/ethereum/awesome-remix')
.waitForElementVisible('*[data-id="clone-branch"]')
.setValue('*[data-id="clone-branch"]', 'master')
.waitForElementVisible('*[data-id="clone-btn"]')
.click('*[data-id="clone-btn"]')
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]')
},
'Update settings for git #group3': function (browser: NightwatchBrowser) {
browser.
clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.pause(1000)
.setValue('*[data-id="githubToken"]', 'invalidtoken')
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
.pause(1000)
.modalFooterOKClick('github-credentials-error')
},
'check the commits panel for pagination #group3': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="commits-panel"]')
.click('*[data-id="commits-panel"]')
.elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) {
console.log('Number of commit-summary elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length == 1)
})
},
'load more commits #group3': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="load-more-commits"]')
.click('*[data-id="load-more-commits"]')
.waitForElementVisible('*[data-id="loader-indicator"]')
.waitForElementNotPresent('*[data-id="loader-indicator"]')
.elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) {
console.log('Number of commit-summary elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length > 2)
})
},
'load more branches from remote #group3': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="branches-panel"]')
.waitForElementVisible({
selector: '//*[@data-id="branches-panel-content-remote-branches"]',
locateStrategy: 'xpath'
})
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) {
console.log('Number of branches elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length == 1)
})
.waitForElementVisible('*[data-id="remote-sync-origin"]')
.click('*[data-id="remote-sync-origin"]')
.waitForElementVisible('*[data-id="loader-indicator"]')
.waitForElementNotPresent('*[data-id="loader-indicator"]')
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) {
console.log('Number of branches elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length > 2)
})
}
} }

@ -36,6 +36,10 @@ module.exports = {
clickLaunchIcon('dgit') clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="initgit-btn"]') .waitForElementVisible('*[data-id="initgit-btn"]')
.click('*[data-id="initgit-btn"]') .click('*[data-id="initgit-btn"]')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="gitubUsername"]')
.setValue('*[data-id="gitubUsername"]', 'git') .setValue('*[data-id="gitubUsername"]', 'git')
.setValue('*[data-id="githubEmail"]', 'git@example.com') .setValue('*[data-id="githubEmail"]', 'git@example.com')
.click('*[data-id="saveGitHubCredentials"]') .click('*[data-id="saveGitHubCredentials"]')
@ -603,7 +607,7 @@ async function createCommitOnLocalServer(path: string, message: string) {
async function spawnGitServer(path: string): Promise<ChildProcess> { async function spawnGitServer(path: string): Promise<ChildProcess> {
console.log(process.cwd()) console.log(process.cwd())
try { try {
const server = spawn('yarn && sh setup.sh && npx ts-node server.ts', [`${path}`], { cwd: process.cwd() + '/apps/remix-ide-e2e/src/githttpbackend/', shell: true, detached: true }) const server = spawn('yarn && sh setup.sh && yarn start:server', [`${path}`], { cwd: process.cwd() + '/apps/remix-ide-e2e/src/githttpbackend/', shell: true, detached: true })
console.log('spawned', server.stdout.closed, server.stderr.closed) console.log('spawned', server.stdout.closed, server.stderr.closed)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) { server.stdout.on('data', function (data) {

@ -90,6 +90,8 @@ module.exports = {
browser browser
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.setSolidityCompilerVersion('soljson-v0.8.20+commit.a1b79de6.js') .setSolidityCompilerVersion('soljson-v0.8.20+commit.a1b79de6.js')
.click('*[data-id="scConfigExpander"]')
.setValue('#evmVersionSelector', 'berlin') // set target EVM for parser to berlin
.addFile('contracts/mytoken.sol', { .addFile('contracts/mytoken.sol', {
content: myToken content: myToken
}).useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]") }).useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]")

@ -31,7 +31,21 @@ module.exports = {
1: 'uint256: out2 15' 1: 'uint256: out2 15'
} }
}) })
.end() },
'Should clear transient storage after tx execution #group1' : function (browser: NightwatchBrowser) {
browser.addFile('clear_transient.sol', { content: clearTransient })
.verifyContracts(['ClearTransient'])
.clickLaunchIcon('udapp')
.createContract('')
.clickInstance(1)
.clickFunction('get - call')
.testFunction('last',
{
'decoded output': {
0: 'uint256: 0'
}
})
} }
} }
@ -49,3 +63,22 @@ contract TestTransientStorage {
} }
} }
}` }`
const clearTransient = `
// SPDX-License-Identifier: none
pragma solidity 0.8.26;
import "hardhat/console.sol";
contract ClearTransient {
uint p;
constructor() {
uint256 value;
assembly { value := tload(hex"1234") }
p = value;
assembly { tstore(hex"1234", 10) }
}
function get () public view returns (uint) {
return p;
}
}`

@ -22,6 +22,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc721') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc721')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc721' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(100) .pause(100)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')

@ -43,19 +43,20 @@ module.exports = {
(el: any) => { (el: any) => {
const id = (el as any).value.getId() const id = (el as any).value.getId()
browser browser
.waitForElementVisible('li[data-id="treeViewLitreeViewItemtests"]') .waitForElementVisible({ selector: 'li[data-id="treeViewLitreeViewItemtests"]', abortOnFailure: false })
.dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]', id) .dragAndDrop('li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]', id)
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementPresent({ selector: '[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok', abortOnFailure: false })
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
.waitForElementVisible('li[data-id="treeViewLitreeViewItemtests/1_Storage.sol"]') .waitForElementVisible({ selector: 'li[data-id="treeViewLitreeViewItemtests/1_Storage.sol"]', abortOnFailure: false })
.waitForElementVisible('li[data-id="treeViewLitreeViewItemtests/2_Owner.sol"]') .waitForElementVisible({ selector: 'li[data-id="treeViewLitreeViewItemtests/2_Owner.sol"]', abortOnFailure: false })
.waitForElementNotPresent('li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]') .waitForElementNotPresent({ selector: 'li[data-id="treeViewLitreeViewItemcontracts/1_Storage.sol"]', abortOnFailure: false })
.waitForElementNotPresent('li[data-id="treeViewLitreeViewItemcontracts/2_Owner.sol"]') .waitForElementNotPresent({ selector: 'li[data-id="treeViewLitreeViewItemcontracts/2_Owner.sol"]', abortOnFailure: false })
.perform(() => done()) .perform(() => done())
}) })
}) })
} }
}, },
'should drag and drop multiple files and folders in file explorer to contracts folder #group3': function (browser: NightwatchBrowser) { 'should drag and drop multiple files and folders in file explorer to contracts folder #group3': function (browser: NightwatchBrowser) {
const selectedElements = [] const selectedElements = []
if (browser.options.desiredCapabilities?.browserName === 'firefox') { if (browser.options.desiredCapabilities?.browserName === 'firefox') {
@ -79,15 +80,15 @@ module.exports = {
(el: any) => { (el: any) => {
const id = (el as any).value.getId() const id = (el as any).value.getId()
browser browser
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible({ selector: 'li[data-id="treeViewLitreeViewItemcontracts"]', abortOnFailure: false })
.dragAndDrop('li[data-id="treeViewLitreeViewItemtests"]', id) .dragAndDrop('li[data-id="treeViewLitreeViewItemtests"]', id)
.waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') .waitForElementPresent({ selector: '[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok', abortOnFailure: false })
.execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() })
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/tests"]', 5000) .waitForElementVisible({ selector: 'li[data-id="treeViewLitreeViewItemcontracts/tests"]', abortOnFailure: false })
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/README.txt"]', 5000) .waitForElementVisible({ selector: 'li[data-id="treeViewLitreeViewItemcontracts/README.txt"]', abortOnFailure: false })
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/scripts"]', 5000) .waitForElementVisible({ selector: 'li[data-id="treeViewLitreeViewItemcontracts/scripts"]', abortOnFailure: false })
.waitForElementNotPresent('li[data-id="treeViewLitreeViewItemtests"]') .waitForElementNotPresent({ selector: 'li[data-id="treeViewLitreeViewItemtests"]', abortOnFailure: false })
.waitForElementNotPresent('li[data-id="treeViewLitreeViewItemREADME.txt"]') .waitForElementNotPresent({ selector: 'li[data-id="treeViewLitreeViewItemREADME.txt"]', abortOnFailure: false })
.perform(() => done()) .perform(() => done())
}) })
}) })

@ -246,13 +246,13 @@ module.exports = {
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 60000) .waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]', 60000)
.waitForElementContainsText('#solidityUnittestsOutput', 'tests/hhLogs_test.sol', 60000) .waitForElementContainsText('#solidityUnittestsOutput', 'tests/hhLogs_test.sol', 60000)
.pause(2000) .pause(2000)
.assert.containsText('#journal > div:nth-child(4) > span', 'Before all:') .assert.textContains('#journal > div:nth-child(3) > span', 'Before all:')
.assert.containsText('#journal > div:nth-child(4) > span', 'Inside beforeAll') .assert.textContains('#journal > div:nth-child(3) > span', 'Inside beforeAll')
.assert.containsText('#journal > div:nth-child(5) > span', 'Check sender:') .assert.textContains('#journal > div:nth-child(4) > span', 'Check sender:')
.assert.containsText('#journal > div:nth-child(5) > span', 'msg.sender is 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4') .assert.textContains('#journal > div:nth-child(4) > span', 'msg.sender is 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4')
.assert.containsText('#journal > div:nth-child(6) > span', 'Check int logs:') .assert.textContains('#journal > div:nth-child(5) > span', 'Check int logs:')
.assert.containsText('#journal > div:nth-child(6) > span', '10 20') .assert.textContains('#journal > div:nth-child(5) > span', '10 20')
.assert.containsText('#journal > div:nth-child(6) > span', 'Number is 25') .assert.textContains('#journal > div:nth-child(5) > span', 'Number is 25')
.openFile('tests/hhLogs_test.sol') .openFile('tests/hhLogs_test.sol')
.removeFile('tests/hhLogs_test.sol', 'workspace_new') .removeFile('tests/hhLogs_test.sol', 'workspace_new')
}, },

@ -17,13 +17,13 @@ module.exports = {
browser.clickLaunchIcon('pluginManager') browser.clickLaunchIcon('pluginManager')
.scrollAndClick('[data-id="pluginManagerComponentActivateButtonvyper"]') .scrollAndClick('[data-id="pluginManagerComponentActivateButtonvyper"]')
.clickLaunchIcon('vyper') .clickLaunchIcon('vyper')
.pause(5000)
// @ts-ignore
.frame(0) .frame(0)
}, },
'Should clone the Vyper repo #group1': function (browser: NightwatchBrowser) { 'Should clone the Vyper repo #group1': function (browser: NightwatchBrowser) {
browser.click('button[data-id="add-repository"]') browser
.waitForElementVisible('button[data-id="add-repository"]')
.click('button[data-id="add-repository"]')
.frameParent() .frameParent()
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.waitForElementVisible({ .waitForElementVisible({
@ -31,11 +31,10 @@ module.exports = {
locateStrategy: 'xpath', locateStrategy: 'xpath',
timeout: 120000 timeout: 120000
}) })
.currentWorkspaceIs('snekmate')
.waitForElementVisible({ .waitForElementVisible({
selector: "//*[@data-id='treeViewLitreeViewItemsrc' and contains(.,'src')]", selector: "//*[contains(., 'Vyper repository cloned')]",
locateStrategy: 'xpath', locateStrategy: 'xpath',
timeout: 1200000 timeout: 120000
}) })
}, },
// 'Add vyper file to run tests #group1': function (browser: NightwatchBrowser) { // 'Add vyper file to run tests #group1': function (browser: NightwatchBrowser) {
@ -123,6 +122,7 @@ module.exports = {
.clickLaunchIcon('vyper') .clickLaunchIcon('vyper')
// @ts-ignore // @ts-ignore
.frame(0) .frame(0)
.waitForElementVisible('[data-id="compile"]')
.click('[data-id="compile"]') .click('[data-id="compile"]')
.waitForElementVisible({ .waitForElementVisible({
selector:'[data-id="compilation-details"]', selector:'[data-id="compilation-details"]',

@ -43,6 +43,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_remix_default') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_remix_default')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_remix_default' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(1000) .pause(1000)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
@ -115,6 +117,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_blank') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_blank')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_blank' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(100) .pause(100)
.waitForElementPresent('*[data-id="treeViewUltreeViewMenu"]') .waitForElementPresent('*[data-id="treeViewUltreeViewMenu"]')
@ -136,6 +140,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc20') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc20')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc20' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(100) .pause(100)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
@ -195,6 +201,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc721') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc721')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc721' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(100) .pause(100)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
@ -254,6 +262,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc1155') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc1155')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc1155' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(100) .pause(100)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
@ -484,6 +494,7 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'sometestworkspace') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'sometestworkspace')
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'sometestworkspace' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]')
@ -510,6 +521,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_db_test') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_db_test')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_db_test' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]') .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts/MyToken.sol"]')
@ -538,6 +551,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'multisig cookbook') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'multisig cookbook')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'multisig cookbook' })
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.waitForElementVisible('[data-id="PermissionHandler-modal-footer-ok-react"]', 300000) .waitForElementVisible('[data-id="PermissionHandler-modal-footer-ok-react"]', 300000)
.click('[data-id="PermissionHandler-modal-footer-ok-react"]') .click('[data-id="PermissionHandler-modal-footer-ok-react"]')

@ -53,6 +53,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]') .scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_blank') .setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_blank')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_blank' })
.click('[data-id="initGitRepositoryLabel"]') .click('[data-id="initGitRepositoryLabel"]')
.modalFooterOKClick('TemplatesSelection') .modalFooterOKClick('TemplatesSelection')
.pause(100) .pause(100)
@ -179,7 +181,7 @@ module.exports = {
.waitForElementVisible('[data-id="workspaceGitPanel"]') .waitForElementVisible('[data-id="workspaceGitPanel"]')
.waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]') .waitForElementVisible('[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]') .click('[data-id="workspaceGitBranchesDropdown"]')
.pause() .pause(1000)
.waitForElementVisible('[data-id="custom-dropdown-menu"]') .waitForElementVisible('[data-id="custom-dropdown-menu"]')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/dev')
.waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/production') .waitForElementContainsText('[data-id="custom-dropdown-items"]', 'origin/production')
@ -402,7 +404,7 @@ module.exports = {
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`contract Counter is BaseHook {`) !== -1, browser.assert.ok(content.indexOf(`contract Counter is BaseHook {`) !== -1,
'Incorrect content') 'Incorrect content')
}).pause() })
}, },
'Should create Remix default workspace with files #group5': function (browser: NightwatchBrowser) { 'Should create Remix default workspace with files #group5': function (browser: NightwatchBrowser) {
@ -423,6 +425,10 @@ module.exports = {
clickLaunchIcon('dgit') clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="initgit-btn"]') .waitForElementVisible('*[data-id="initgit-btn"]')
.click('*[data-id="initgit-btn"]') .click('*[data-id="initgit-btn"]')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="gitubUsername"]')
.setValue('*[data-id="gitubUsername"]', 'git') .setValue('*[data-id="gitubUsername"]', 'git')
.setValue('*[data-id="githubEmail"]', 'git@example.com') .setValue('*[data-id="githubEmail"]', 'git@example.com')
.click('*[data-id="saveGitHubCredentials"]') .click('*[data-id="saveGitHubCredentials"]')
@ -430,6 +436,8 @@ module.exports = {
}, },
'check source controle panel #group5': function (browser: NightwatchBrowser) { 'check source controle panel #group5': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="sourcecontrol-panel"]')
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible({ .waitForElementVisible({
selector: "//*[@data-status='new-untracked' and @data-file='/tests/MyToken_test.sol']", selector: "//*[@data-status='new-untracked' and @data-file='/tests/MyToken_test.sol']",
locateStrategy: 'xpath' locateStrategy: 'xpath'

@ -4,7 +4,7 @@ echo "Downloading specified soljson.js version based on defaultVersion in packag
set -e set -e
# Check if curl and jq are installed # Check if curl is installed
if ! command -v curl &> /dev/null; then if ! command -v curl &> /dev/null; then
echo "curl could not be found" echo "curl could not be found"
exit 1 exit 1
@ -14,32 +14,38 @@ fi
defaultVersion=$(grep '"defaultVersion"' package.json | awk -F '"' '{print $4}') defaultVersion=$(grep '"defaultVersion"' package.json | awk -F '"' '{print $4}')
echo "Specified version from package.json: $defaultVersion" echo "Specified version from package.json: $defaultVersion"
# Download the list.json file containing available versions # Fetch the list.json from the Solidity binaries
curl -s https://binaries.soliditylang.org/wasm/list.json > list.json listJson=$(curl -s --connect-timeout 5 --max-time 5 https://binaries.soliditylang.org/wasm/list.json)
# Check if the download was successful
if [ -z "$listJson" ]; then
echo "Failed to fetch version list. No internet connection or the connection is too slow."
exit 0 # Silently exit
fi
# Overwrite the local list.json with the fetched content
echo "$listJson" > ./apps/remix-ide/src/assets/list.json
# Check if the specified version exists in the list
check=$(echo "$listJson" | grep "\"$defaultVersion\"")
# Use jq to extract the path for the specified version from the builds array
check=$(grep "\"$defaultVersion\"" list.json)
if [ -z "$check" ]; then if [ -z "$check" ]; then
echo "The specified version $defaultVersion could not be found in the list" echo "The specified version $defaultVersion could not be found in the list"
exit 1 exit 1
fi fi
echo "Path for the specified version: $defaultVersion" echo "Path for the specified version: $defaultVersion"
fullPath="https://binaries.soliditylang.org/bin/$defaultVersion" fullPath="https://binaries.soliditylang.org/bin/$defaultVersion"
echo "Download fullPath: $fullPath" echo "Download fullPath: $fullPath"
# Ensure the target directory exists # Ensure the target directory exists
if [ ! -d "./apps/remix-ide/src/assets/js/soljson" ]; then if [ ! -d "./apps/remix-ide/src/assets/js/soljson" ]; then
mkdir -p ./apps/remix-ide/src/assets/js/soljson mkdir -p ./apps/remix-ide/src/assets/js/soljson
fi fi
# Download the file to ./apps/remix-ide/src/assets/js/soljson.js # Download the soljson.js file to ./apps/remix-ide/src/assets/js/soljson.js
echo "Downloading soljson.js from "$fullPath" to ./apps/remix-ide/src/assets/js/soljson.js" echo "Downloading soljson.js from $fullPath to ./apps/remix-ide/src/assets/js/soljson.js"
curl -s "$fullPath" > ./apps/remix-ide/src/assets/js/soljson.js curl -s "$fullPath" > ./apps/remix-ide/src/assets/js/soljson.js
# Copy the downloaded soljson.js to the specific version directory # Copy the downloaded soljson.js to the specific version directory
cp ./apps/remix-ide/src/assets/js/soljson.js "./apps/remix-ide/src/assets/js/soljson/$path" cp ./apps/remix-ide/src/assets/js/soljson.js "./apps/remix-ide/src/assets/js/soljson/$defaultVersion.js"
cp list.json ./apps/remix-ide/src/assets/list.json
# Clean up by removing the list.json echo "Download and setup of soljson.js complete"
rm list.json

@ -16,8 +16,6 @@ const profile = {
events: ['toggleContent', 'showContent'] events: ['toggleContent', 'showContent']
} }
const toMaximize = ['LearnEth']
export class VerticalIcons extends Plugin { export class VerticalIcons extends Plugin {
events: EventEmitter events: EventEmitter
htmlElement: HTMLDivElement htmlElement: HTMLDivElement
@ -128,11 +126,6 @@ export class VerticalIcons extends Plugin {
// TODO: Only keep `this.emit` (issue#2210) // TODO: Only keep `this.emit` (issue#2210)
this.emit('showContent', name) this.emit('showContent', name)
this.events.emit('showContent', name) this.events.emit('showContent', name)
if (toMaximize.includes(name)) {
setTimeout(_ => {
this.call('layout', 'maximiseSidePanel')
}, 500)
}
} }
/** /**

@ -4,26 +4,16 @@ import {
Plugin Plugin
} from '@remixproject/engine' } from '@remixproject/engine'
import git, { ReadBlobResult, ReadCommitResult, StatusRow } from 'isomorphic-git' import git, { ReadBlobResult, ReadCommitResult, StatusRow } from 'isomorphic-git'
import IpfsHttpClient from 'ipfs-http-client'
import {
saveAs
} from 'file-saver'
import http from 'isomorphic-git/http/web'
import JSZip from 'jszip'
import path from 'path' import path from 'path'
import FormData from 'form-data'
import axios from 'axios' import axios from 'axios'
import { Registry } from '@remix-project/remix-lib' import { Registry } from '@remix-project/remix-lib'
import { Octokit, App } from "octokit" import { Octokit } from "octokit"
import { OctokitResponse } from '@octokit/types'
import { Endpoints } from "@octokit/types"
import { IndexedDBStorage } from './filesystems/indexedDB' import { IndexedDBStorage } from './filesystems/indexedDB'
import { GitHubUser, branch, commitChange, remote, pagedCommits, remoteCommitsInputType, cloneInputType, fetchInputType, pullInputType, pushInputType, currentBranchInput, branchInputType, addInput, rmInput, resolveRefInput, readBlobInput, repositoriesInput, commitInput, branchDifference, compareBranchesInput, initInput, userEmails, checkoutInput } from '@remix-ui/git' import { branch, commitChange, remote } from '@remix-ui/git'
import { LibraryProfile, StatusEvents } from '@remixproject/plugin-utils' import { checkoutInputType, statusInput, logInputType, author, pagedCommits, remoteCommitsInputType, cloneInputType, fetchInputType, pullInputType, pushInputType, currentBranchInput, branchInputType, addInputType, rmInputType, resolveRefInput, readBlobInput, repositoriesInput, commitInputType, branchDifference, compareBranchesInput, initInputType, isoGitFSConfig, GitHubUser, userEmails } from '@remix-api'
import { ITerminal } from '@remixproject/plugin-api/src/lib/terminal' import { LibraryProfile } from '@remixproject/plugin-utils'
import { partial } from 'lodash' import { CustomRemixApi } from '@remix-api'
import { isoGit } from "@remix-git"
declare global { declare global {
interface Window { remixFileSystemCallback: IndexedDBStorage; remixFileSystem: any; } interface Window { remixFileSystemCallback: IndexedDBStorage; remixFileSystem: any; }
} }
@ -34,49 +24,17 @@ const profile: LibraryProfile = {
description: 'Decentralized git provider', description: 'Decentralized git provider',
icon: 'assets/img/fileManager.webp', icon: 'assets/img/fileManager.webp',
version: '0.0.1', version: '0.0.1',
methods: ['init', 'localStorageUsed', 'addremote', 'delremote', 'remotes', 'fetch', 'clone', 'export', 'import', 'status', 'log', 'commit', 'add', 'remove', 'reset', 'rm', 'lsfiles', 'readblob', 'resolveref', 'branches', 'branch', 'checkout', 'currentbranch', 'push', 'pull', 'setIpfsConfig', 'zip', 'setItem', 'getItem', 'version', 'updateSubmodules' methods: ['init', 'addremote', 'delremote', 'remotes', 'fetch', 'clone', 'status', 'log', 'commit', 'add', 'remove', 'rm', 'readblob', 'resolveref', 'branches', 'branch', 'checkout', 'currentbranch', 'push', 'pull', 'version', 'updateSubmodules'
, 'getGitHubUser', 'remotebranches', 'remotecommits', 'repositories', 'getCommitChanges', 'compareBranches'], , 'getGitHubUser', 'remotebranches', 'remotecommits', 'repositories', 'getCommitChanges', 'compareBranches'],
kind: 'file-system' kind: 'file-system'
} }
class DGitProvider extends Plugin { class DGitProvider extends Plugin<any, CustomRemixApi> {
ipfsconfig: { host: string; port: number; protocol: string; ipfsurl: string }
globalIPFSConfig: { host: string; port: number; protocol: string; ipfsurl: string }
remixIPFS: { host: string; port: number; protocol: string; ipfsurl: string }
ipfsSources: any[]
ipfs: any
filesToSend: any[]
constructor() { constructor() {
super(profile) super(profile)
this.ipfsconfig = {
host: 'jqgt.remixproject.org',
port: 443,
protocol: 'https',
ipfsurl: 'https://jqgt.remixproject.org/ipfs/'
}
this.globalIPFSConfig = {
host: 'ipfs.io',
port: 443,
protocol: 'https',
ipfsurl: 'https://ipfs.io/ipfs/'
}
this.remixIPFS = {
host: 'jqgt.remixproject.org',
port: 443,
protocol: 'https',
ipfsurl: 'https://jqgt.remixproject.org/ipfs/'
}
this.ipfsSources = [this.remixIPFS, this.globalIPFSConfig, this.ipfsconfig]
} }
async addIsomorphicGitConfigFS(dir = '') { async addIsomorphicGitConfigFS(dir = '') {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
return {
fs: window.remixFileSystem,
dir: '/'
}
}
const workspace = await this.call('filePanel', 'getCurrentWorkspace') const workspace = await this.call('filePanel', 'getCurrentWorkspace')
if (!workspace) return if (!workspace) return
@ -86,62 +44,12 @@ class DGitProvider extends Plugin {
} }
} }
async addIsomorphicGitConfig(input) { async getToken() {
return await this.call('config' as any, 'getAppParameter', 'settings/gist-access-token')
const token = await this.call('config' as any, 'getAppParameter', 'settings/gist-access-token')
let config = {
corsProxy: 'https://corsproxy.remixproject.org/',
http,
onAuth: url => {
url
const auth = {
username: input.token || token,
password: ''
}
return auth
}
}
if (input.url) {
const url = new URL(input.url)
if (url.hostname.includes('localhost')) {
config = {
...config,
corsProxy: null
}
}
}
if ((input.remote && input.remote.url)) {
const url = new URL(input.remote.url)
if (url.hostname.includes('localhost')) {
config = {
...config,
corsProxy: null,
}
}
}
if (input.provider && input.provider === 'github') {
config = {
...config,
corsProxy: 'https://corsproxy.remixproject.org/',
}
}
if (input.provider && input.provider === 'localhost') {
config = {
...config,
corsProxy: null
}
}
return config
} }
async getCommandUser(input) { async getAuthor(input) {
const author = { const author: author = {
name: '', name: '',
email: '' email: ''
} }
@ -167,7 +75,7 @@ class DGitProvider extends Plugin {
return author return author
} }
async init(input?: initInput): Promise<void> { async init(input?: initInputType): Promise<void> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'init', { await this.call('isogit', 'init', {
defaultBranch: (input && input.defaultBranch) || 'main' defaultBranch: (input && input.defaultBranch) || 'main'
@ -192,11 +100,10 @@ class DGitProvider extends Plugin {
return version return version
} }
async status(cmd): Promise<Array<StatusRow>> { async status(cmd: statusInput): Promise<Array<StatusRow>> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
const status = await this.call('isogit', 'status', cmd) const status = await this.call('isogit', 'status', cmd)
return status return status
} }
@ -208,7 +115,7 @@ class DGitProvider extends Plugin {
return status return status
} }
async add(cmd: addInput): Promise<void> { async add(cmd: addInputType): Promise<void> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'add', cmd) await this.call('isogit', 'add', cmd)
@ -222,7 +129,7 @@ class DGitProvider extends Plugin {
this.emit('add') this.emit('add')
} }
async rm(cmd: rmInput) { async rm(cmd: rmInputType) {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'rm', cmd) await this.call('isogit', 'rm', cmd)
@ -231,26 +138,11 @@ class DGitProvider extends Plugin {
...await this.addIsomorphicGitConfigFS(), ...await this.addIsomorphicGitConfigFS(),
...cmd ...cmd
}) })
this.emit('rm')
}
}
async reset(cmd) {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'reset', cmd)
} else {
await git.resetIndex({
...await this.addIsomorphicGitConfigFS(),
...cmd
})
this.emit('rm')
} }
this.emit('rm')
} }
async checkout(cmd: checkoutInput): Promise<void> { async checkout(cmd: checkoutInputType): Promise<void> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'checkout', cmd) await this.call('isogit', 'checkout', cmd)
@ -291,12 +183,11 @@ class DGitProvider extends Plugin {
this.emit('checkout') this.emit('checkout')
} }
async log(cmd: { ref: string }): Promise<ReadCommitResult[]> { async log(cmd: logInputType): Promise<ReadCommitResult[]> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
const status = await this.call('isogit', 'log', { const status = await this.call('isogit', 'log', {
...cmd, ...cmd,
depth: 10
}) })
return status return status
@ -306,101 +197,32 @@ class DGitProvider extends Plugin {
...await this.addIsomorphicGitConfigFS(), ...await this.addIsomorphicGitConfigFS(),
...cmd, ...cmd,
}) })
return status return status
} }
async compareBranches({ branch, remote }: compareBranchesInput): Promise<branchDifference> { async compareBranches({ branch, remote }: compareBranchesInput): Promise<branchDifference> {
// Get current branch commits if ((Registry.getInstance().get('platform').api.isDesktop())) {
const headCommits = await git.log({ return await this.call('isogit', 'compareBranches', { branch, remote })
...await this.addIsomorphicGitConfigFS(), }
ref: branch.name, return await isoGit.compareBranches({ branch, remote }, await this.addIsomorphicGitConfigFS())
});
// Get remote branch commits
const remoteCommits = await git.log({
...await this.addIsomorphicGitConfigFS(),
ref: `${remote.name}/${branch.name}`,
});
// Convert arrays of commit objects to sets of commit SHAs
const headCommitSHAs = new Set(headCommits.map(commit => commit.oid));
const remoteCommitSHAs = new Set(remoteCommits.map(commit => commit.oid));
// Filter out commits that are only in the remote branch
const uniqueRemoteCommits = remoteCommits.filter(commit => !headCommitSHAs.has(commit.oid));
// filter out commits that are only in the local branch
const uniqueHeadCommits = headCommits.filter(commit => !remoteCommitSHAs.has(commit.oid));
return {
uniqueHeadCommits,
uniqueRemoteCommits,
};
} }
async getCommitChanges(commitHash1: string, commitHash2: string): Promise<commitChange[]> { async getCommitChanges(commitHash1: string, commitHash2: string): Promise<commitChange[]> {
const result: commitChange[] = await git.walk({ if ((Registry.getInstance().get('platform').api.isDesktop())) {
...await this.addIsomorphicGitConfigFS(), const result = this.call('isogit', 'getCommitChanges', commitHash1, commitHash2)
trees: [git.TREE({ ref: commitHash1 }), git.TREE({ ref: commitHash2 })], return result
map: async function (filepath, [A, B]) { }
if (filepath === '.') {
return
}
try {
if ((A && await A.type()) === 'tree' || B && (await B.type()) === 'tree') {
return
}
} catch (e) {
// ignore
}
// generate ids
const Aoid = A && await A.oid() || undefined
const Boid = B && await B.oid() || undefined
const commitChange: Partial<commitChange> = {
hashModified: commitHash1,
hashOriginal: commitHash2,
path: filepath,
}
// determine modification type
if (Aoid !== Boid) {
commitChange.type = "modified"
}
if (Aoid === undefined) {
commitChange.type = "deleted"
}
if (Boid === undefined || !commitHash2) {
commitChange.type = "added"
}
if (Aoid === undefined && Boid === undefined) {
commitChange.type = "unknown"
}
if (commitChange.type)
return commitChange
else
return undefined
},
})
return result return await isoGit.getCommitChanges(commitHash1, commitHash2, await this.addIsomorphicGitConfigFS())
} }
async remotes(config): Promise<remote[]> { async remotes(): Promise<remote[]> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'remotes', config) return await this.call('isogit', 'remotes')
} }
let remotes: remote[] = [] return await isoGit.remotes(await this.addIsomorphicGitConfigFS())
try {
remotes = (await git.listRemotes({ ...config ? config : await this.addIsomorphicGitConfigFS() })).map((remote) => { return { name: remote.remote, url: remote.url } }
)
} catch (e) {
// do nothing
}
return remotes
} }
async branch(cmd: branchInputType): Promise<void> { async branch(cmd: branchInputType): Promise<void> {
@ -423,68 +245,28 @@ class DGitProvider extends Plugin {
return status return status
} }
async currentbranch(config: currentBranchInput): Promise<branch> { async currentbranch(input: currentBranchInput): Promise<branch> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'currentbranch') return await this.call('isogit', 'currentbranch', input)
} }
try { const defaultConfig = await this.addIsomorphicGitConfigFS()
const defaultConfig = await this.addIsomorphicGitConfigFS() return await isoGit.currentbranch(input, defaultConfig)
const cmd = config ? defaultConfig ? { ...defaultConfig, ...config } : config : defaultConfig
const name = await git.currentBranch(cmd)
let remote: remote = undefined
try {
const remoteName = await git.getConfig({
...defaultConfig,
path: `branch.${name}.remote`
})
if (remoteName) {
const remoteUrl = await git.getConfig({
...defaultConfig,
path: `remote.${remoteName}.url`
})
remote = { name: remoteName, url: remoteUrl }
}
} catch (e) {
// do nothing
}
return {
remote: remote,
name: name || ''
}
} catch (e) {
return undefined
}
} }
async branches(config): Promise<branch[]> { async branches(config: isoGitFSConfig): Promise<branch[]> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'branches') const branches = await this.call('isogit', 'branches')
}
try {
const defaultConfig = await this.addIsomorphicGitConfigFS()
const cmd = config ? defaultConfig ? { ...defaultConfig, ...config } : config : defaultConfig
const remotes = await this.remotes(config)
let branches: branch[] = []
branches = (await git.listBranches(cmd)).map((branch) => { return { remote: undefined, name: branch } })
for (const remote of remotes) {
cmd.remote = remote.name
const remotebranches = (await git.listBranches(cmd)).map((branch) => { return { remote: remote, name: branch } })
branches = [...branches, ...remotebranches]
}
return branches return branches
} catch (e) {
console.log(e)
return []
} }
const defaultConfig = await this.addIsomorphicGitConfigFS()
const cmd = config ? defaultConfig ? { ...defaultConfig, ...config } : config : defaultConfig
return await isoGit.branches(cmd)
} }
async commit(cmd: commitInput): Promise<string> { async commit(cmd: commitInputType): Promise<string> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
try { try {
@ -511,19 +293,6 @@ class DGitProvider extends Plugin {
} }
} }
async lsfiles(cmd) {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'lsfiles', cmd)
}
const filesInStaging = await git.listFiles({
...await this.addIsomorphicGitConfigFS(),
...cmd
})
return filesInStaging
}
async resolveref(cmd: resolveRefInput): Promise<string> { async resolveref(cmd: resolveRefInput): Promise<string> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
@ -550,26 +319,9 @@ class DGitProvider extends Plugin {
return readBlobResult return readBlobResult
} }
async setIpfsConfig(config) {
this.ipfsconfig = config
return new Promise((resolve) => {
resolve(this.checkIpfsConfig())
})
}
async checkIpfsConfig(config?) {
this.ipfs = IpfsHttpClient(config || this.ipfsconfig)
try {
await this.ipfs.config.getAll()
return true
} catch (e) {
return false
}
}
async addremote(input: remote): Promise<void> { async addremote(input: remote): Promise<void> {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'addremote', { url: input.url, remote: input.name }) await this.call('isogit', 'addremote', input)
return return
} }
await git.addRemote({ ...await this.addIsomorphicGitConfigFS(), url: input.url, remote: input.name }) await git.addRemote({ ...await this.addIsomorphicGitConfigFS(), url: input.url, remote: input.name })
@ -577,51 +329,39 @@ class DGitProvider extends Plugin {
async delremote(input: remote) { async delremote(input: remote) {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'delremote', { remote: input.name }) await this.call('isogit', 'delremote', input)
return return
} }
await git.deleteRemote({ ...await this.addIsomorphicGitConfigFS(), remote: input.name }) await git.deleteRemote({ ...await this.addIsomorphicGitConfigFS(), remote: input.name })
} }
async localStorageUsed() {
return this.calculateLocalStorage()
}
async clone(input: cloneInputType) { async clone(input: cloneInputType) {
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
const folder = await this.call('fs', 'selectFolder', null, 'Select or create a folder to clone the repository in', 'Select as Repository Destination')
if (!folder) return false
const cmd = {
url: input.url,
singleBranch: input.singleBranch,
ref: input.branch,
depth: input.depth || 10,
dir: folder,
input
}
this.call('terminal', 'logHtml', `Cloning ${input.url}... please wait...`)
try { try {
const result = await this.call('isogit', 'clone', cmd) const folder = await this.call('fs', 'selectFolder', null, 'Select or create a folder to clone the repository in', 'Select as Repository Destination')
this.call('fs', 'openWindow', folder) if (!folder) return false
return result input.dir = folder
input.depth = input.depth || 10
const result = await this.call('isogit', 'clone', input)
this.call('fs' as any, 'openWindow', folder)
} catch (e) { } catch (e) {
this.call('notification', 'alert', { this.call('notification', 'alert', {
id: 'dgitAlert', id: 'dgitAlert',
title: 'Error Cloning',
message: 'Unexpected error while cloning the repository: \n' + e.toString(), message: 'Unexpected error while cloning the repository: \n' + e.toString(),
}) })
} }
} else { } else {
const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.') const permission = await this.askUserPermission('clone', 'Import multiple files into your workspaces.')
if (!permission) return false if (!permission) return false
if (parseFloat(this.calculateLocalStorage()) > 10000) throw new Error('The local storage of the browser is full.')
if (!input.workspaceExists) await this.call('filePanel', 'createWorkspace', input.workspaceName || `workspace_${Date.now()}`, true) if (!input.workspaceExists) await this.call('filePanel', 'createWorkspace', input.workspaceName || `workspace_${Date.now()}`, true)
const cmd = { const cmd = {
url: input.url, url: input.url,
singleBranch: input.singleBranch, singleBranch: input.singleBranch,
ref: input.branch, ref: input.branch,
depth: input.depth || 10, depth: input.depth || 10,
...await this.addIsomorphicGitConfig(input), ...await isoGit.addIsomorphicGitProxyConfig(input, this),
...await this.addIsomorphicGitConfigFS() ...await this.addIsomorphicGitConfigFS()
} }
this.call('terminal', 'logHtml', `Cloning ${input.url}... please wait...`) this.call('terminal', 'logHtml', `Cloning ${input.url}... please wait...`)
@ -674,6 +414,9 @@ class DGitProvider extends Plugin {
} }
async updateSubmodules(input) { async updateSubmodules(input) {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'updateSubmodules', null)
}
try { try {
const currentDir = (input && input.dir) || '' const currentDir = (input && input.dir) || ''
const gitmodules = await this.parseGitmodules(currentDir) const gitmodules = await this.parseGitmodules(currentDir)
@ -705,10 +448,10 @@ class DGitProvider extends Plugin {
url: module.url, url: module.url,
singleBranch: true, singleBranch: true,
depth: 1, depth: 1,
...await this.addIsomorphicGitConfig({ ...await isoGit.addIsomorphicGitProxyConfig({
...input, ...input,
provider: 'github' provider: 'github',
}), }, this),
...await this.addIsomorphicGitConfigFS(dir) ...await this.addIsomorphicGitConfigFS(dir)
} }
this.call('terminal', 'logHtml', `Cloning submodule ${dir}...`) this.call('terminal', 'logHtml', `Cloning submodule ${dir}...`)
@ -732,10 +475,10 @@ class DGitProvider extends Plugin {
if (result && result.length) { if (result && result.length) {
this.call('terminal', 'logHtml', `Checking out submodule ${dir} to ${result[0]} in directory ${dir}`) this.call('terminal', 'logHtml', `Checking out submodule ${dir} to ${result[0]} in directory ${dir}`)
await git.fetch({ await git.fetch({
...await this.addIsomorphicGitConfig({ ...await isoGit.addIsomorphicGitProxyConfig({
...input, ...input,
provider: 'github' provider: 'github',
}), }, this),
...await this.addIsomorphicGitConfigFS(dir), ...await this.addIsomorphicGitConfigFS(dir),
singleBranch: true, singleBranch: true,
ref: result[0] ref: result[0]
@ -782,55 +525,22 @@ class DGitProvider extends Plugin {
async push(input: pushInputType) { async push(input: pushInputType) {
const cmd = {
force: input.force,
ref: input.ref.name,
remoteRef: input.remoteRef && input.remoteRef.name,
remote: input.remote.name,
author: await this.getCommandUser(input),
input,
}
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'push', cmd) return await this.call('isogit', 'push', input)
} else { } else {
const result = await isoGit.push(input, await this.addIsomorphicGitConfigFS(), this)
const cmd2 = {
...cmd,
...await this.addIsomorphicGitConfig(input),
}
const result = await git.push({
...await this.addIsomorphicGitConfigFS(),
...cmd2
})
return result return result
} }
} }
async pull(input: pullInputType) { async pull(input: pullInputType) {
const cmd = {
ref: input.ref.name,
remoteRef: input.remoteRef && input.remoteRef.name,
author: await this.getCommandUser(input),
remote: input.remote.name,
input,
}
let result let result
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
result = await this.call('isogit', 'pull', cmd) result = await this.call('isogit', 'pull', input)
} }
else { else {
const cmd2 = { result = await isoGit.pull(input, await this.addIsomorphicGitConfigFS(), this)
...cmd,
...await this.addIsomorphicGitConfig(input),
}
result = await git.pull({
...await this.addIsomorphicGitConfigFS(),
...cmd2
})
} }
setTimeout(async () => { setTimeout(async () => {
await this.call('fileManager', 'refresh') await this.call('fileManager', 'refresh')
@ -839,192 +549,19 @@ class DGitProvider extends Plugin {
} }
async fetch(input: fetchInputType) { async fetch(input: fetchInputType) {
const cmd = {
ref: input.ref && input.ref.name,
remoteRef: input.remoteRef && input.remoteRef.name,
author: await this.getCommandUser(input),
remote: input.remote && input.remote.name,
depth: input.depth || 5,
singleBranch: input.singleBranch,
relative: input.relative,
input
}
let result let result
if ((Registry.getInstance().get('platform').api.isDesktop())) { if ((Registry.getInstance().get('platform').api.isDesktop())) {
result = await this.call('isogit', 'fetch', cmd) result = await this.call('isogit', 'fetch', {
} else { ...input,
const cmd2 = {
...cmd,
...await this.addIsomorphicGitConfig(input),
}
result = await git.fetch({
...await this.addIsomorphicGitConfigFS(),
...cmd2
}) })
}
setTimeout(async () => {
await this.call('fileManager', 'refresh')
}, 1000)
return result
}
async export(config) {
if (!this.checkIpfsConfig(config)) return false
const workspace = await this.call('filePanel', 'getCurrentWorkspace')
const files = await this.getDirectory('/')
this.filesToSend = []
for (const file of files) {
const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`, null)
const ob = {
path: file,
content: c
}
this.filesToSend.push(ob)
}
const addOptions = {
wrapWithDirectory: true
}
const r = await this.ipfs.add(this.filesToSend, addOptions)
return r.cid.string
}
async importIPFSFiles(config, cid, workspace) {
const ipfs = IpfsHttpClient(config)
let result = false
try {
const data = ipfs.get(cid, { timeout: 60000 })
for await (const file of data) {
if (file.path) result = true
file.path = file.path.replace(cid, '')
if (!file.content) {
continue
}
const content = []
for await (const chunk of file.content) {
content.push(chunk)
}
const dir = path.dirname(file.path)
try {
await this.createDirectories(`${workspace.absolutePath}/${dir}`)
} catch (e) { throw new Error(e) }
try {
await window.remixFileSystem.writeFile(`${workspace.absolutePath}/${file.path}`, Buffer.concat(content) || new Uint8Array(), null)
} catch (e) { throw new Error(e) }
}
} catch (e) {
throw new Error(e)
}
return result
}
calculateLocalStorage() {
let _lsTotal = 0
let _xLen; let _x
for (_x in localStorage) {
// eslint-disable-next-line no-prototype-builtins
if (!localStorage.hasOwnProperty(_x)) {
continue
}
_xLen = ((localStorage[_x].length + _x.length) * 2)
_lsTotal += _xLen
}
return (_lsTotal / 1024).toFixed(2)
}
async import(cmd) {
const permission = await this.askUserPermission('import', 'Import multiple files into your workspaces.')
if (!permission) return false
if (parseFloat(this.calculateLocalStorage()) > 10000) throw new Error('The local storage of the browser is full.')
const cid = cmd.cid
await this.call('filePanel', 'createWorkspace', `workspace_${Date.now()}`, true)
const workspace = await this.call('filePanel', 'getCurrentWorkspace')
let result
if (cmd.local) {
result = await this.importIPFSFiles(this.ipfsconfig, cid, workspace)
} else { } else {
result = await this.importIPFSFiles(this.remixIPFS, cid, workspace) || await this.importIPFSFiles(this.ipfsconfig, cid, workspace) || await this.importIPFSFiles(this.globalIPFSConfig, cid, workspace) result = await isoGit.fetch(input, await this.addIsomorphicGitConfigFS(), this)
} }
setTimeout(async () => { setTimeout(async () => {
await this.call('fileManager', 'refresh') await this.call('fileManager', 'refresh')
}, 1000) }, 1000)
if (!result) throw new Error(`Cannot pull files from IPFS at ${cid}`)
}
async getItem(name) {
if (typeof window !== 'undefined') {
return window.localStorage.getItem(name)
}
}
async setItem(name, content) {
try {
if (typeof window !== 'undefined') {
window.localStorage.setItem(name, content)
}
} catch (e) {
console.log(e)
return false
}
return true
}
async zip() {
const zip = new JSZip()
const workspace = await this.call('filePanel', 'getCurrentWorkspace')
const files = await this.getDirectory('/')
this.filesToSend = []
for (const file of files) {
const c = await window.remixFileSystem.readFile(`${workspace.absolutePath}/${file}`, null)
zip.file(file, c)
}
await zip.generateAsync({
type: 'blob'
})
.then(function (content) {
saveAs(content, `${workspace.name}.zip`)
})
}
async createDirectories(strdirectories) {
const ignore = ['.', '/.', '']
if (ignore.indexOf(strdirectories) > -1) return false
const directories = strdirectories.split('/')
for (let i = 0; i < directories.length; i++) {
let previouspath = ''
if (i > 0) previouspath = '/' + directories.slice(0, i).join('/')
const finalPath = previouspath + '/' + directories[i]
try {
if (!await window.remixFileSystem.exists(finalPath)) {
await window.remixFileSystem.mkdir(finalPath)
}
} catch (e) {
console.log(e)
}
}
}
async getDirectory(dir) {
let result = []
const files = await this.call('fileManager', 'readdir', dir)
const fileArray = normalize(files)
for (const fi of fileArray) {
if (fi) {
const type = fi.data.isDirectory
if (type === true) {
result = [
...result,
...(await this.getDirectory(
`${fi.filename}`
))
]
} else {
result = [...result, fi.filename]
}
}
}
return result return result
} }
@ -1056,15 +593,22 @@ class DGitProvider extends Plugin {
auth: input.token auth: input.token
}) })
const user = await octokit.request('GET /user') const user = await octokit.request('GET /user', {
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
})
const emails = await octokit.request('GET /user/emails') const emails = await octokit.request('GET /user/emails')
const scopes = user.headers['x-oauth-scopes'] || '' const scopes = user.headers['x-oauth-scopes'] || ''
return { return {
user: user.data, user: {
...user.data, isConnected:
user.data.login !== undefined && user.data.login !== null && user.data.login !== ''
},
emails: emails.data, emails: emails.data,
scopes: scopes && scopes.split(',') scopes: scopes && scopes.split(',').map(scope => scope.trim())
} }
} catch (e) { } catch (e) {
return null return null
@ -1072,6 +616,7 @@ class DGitProvider extends Plugin {
} }
async remotecommits(input: remoteCommitsInputType): Promise<pagedCommits[]> { async remotecommits(input: remoteCommitsInputType): Promise<pagedCommits[]> {
const octokit = new Octokit({ const octokit = new Octokit({
auth: input.token auth: input.token
}) })
@ -1162,23 +707,4 @@ const addSlash = (file) => {
return file return file
} }
const normalize = (filesList) => {
const folders = []
const files = []
Object.keys(filesList || {}).forEach(key => {
if (filesList[key].isDirectory) {
folders.push({
filename: key,
data: filesList[key]
})
} else {
files.push({
filename: key,
data: filesList[key]
})
}
})
return [...folders, ...files]
}
module.exports = DGitProvider module.exports = DGitProvider

@ -6,8 +6,6 @@ import { FileSystemProvider } from '@remix-ui/workspace' // eslint-disable-line
import {Registry} from '@remix-project/remix-lib' import {Registry} from '@remix-project/remix-lib'
import { RemixdHandle } from '../plugins/remixd-handle' import { RemixdHandle } from '../plugins/remixd-handle'
import {PluginViewWrapper} from '@remix-ui/helper' import {PluginViewWrapper} from '@remix-ui/helper'
const { HardhatHandle } = require('../files/hardhat-handle.js')
const { FoundryHandle } = require('../files/foundry-handle.js')
const { TruffleHandle } = require('../files/truffle-handle.js') const { TruffleHandle } = require('../files/truffle-handle.js')
/* /*
@ -68,8 +66,6 @@ module.exports = class Filepanel extends ViewPlugin {
this.el.setAttribute('id', 'fileExplorerView') this.el.setAttribute('id', 'fileExplorerView')
this.remixdHandle = new RemixdHandle(this.fileProviders.localhost, appManager) this.remixdHandle = new RemixdHandle(this.fileProviders.localhost, appManager)
this.hardhatHandle = new HardhatHandle()
this.foundryHandle = new FoundryHandle()
this.truffleHandle = new TruffleHandle() this.truffleHandle = new TruffleHandle()
this.contentImport = contentImport this.contentImport = contentImport
this.workspaces = [] this.workspaces = []

@ -30,11 +30,14 @@ export type PanelConfiguration = {
export class Layout extends Plugin { export class Layout extends Plugin {
event: any event: any
panels: panels panels: panels
maximised: { [key: string]: boolean } enhanced: { [key: string]: boolean }
maximized: { [key: string]: boolean }
constructor () { constructor () {
super(profile) super(profile)
this.maximised = { this.maximized = {}
'dgit': true this.enhanced = {
'dgit': true,
'LearnEth': true
} }
this.event = new EventEmitter() this.event = new EventEmitter()
} }
@ -80,18 +83,30 @@ export class Layout extends Plugin {
}) })
this.on('sidePanel', 'focusChanged', async (name) => { this.on('sidePanel', 'focusChanged', async (name) => {
const current = await this.call('sidePanel', 'currentFocus') const current = await this.call('sidePanel', 'currentFocus')
if (this.maximised[current]) { if (this.enhanced[current]) {
this.event.emit('enhancesidepanel')
}
if (this.maximized[current]) {
this.event.emit('maximisesidepanel') this.event.emit('maximisesidepanel')
} else { }
if (!this.enhanced[current] && !this.maximized[current]) {
this.event.emit('resetsidepanel') this.event.emit('resetsidepanel')
} }
}) })
this.on('pinnedPanel', 'pinnedPlugin', async (name) => { this.on('pinnedPanel', 'pinnedPlugin', async (name) => {
const current = await this.call('pinnedPanel', 'currentFocus') const current = await this.call('pinnedPanel', 'currentFocus')
if (this.maximised[current]) { if (this.enhanced[current]) {
this.event.emit('enhancepinnedpanel')
}
if (this.maximized[current]) {
this.event.emit('maximisepinnedpanel') this.event.emit('maximisepinnedpanel')
} else { }
if (!this.enhanced[current] && !this.maximized[current]) {
this.event.emit('resetpinnedpanel') this.event.emit('resetpinnedpanel')
} }
}) })
@ -128,13 +143,13 @@ export class Layout extends Plugin {
async maximiseSidePanel () { async maximiseSidePanel () {
const current = await this.call('sidePanel', 'currentFocus') const current = await this.call('sidePanel', 'currentFocus')
this.maximised[current] = true this.maximized[current] = true
this.event.emit('maximisesidepanel') this.event.emit('maximisesidepanel')
} }
async maximisePinnedPanel () { async maximisePinnedPanel () {
const current = await this.call('pinnedPanel', 'currentFocus') const current = await this.call('pinnedPanel', 'currentFocus')
this.maximised[current] = true this.maximized[current] = true
this.event.emit('maximisepinnedpanel') this.event.emit('maximisepinnedpanel')
} }
@ -146,13 +161,13 @@ export class Layout extends Plugin {
async resetSidePanel () { async resetSidePanel () {
const current = await this.call('sidePanel', 'currentFocus') const current = await this.call('sidePanel', 'currentFocus')
this.maximised[current] = false this.enhanced[current] = false
this.event.emit('resetsidepanel') this.event.emit('resetsidepanel')
} }
async resetPinnedPanel () { async resetPinnedPanel () {
const current = await this.call('pinnedPanel', 'currentFocus') const current = await this.call('pinnedPanel', 'currentFocus')
this.maximised[current] = false this.enhanced[current] = false
this.event.emit('resetpinnedpanel') this.event.emit('resetpinnedpanel')
} }
} }

@ -0,0 +1,13 @@
import { ElectronPlugin } from '@remixproject/engine-electron';
export class FoundryHandleDesktop extends ElectronPlugin {
constructor() {
super({
displayName: 'foundry',
name: 'foundry',
description: 'electron foundry',
methods: ['sync', 'compile']
})
this.methods = ['sync', 'compile']
}
}

@ -0,0 +1,13 @@
import { ElectronPlugin } from '@remixproject/engine-electron';
export class HardhatHandleDesktop extends ElectronPlugin {
constructor() {
super({
displayName: 'hardhat',
name: 'hardhat',
description: 'electron hardhat',
methods: ['sync', 'compile']
})
this.methods = ['sync', 'compile']
}
}

@ -161,7 +161,7 @@ export default class CodeParserCompiler {
"*": ["evm.gasEstimates"] "*": ["evm.gasEstimates"]
} }
}, },
"evmVersion": state.evmVersion && state.evmVersion.toString() || "berlin", "evmVersion": state.evmVersion && state.evmVersion.toString() || "cancun",
} }
} }

@ -147,15 +147,16 @@ export class TemplatesSelectionPlugin extends ViewPlugin {
['ERC721', 'secondary'], ['ERC721', 'secondary'],
['ERC1155', 'primary'], ['ERC1155', 'primary'],
]} ]}
title='Template explorer' title='Workspace Templates'
description="Select a template to create a workspace or to add it to current workspace" description="Select a template to create a workspace or to add it to current workspace"
> >
{ {
templates(window._intl).map(template => { templates(window._intl, this).map(template => {
return <RemixUIGridSection return <RemixUIGridSection
plugin={this} plugin={this}
key={template.name} key={template.name}
title={template.name} title={template.name}
tooltipTitle={template.tooltip}
hScrollable={false} hScrollable={false}
> >
{template.items.map(item => { {template.items.map(item => {
@ -166,14 +167,14 @@ export class TemplatesSelectionPlugin extends ViewPlugin {
id={item.name} id={item.name}
searchKeywords={[item.displayName, item.description, template.name]} searchKeywords={[item.displayName, item.description, template.name]}
tagList={item.tagList} tagList={item.tagList}
classList='TSCellStyle' classList={'TSCellStyle'}
> >
<div className='d-flex justify-content-between h-100 flex-column'> <div className='d-flex justify-content-between h-100 flex-column'>
<div className='d-flex flex-column'> <div className='d-flex flex-column'>
<div> <div>
{item.description && <span className='text-dark'>{item.description}</span>} {item.description && <span className='text-dark'>{item.description}</span>}
</div> </div>
<div className='d-flex flex-wrap'> <div className='d-flex flex-wrap mb-2'>
{(item.opts && item.opts.upgradeable && item.opts.upgradeable === 'uupds') && <span className='badgeForCell badge text-secondary'>Upgradeable-UUPS</span>} {(item.opts && item.opts.upgradeable && item.opts.upgradeable === 'uupds') && <span className='badgeForCell badge text-secondary'>Upgradeable-UUPS</span>}
{(item.opts && item.opts.mintable) && <span className='badgeForCell text-secondary'>mintable</span>} {(item.opts && item.opts.mintable) && <span className='badgeForCell text-secondary'>mintable</span>}
{(item.opts && item.opts.burnable) && <span className='badgeForCell text-secondary'>burnable</span>} {(item.opts && item.opts.burnable) && <span className='badgeForCell text-secondary'>burnable</span>}
@ -211,6 +212,20 @@ export class TemplatesSelectionPlugin extends ViewPlugin {
</div> </div>
</RemixUIGridCell> </RemixUIGridCell>
})} })}
{ template.name === 'Cookbook' && <RemixUIGridCell
plugin={this}
title={"More from Cookbook"}
key={"cookbookMore"}
id={"cookBookMore"}
searchKeywords={["cookbook"]}
tagList={[]}
classList='TSCellStyle'
>
<div className='d-flex justify-content-between h-100 flex-column'>
<span className='pt-4 px-1 h6 text-dark'>{ template.description }</span>
<span style={{ cursor: 'pointer' }} className='mt-2 btn btn-sm border align-items-left' onClick={() => template.onClick() }>{ template.onClickLabel }</span>
</div>
</RemixUIGridCell> }
</RemixUIGridSection> </RemixUIGridSection>
})} })}
</RemixUIGridView> </RemixUIGridView>

@ -1,10 +1,9 @@
export const templates = (intl, plugin) => {
export const templates = (intl) => {
return [ return [
{ {
name: "Generic", name: "Generic",
items: [ items: [
{ value: "remixDefault", tagList: ["Solidity"], displayName: intl.formatMessage({ id: 'filePanel.basic' }), description: 'A default project' }, { value: "remixDefault", tagList: ["Solidity"], displayName: intl.formatMessage({ id: 'filePanel.basic' }), description: 'The default project' },
{ value: "blank", displayName: intl.formatMessage({ id: 'filePanel.blank' }), IsArtefact: true, description: 'A blank project' } { value: "blank", displayName: intl.formatMessage({ id: 'filePanel.blank' }), IsArtefact: true, description: 'A blank project' }
] ]
}, },
@ -15,102 +14,102 @@ export const templates = (intl) => {
value: "ozerc20", value: "ozerc20",
displayName: "ERC20", displayName: "ERC20",
tagList: ["ERC20", "Solidity"], tagList: ["ERC20", "Solidity"],
description: 'A simple ERC20 project' description: 'A simple fungible token contract'
}, },
{ {
value: "ozerc721", value: "ozerc20",
displayName: "ERC721 (NFT)", displayName: "ERC20",
tagList: ["ERC721", "Solidity"], description: "An ERC20 contract with:",
description: 'A simple ERC721 (aka NFT) project' tagList: ["Solidity"],
opts: {
mintable: true
}
}, },
{ {
value: "ozerc1155", value: "ozerc20",
tagList: ["Solidity"], displayName: "ERC20",
displayName: "ERC1155", description: "An ERC20 contract with:",
description: 'A simple ERC1155 (multi token) project' tagList: ["Solidity", "ERC20"],
opts: {
mintable: true,
burnable: true
},
}, },
{ {
value: "ozerc20", value: "ozerc20",
displayName: "ERC20", displayName: "ERC20",
description: "A standard interface for fungible tokens", description: "An ERC20 contract with:",
tagList: ["Solidity"],
opts: { opts: {
mintable: true mintable: true,
} pausable: true
},
tagList: ["ERC20", "Solidity"]
}, },
{ {
value: "ozerc721", value: "ozerc721",
displayName: "ERC721 (NFT)", displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard", tagList: ["ERC721", "Solidity"],
tagList: ["Solidity", "ERC721"], description: 'A simple non-fungible token (NFT) contract'
opts: {
mintable: true
}
}, },
{ {
value: "ozerc1155", value: "ozerc721",
displayName: "ERC1155", displayName: "ERC721 (NFT)",
tagList: ["Solidity"], description: "An ERC721 contract with:",
description: "A standard interface for contracts that manage multiple token types", tagList: ["Solidity", "ERC721"],
opts: { opts: {
mintable: true mintable: true
} }
}, },
{ {
value: "ozerc20", value: "ozerc721",
displayName: "ERC20", displayName: "ERC721 (NFT)",
description: "A standard interface for fungible tokens", description: "An ERC721 contract with:",
tagList: ["Solidity", "ERC20"],
opts: { opts: {
mintable: true, mintable: true,
burnable: true burnable: true
}, },
tagList: ["ERC721", "Solidity"]
}, },
{ {
value: "ozerc721", value: "ozerc721",
displayName: "ERC721 (NFT)", displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard", description: "An ERC721 contract with:",
opts: { opts: {
mintable: true, mintable: true,
burnable: true pausable: true
}, },
tagList: ["ERC721", "Solidity"] tagList: ["ERC721", "Solidity"]
}, },
{ {
value: "ozerc1155", value: "ozerc1155",
tagList: ["Solidity"],
displayName: "ERC1155", displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types", description: 'A simple multi token contract'
opts: {
mintable: true,
burnable: true
},
tagList: ["ERC1155", "Solidity"]
}, },
{ {
value: "ozerc20", value: "ozerc1155",
displayName: "ERC20", displayName: "ERC1155",
description: "A standard interface for fungible tokens", tagList: ["Solidity"],
description: "An ERC1155 contract with:",
opts: { opts: {
mintable: true, mintable: true
pausable: true }
},
tagList: ["ERC20", "Solidity"]
}, },
{ {
value: "ozerc721", value: "ozerc1155",
displayName: "ERC721 (NFT)", displayName: "ERC1155",
description: "Non-fungible Token Standard", description: "An ERC1155 contract with:",
opts: { opts: {
mintable: true, mintable: true,
pausable: true burnable: true
}, },
tagList: ["ERC721", "Solidity"] tagList: ["ERC1155", "Solidity"]
}, },
{ {
value: "ozerc1155", value: "ozerc1155",
displayName: "ERC1155", displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types", description: "An ERC1155 contract with:",
tagList: ["ERC20"], tagList: ["ERC1155"],
opts: { opts: {
mintable: true, mintable: true,
pausable: true pausable: true
@ -123,120 +122,120 @@ export const templates = (intl) => {
items: [ items: [
{ {
value: "ozerc20", value: "ozerc20",
displayName: "ERC20", displayName: "UUPS ERC20",
description: "A standard interface for fungible tokens", description: "A simple ERC20 contract using the Universal Upgradeable Proxy Standard (UUPS) pattern",
opts: { opts: {
upgradeable: 'uups' upgradeable: 'uups'
}, },
tagList: ["ERC20", "Solidity"] tagList: ["ERC20", "Solidity"]
}, },
{ {
value: "ozerc721", value: "ozerc20",
displayName: "ERC721 (NFT)", displayName: "UUPS ERC20",
description: "Non-fungible Token Standard", description: "UUPS ERC20 contract with:",
opts: { opts: {
upgradeable: 'uups' upgradeable: 'uups',
mintable: true
}, },
tagList: ["ERC721", "Solidity"] tagList: ["ERC20", "Solidity"]
}, },
{ {
value: "ozerc1155", value: "ozerc20",
displayName: "ERC1155", displayName: "UUPS ERC20",
description: "A standard interface for contracts that manage multiple token types", description: "UUPS ERC20 contract with:",
opts: { opts: {
upgradeable: 'uups' upgradeable: 'uups',
mintable: true,
burnable: true
}, },
tagList: ["ERC1155", "Solidity"] tagList: ["ERC20", "Solidity"]
}, },
{ {
value: "ozerc20", value: "ozerc20",
displayName: "ERC20", displayName: "UUPS ERC20",
description: "A standard interface for fungible tokens", description: "UUPS ERC20 contract with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true mintable: true,
pausable: true
}, },
tagList: ["ERC20", "Solidity"] tagList: ["ERC20", "Solidity"]
}, },
{ {
value: "ozerc721", value: "ozerc721",
displayName: "ERC721 (NFT)", displayName: "UUPS ERC721 (NFT)",
description: "Non-fungible Token Standard", description: "A simple UUPS ERC721 contract",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups'
mintable: true
}, },
tagList: ["ERC721", "Solidity"] tagList: ["ERC721", "Solidity"]
}, },
{ {
value: "ozerc1155", value: "ozerc721",
displayName: "ERC1155", displayName: "UUPS ERC721 (NFT)",
description: "A standard interface for contracts that manage multiple token types", description: "UUPS ERC721 contract with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true mintable: true
}, },
tagList: ["ERC1155", "Solidity"] tagList: ["ERC721", "Solidity"]
}, },
{ {
value: "ozerc20", value: "ozerc721",
displayName: "ERC20", displayName: "UUPS ERC721 (NFT)",
description: "A standard interface for fungible tokens", description: "UUPS ERC721 contract with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true, mintable: true,
burnable: true burnable: true
}, },
tagList: ["ERC20", "Solidity"] tagList: ["ERC721", "Solidity"]
}, },
{ {
value: "ozerc721", value: "ozerc721",
displayName: "ERC721 (NFT)", displayName: "UUPS ERC721 (NFT)",
description: "Non-fungible Token Standard", description: "UUPS ERC721 contract with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true, mintable: true,
burnable: true pausable: true
}, },
tagList: ["ERC721", "Solidity"] tagList: ["ERC721", "Solidity"]
}, },
{ {
value: "ozerc1155", value: "ozerc1155",
displayName: "ERC1155", displayName: "UUPS ERC1155",
description: "A standard interface for contracts that manage multiple token types", description: "A simple multi token contract using the UUPS pattern",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups'
mintable: true,
burnable: true
}, },
tagList: ["ERC1155", "Solidity"] tagList: ["ERC1155", "Solidity"]
}, },
{ {
value: "ozerc20", value: "ozerc1155",
displayName: "ERC20", displayName: "UUPS ERC1155",
description: "A standard interface for fungible tokens", description: "UUPS ERC1155 with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true, mintable: true
pausable: true
}, },
tagList: ["ERC20", "Solidity"] tagList: ["ERC1155", "Solidity"]
}, },
{ {
value: "ozerc721", value: "ozerc1155",
displayName: "ERC721 (NFT)", displayName: "UUPS ERC1155",
description: "Non-fungible Token Standard", description: "UUPS ERC1155 with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true, mintable: true,
pausable: true burnable: true
}, },
tagList: ["ERC721", "Solidity"] tagList: ["ERC1155", "Solidity"]
}, },
{ {
value: "ozerc1155", value: "ozerc1155",
displayName: "ERC1155", displayName: "UUPS ERC1155",
description: "A standard interface for contracts that manage multiple token types", description: "UUPS ERC1155 with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true, mintable: true,
@ -246,8 +245,8 @@ export const templates = (intl) => {
}, },
{ {
value: "ozerc1155", value: "ozerc1155",
displayName: "ERC1155", displayName: "UUPS ERC1155",
description: "A standard interface for contracts that manage multiple token types", description: "UUPS ERC1155 with:",
opts: { opts: {
upgradeable: 'uups', upgradeable: 'uups',
mintable: true, mintable: true,
@ -259,23 +258,82 @@ export const templates = (intl) => {
] ]
}, },
{ {
name: "OxProject", name: "Cookbook",
tooltip: "Cookbook is a Smart Contract Search Tool. Click here to open Cookbook and browse Contracts.",
onClick: async () => {
await plugin.call('manager', 'activatePlugin', 'cookbookdev')
plugin.call('menuicons', 'showContent', 'cookbookdev')
},
onClickLabel: 'Open Cookbook Plugin',
description: 'Discover more templates!',
items: [
{
value: "token-sale",
displayName: 'Token Sale',
description: "ERC20 token sale contact. Sell tokens for ETH"
},
{
value: "simple-nft-sale",
displayName: 'Simple Nft Sale',
description: "ERC721 NFT with an adjustable price & to mint free NFTs"
},
{
value: "Azuki-ERC721A-NFT-Sale-basic",
displayName: 'Azuki ERC721A NFT Sale basic',
description: "An implementation of the ERC721A standard"
},
{
value: "Azuki-ERC721A-NFT-Sale",
displayName: 'Azuki ERC721A NFT Sale',
description: "An extension of the ERC721A standard with wallet limit"
},
{
value: "token-staking-with-infinite-rewards",
displayName: 'Token Staking with infinite rewards',
description: "Token staking contract to reward ERC20 tokens for every token staked"
},
{
value: "nft-staking-with-infinite-rewards",
displayName: 'NFT Staking with infinite rewards',
description: "NFT staking contract to reward exact number of ERC20 tokens per day"
},
{
value: "basic-dao",
displayName: 'Basic DAO',
description: "A very simple implementation of a DAO"
},
{
value: "soulbound-nft",
displayName: 'Soulbound NFT',
description: "ERC721 Soulbound NFT with no transfer capability"
},
{ value: "multi-collection-nft-with-burnable-nfts-and-pausable-transfers",
displayName: 'Multi collection NFT',
description: "Multi collection NFT with:",
opts: {
burnable: true,
pausable: true
}, },
]
},
{
name: "0xProject",
items: [ items: [
{ value: "zeroxErc20", displayName: "ERC20", tagList: ["ERC20", "Solidity"], description: "A standard interface for fungible tokens by 0xProject" } { value: "zeroxErc20", displayName: "ERC20", tagList: ["ERC20", "Solidity"], description: "A fungible token contract by 0xProject" }
] ]
}, },
{ {
name: "Gnosis Safe", name: "Gnosis Safe",
items: [ items: [
{ value: "gnosisSafeMultisig", tagList: ["Solidity"], displayName: intl.formatMessage({ id: 'filePanel.multiSigWallet' }), description: 'Deploy or Customize the Gnosis Safe.' } { value: "gnosisSafeMultisig", tagList: ["Solidity"], displayName: intl.formatMessage({ id: 'filePanel.multiSigWallet' }), description: 'Deploy or customize the Gnosis Safe MultiSig Wallet' }
] ]
}, },
{ {
name: "Circom ZKP", name: "Circom ZKP",
items: [ items: [
{ value: "semaphore", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.semaphore' }), description: 'Run a ZK Semaphore circom circuit.' }, { value: "semaphore", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.semaphore' }), description: 'Semaphore protocol for casting a message as a provable group member' },
{ value: "hashchecker", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.hashchecker' }), description: 'Run a ZK Hash checker circom circuit.' }, { value: "hashchecker", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.hashchecker' }), description: 'Hash checker Circom circuit' },
{ value: "rln", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.rln' }), description: 'Run a Rate Limiting Nullifier circom circuit.' } { value: "rln", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.rln' }), description: 'Rate Limiting Nullifier Circom circuit' }
] ]
}, },
{ {
@ -285,7 +343,7 @@ export const templates = (intl) => {
value: "sindriScripts", value: "sindriScripts",
tagList: ["ZKP"], tagList: ["ZKP"],
displayName: intl.formatMessage({ id: 'filePanel.addscriptsindri' }), displayName: intl.formatMessage({ id: 'filePanel.addscriptsindri' }),
description: 'Use the Sindri API to compile and generate proof.' description: 'Use the Sindri API to compile and generate proofs'
}, },
], ],
}, },
@ -294,12 +352,12 @@ export const templates = (intl) => {
items: [ items: [
{ value: "uniswapV4Template", { value: "uniswapV4Template",
displayName: intl.formatMessage({ id: 'filePanel.uniswapV4Template' }), displayName: intl.formatMessage({ id: 'filePanel.uniswapV4Template' }),
description: 'Use an Uniswap hook' description: 'Use a Uniswap hook'
}, },
{ {
value: "breakthroughLabsUniswapv4Hooks", value: "breakthroughLabsUniswapv4Hooks",
displayName: intl.formatMessage({ id: 'filePanel.breakthroughLabsUniswapv4Hooks' }), displayName: intl.formatMessage({ id: 'filePanel.breakthroughLabsUniswapv4Hooks' }),
description: 'Use an Uniswap hook developed by Breakthrough Labs' description: 'Use a Uniswap hook developed by Breakthrough Labs'
}, },
{ {
value: "uniswapV4HookBookMultiSigSwapHook", value: "uniswapV4HookBookMultiSigSwapHook",
@ -315,12 +373,12 @@ export const templates = (intl) => {
value: "contractCreate2Factory", value: "contractCreate2Factory",
tagList: ["Solidity"], tagList: ["Solidity"],
displayName: intl.formatMessage({ id: 'filePanel.addcreate2solidityfactory' }), displayName: intl.formatMessage({ id: 'filePanel.addcreate2solidityfactory' }),
description: 'Factory for deploying a Contract using the CREATE2 opcode.' description: 'Factory for deploying a contract using the CREATE2 opcode'
}, },
{ {
value: "contractDeployerScripts", value: "contractDeployerScripts",
displayName: intl.formatMessage({ id: 'filePanel.addscriptdeployer' }), displayName: intl.formatMessage({ id: 'filePanel.addscriptdeployer' }),
description: 'Script for deploying a Contract using the CREATE2 opcode.' description: 'Script for deploying a contract using the CREATE2 opcode'
} }
] ]
}, },
@ -330,25 +388,25 @@ export const templates = (intl) => {
{ {
value: "etherscanScripts", value: "etherscanScripts",
displayName: intl.formatMessage({ id: 'filePanel.addscriptetherscan' }), displayName: intl.formatMessage({ id: 'filePanel.addscriptetherscan' }),
description: 'Script for verifying a Contract in Etherscan.' description: 'Script for verifying a Contract in Etherscan'
}, },
], ],
}, },
{ {
name: 'Github Actions', name: 'GitHub Actions',
items: [ items: [
{ value: "runJsTestAction", { value: "runJsTestAction",
displayName: intl.formatMessage({ id: 'filePanel.tssoltestghaction' }), displayName: intl.formatMessage({ id: 'filePanel.tssoltestghaction' }),
description: 'A Mocha Chai Test Workflow in a GitHub CI.' description: 'A Mocha Chai test workflow in a GitHub CI'
}, },
{ value: "runSolidityUnittestingAction", { value: "runSolidityUnittestingAction",
displayName: intl.formatMessage({ id: 'filePanel.solghaction' }), displayName: intl.formatMessage({ id: 'filePanel.solghaction' }),
description: 'Run a Solidity Unittest Workflow in a GitHub CI.' description: 'Run a Solidity unit test workflow in a GitHub CI'
}, },
{ {
value: "runSlitherAction", value: "runSlitherAction",
displayName: intl.formatMessage({ id: 'filePanel.slitherghaction' }), displayName: intl.formatMessage({ id: 'filePanel.slitherghaction' }),
description: 'Run a Slither Security Analysis in a GitHub CI.' description: 'Run a Slither security analysis in a GitHub CI'
} }
], ],
IsArtefact: true IsArtefact: true

@ -25,7 +25,7 @@ const profile = {
methods: [] methods: []
} }
type ProvidersSection = `Injected` | 'Remix VMs' | 'Externals' type ProvidersSection = `Injected` | 'Remix VMs' | 'Externals' | 'Remix forked VMs'
export class EnvironmentExplorer extends ViewPlugin { export class EnvironmentExplorer extends ViewPlugin {
providers: { [key in ProvidersSection]: Provider[] } providers: { [key in ProvidersSection]: Provider[] }
@ -39,6 +39,7 @@ export class EnvironmentExplorer extends ViewPlugin {
this.providers = { this.providers = {
'Injected': [], 'Injected': [],
'Remix VMs': [], 'Remix VMs': [],
'Remix forked VMs': [],
'Externals': [] 'Externals': []
} }
} }
@ -52,6 +53,8 @@ export class EnvironmentExplorer extends ViewPlugin {
addProvider (provider: Provider) { addProvider (provider: Provider) {
if (provider.isInjected) { if (provider.isInjected) {
this.providers['Injected'].push(provider) this.providers['Injected'].push(provider)
} else if (provider.isForkedVM) {
this.providers['Remix forked VMs'].push(provider)
} else if (provider.isVM) { } else if (provider.isVM) {
this.providers['Remix VMs'].push(provider) this.providers['Remix VMs'].push(provider)
} else { } else {
@ -81,7 +84,8 @@ export class EnvironmentExplorer extends ViewPlugin {
this.providers = { this.providers = {
'Injected': [], 'Injected': [],
'Remix VMs': [], 'Remix VMs': [],
'Externals': [] 'Externals': [],
'Remix forked VMs': []
} }
for (const [key, provider] of Object.entries(this.providersFlat)) { for (const [key, provider] of Object.entries(this.providersFlat)) {
this.addProvider(provider) this.addProvider(provider)
@ -167,6 +171,40 @@ export class EnvironmentExplorer extends ViewPlugin {
<div>{provider.description}</div> <div>{provider.description}</div>
</RemixUIGridCell> </RemixUIGridCell>
})}</RemixUIGridSection> })}</RemixUIGridSection>
<RemixUIGridSection
plugin={this}
title='Deploy to an In-browser forked Virtual Machine.'
hScrollable={false}
>{this.providers['Remix forked VMs'].map(provider => {
return <RemixUIGridCell
plugin={this}
title={provider.displayName}
logos={provider.logos}
classList='EECellStyle'
searchKeywords={['Remix VMs', provider.name, provider.displayName, provider.title, provider.description]}
pinned={this.pinnedProviders.includes(provider.name)}
key={provider.name}
id={provider.name}
pinStateCallback={async (pinned: boolean) => {
if (pinned) {
this.emit('providerPinned', provider.name, provider)
this.call('notification', 'toast', `"${provider.displayName}" has been added to the Environment list of the Deploy & Run Transactions plugin.`)
return true
}
const providerName = await this.call('blockchain', 'getProvider')
if (providerName !== provider.name) {
this.emit('providerUnpinned', provider.name, provider)
this.call('notification', 'toast', `"${provider.displayName}" has been removed from the Environment list of the Deploy & Run Transactions plugin.`)
return true
} else {
this.call('notification', 'toast', 'Cannot unpin the current selected provider')
return false
}
}}
>
<div>{provider.description}</div>
</RemixUIGridCell>
})}</RemixUIGridSection>
<RemixUIGridSection <RemixUIGridSection
plugin={this} plugin={this}
title='Deploy to an external Provider.' title='Deploy to an external Provider.'

@ -1,4 +1,4 @@
import Web3 from 'web3' import { Web3 } from 'web3'
import { InjectedProviderDefault } from './injected-provider-default' import { InjectedProviderDefault } from './injected-provider-default'
export class InjectedCustomProvider extends InjectedProviderDefault { export class InjectedCustomProvider extends InjectedProviderDefault {

@ -1,6 +1,6 @@
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { InjectedCustomProvider } from './injected-custom-provider' import { InjectedCustomProvider } from './injected-custom-provider'
import Web3 from 'web3' import { Web3 } from 'web3'
const profile = { const profile = {
name: 'injected-ephemery-testnet-provider', name: 'injected-ephemery-testnet-provider',

@ -9,7 +9,7 @@ import { QueryParams } from '@remix-project/remix-lib'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import { compilerConfigChangedToastMsg, compileToastMsg } from '@remix-ui/helper' import { compilerConfigChangedToastMsg, compileToastMsg } from '@remix-ui/helper'
import { isNative } from '../../remixAppManager' import { isNative } from '../../remixAppManager'
import { Registry } from '@remix-project/remix-lib'
const profile = { const profile = {
name: 'solidity', name: 'solidity',
displayName: 'Solidity compiler', displayName: 'Solidity compiler',
@ -90,6 +90,10 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
return this.fileManager.mode return this.fileManager.mode
} }
isDesktop () {
return Registry.getInstance().get('platform').api.isDesktop()
}
/** /**
* set the compiler configuration * set the compiler configuration
* This function is used by remix-plugin compiler API. * This function is used by remix-plugin compiler API.

@ -1,4 +1,4 @@
import Web3 from 'web3' import { Web3 } from 'web3'
import { DebuggerUI } from '@remix-ui/debugger-ui' // eslint-disable-line import { DebuggerUI } from '@remix-ui/debugger-ui' // eslint-disable-line
import { DebuggerApiMixin } from '@remix-ui/debugger-ui' import { DebuggerApiMixin } from '@remix-ui/debugger-ui'
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'

@ -21,5 +21,6 @@
"circuit.exportVerifierContract": "Export verifier contract", "circuit.exportVerifierContract": "Export verifier contract",
"circuit.exportVerificationKey": "Export verification key", "circuit.exportVerificationKey": "Export verification key",
"circuit.exportVerifierCalldata": "Export verifier calldata", "circuit.exportVerifierCalldata": "Export verifier calldata",
"circuit.exportWtnsJson": "Export witness as JSON",
"circuit.runSetup": "Run setup" "circuit.runSetup": "Run setup"
} }

@ -1,4 +1,6 @@
{ {
"electron.openFolder": "Open Folder", "electron.openFolder": "Open Folder",
"electron.recentFolders": "Recent Folders" "electron.recentFolders": "Recent Folders",
"electron.gitClone": "Clone a Git Repository",
"electron.openFolderMessage": "In order to use Git features, you can open a folder or clone a repository."
} }

@ -2,6 +2,9 @@
"filePanel.displayName": "File explorer", "filePanel.displayName": "File explorer",
"filePanel.workspace": "WORKSPACES", "filePanel.workspace": "WORKSPACES",
"filePanel.create": "Create", "filePanel.create": "Create",
"filePanel.createLabel": "Create Using Template",
"filePanel.createBlank":"Create Blank",
"filePanel.create.desktop": "Create Project", "filePanel.create.desktop": "Create Project",
"filePanel.clone": "Clone", "filePanel.clone": "Clone",
"filePanel.download": "Download", "filePanel.download": "Download",
@ -9,7 +12,8 @@
"filePanel.restore": "Restore", "filePanel.restore": "Restore",
"filePanel.name": "Name", "filePanel.name": "Name",
"filePanel.save": "Save", "filePanel.save": "Save",
"filePanel.workspace.create": "Create Workspace", "filePanel.workspace.create": "Create Workspace Using Template",
"filePanel.workspace.createBlank": "Create Blank Workspace",
"filePanel.workspace.create.desktop": "Create project in new folder", "filePanel.workspace.create.desktop": "Create project in new folder",
"filePanel.workspace.rename": "Rename Workspace", "filePanel.workspace.rename": "Rename Workspace",
"filePanel.workspace.save_workspace": "Save Workspace", "filePanel.workspace.save_workspace": "Save Workspace",
@ -67,7 +71,7 @@
"filePanel.createNewFile": "Create new file", "filePanel.createNewFile": "Create new file",
"filePanel.createNewFolder": "Create new folder", "filePanel.createNewFolder": "Create new folder",
"filePanel.publishToGist": "Publish to Gist", "filePanel.publishToGist": "Publish to Gist",
"filePanel.workspace.publishToGist": "Publish workspace to GitHub gist", "filePanel.workspace.publishToGist": "Publish Workspace to GitHub Gist",
"filePanel.uploadFile": "Open a File from your File System", "filePanel.uploadFile": "Open a File from your File System",
"filePanel.uploadFolder": "Upload folder", "filePanel.uploadFolder": "Upload folder",
"filePanel.updateGist": "Update Gist", "filePanel.updateGist": "Update Gist",
@ -142,5 +146,7 @@
"filePanel.movingFolderFailedMsg": "Unexpected error while moving folder: {src}", "filePanel.movingFolderFailedMsg": "Unexpected error while moving folder: {src}",
"filePanel.workspaceActions": "Workspace actions", "filePanel.workspaceActions": "Workspace actions",
"filePanel.saveCodeSample": "This code-sample workspace will not be persisted. Click here to save it.", "filePanel.saveCodeSample": "This code-sample workspace will not be persisted. Click here to save it.",
"filePanel.logInGithub": "Sign in to GitHub.",
"filePanel.gitHubLoggedAs": "Signed in as {githubuser}",
"filePanel.updateSubmodules": "Update all submodules of repository. Click to pull dependencies." "filePanel.updateSubmodules": "Update all submodules of repository. Click to pull dependencies."
} }

@ -16,5 +16,6 @@
"git.unstageall": "unstage all", "git.unstageall": "unstage all",
"git.stageall": "stage all", "git.stageall": "stage all",
"git.noremote": "this repo has no remotes", "git.noremote": "this repo has no remotes",
"git.init": "Initialize repository" "git.init": "Initialize repository",
"git.setup": "Setup git"
} }

@ -0,0 +1,3 @@
{
"gitui.openFolderMessage": "In order to use Git features, you can open a folder or clone a repository."
}

@ -7,9 +7,9 @@
"home.learnMore": "Learn more", "home.learnMore": "Learn more",
"home.here": "more", "home.here": "more",
"home.featured": "Featured", "home.featured": "Featured",
"home.jumpIntoWeb3": "JUMP INTO WEB3", "home.learnEthPromoTitle": "LearnEth: Tutorials inside Remix",
"home.jumpIntoWeb3More": "More", "home.learnEthPromoButton": "Start Learning",
"home.jumpIntoWeb3Text": "Remix IDE is part of the Remix Project, a rich toolset that can be used for the entire journey of contract development by users of any knowledge level. Learn more on the Remix Project website.", "home.learnEthPromoText": "Check out tutorials on Remix, Solidity, and other Web3 projects. Great for all skill levels.",
"home.remixYouTube": "WATCH TO LEARN", "home.remixYouTube": "WATCH TO LEARN",
"home.remixYouTubeText1": "Video Tips from the Remix Team", "home.remixYouTubeText1": "Video Tips from the Remix Team",
"home.remixYouTubeMore": "Watch", "home.remixYouTubeMore": "Watch",
@ -37,6 +37,7 @@
"home.ozerc1155TemplateDesc": "Create an ERC1155 token by importing OpenZeppelin library.", "home.ozerc1155TemplateDesc": "Create an ERC1155 token by importing OpenZeppelin library.",
"home.gnosisSafeMultisigTemplateDesc": "Create Multi-Signature wallets using this template.", "home.gnosisSafeMultisigTemplateDesc": "Create Multi-Signature wallets using this template.",
"home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.", "home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.",
"home.learnEthPluginDesc": "Learn about Remix, Solidity, and other Web3 projects.",
"home.learn": "Learn", "home.learn": "Learn",
"home.learnEth1": "Remix Basics", "home.learnEth1": "Remix Basics",
"home.learnEth1Desc": "An introduction to Remix's interface and basic operations.", "home.learnEth1Desc": "An introduction to Remix's interface and basic operations.",

@ -4,10 +4,10 @@
"remixUiTabs.tooltipText3": "Select .sol or .yul file to compile OR a .ts or .js file to run", "remixUiTabs.tooltipText3": "Select .sol or .yul file to compile OR a .ts or .js file to run",
"remixUiTabs.tooltipText4": "To explain a contract, choose a .sol file", "remixUiTabs.tooltipText4": "To explain a contract, choose a .sol file",
"remixUiTabs.tooltipText5": "Explain the contract(s) in current file [BETA]", "remixUiTabs.tooltipText5": "Explain the contract(s) in current file [BETA]",
"remixUiTabs.tooltipText6": "Enable Remix AI Copilot [BETA]", "remixUiTabs.tooltipText6": "Enable RemixAI Copilot [BETA]",
"remixUiTabs.tooltipText7": "Disable Remix AI Copilot [BETA]", "remixUiTabs.tooltipText7": "Disable RemixAI Copilot [BETA]",
"remixUiTabs.tooltipText8": "Remix AI Tools Documentation", "remixUiTabs.tooltipText8": "Remix AI Tools Documentation",
"remixUiTabs.tooltipTextDisabledCopilot": "To use Remix AI Copilot, choose a .sol file", "remixUiTabs.tooltipTextDisabledCopilot": "To use RemixAI Copilot, choose a .sol file",
"remixUiTabs.zoomOut": "Zoom out", "remixUiTabs.zoomOut": "Zoom out",
"remixUiTabs.zoomIn": "Zoom in" "remixUiTabs.zoomIn": "Zoom in"
} }

@ -1,16 +1,18 @@
{ {
"settings.displayName": "Settings", "settings.displayName": "Settings",
"settings.reset": "Reset to Default settings", "settings.reset": "Reset to Default settings",
"settings.general": "General settings", "settings.general": "General",
"settings.generateContractMetadataText": "Generate contract metadata. Generate a JSON file in the contract folder. Allows to specify library addresses the contract depends on. If nothing is specified, Remix deploys libraries automatically.", "settings.generateContractMetadataText": "Generate contract metadata",
"settings.generateContractMetadataTooltip": "Generate a JSON file in the contract folder. Allows to specify library addresses the contract depends on. If nothing is specified, Remix deploys libraries automatically.",
"settings.ethereunVMText": "Always use the Remix VM at load", "settings.ethereunVMText": "Always use the Remix VM at load",
"settings.wordWrapText": "Word wrap in editor", "settings.wordWrapText": "Word wrap in editor",
"settings.useAutoCompleteText": "Enable code completion in editor.", "settings.useAutoCompleteText": "Enable code completion in editor",
"settings.useShowGasInEditorText": "Display gas estimates in editor.", "settings.useShowGasInEditorText": "Display gas estimates in editor",
"settings.displayErrorsText": "Display errors in editor while typing.", "settings.displayErrorsText": "Display errors in editor while typing",
"settings.matomoAnalytics": "Enable Matomo Analytics. We do not collect personally identifiable information (PII). The info is used to improve the site’s UX & UI. See more about ", "settings.matomoAnalytics": "Enable Matomo Analytics. See",
"settings.enablePersonalModeText": " Enable Personal Mode for web3 provider. Transaction sent over Web3 will use the web3.personal API.\n", "settings.matomoAnalyticsTooltip": "We do not collect personally identifiable information (PII). The info is used to improve the site’s UX & UI.",
"settings.warnText": "Be sure the endpoint is opened before enabling it. This mode allows a user to provide a passphrase in the Remix interface without having to unlock the account. Although this is very convenient, you should completely trust the backend you are connected to (Geth, Parity, ...). Remix never persists any passphrase", "settings.enablePersonalModeText": " Enable Personal Mode for web3 provider",
"settings.enablePersonalModeTooltip": "Transaction sent over Web3 will use the web3.personal API. Be sure the endpoint is opened before enabling it. This mode allows a user to provide a passphrase in the Remix interface without having to unlock the account. Although this is very convenient, you should completely trust the backend you are connected to (Geth, Parity, ...). Remix never persists any passphrase",
"settings.gitAccessTokenTitle": "Github Credentials", "settings.gitAccessTokenTitle": "Github Credentials",
"settings.gitAccessTokenText": "The access token is used to publish a Gist and retrieve GitHub contents. You may need to input username/email.", "settings.gitAccessTokenText": "The access token is used to publish a Gist and retrieve GitHub contents. You may need to input username/email.",
"settings.gitAccessTokenText2":"Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only 'create gist' permission", "settings.gitAccessTokenText2":"Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only 'create gist' permission",

@ -165,7 +165,7 @@ export class RunTab extends ViewPlugin {
'foundry-provider': ['assets/img/foundry.png'] 'foundry-provider': ['assets/img/foundry.png']
} }
const addProvider = async (position, name, displayName, isInjected, isVM, fork = '', dataId = '', title = '') => { const addProvider = async (position, name, displayName, isInjected, isVM, fork = '', dataId = '', title = '', forkedVM = false) => {
await this.call('blockchain', 'addProvider', { await this.call('blockchain', 'addProvider', {
position, position,
options: {}, options: {},
@ -176,6 +176,7 @@ export class RunTab extends ViewPlugin {
logos: logos[name], logos: logos[name],
fork, fork,
isInjected, isInjected,
isForkedVM: forkedVM,
isVM, isVM,
title, title,
init: async function () { init: async function () {
@ -237,9 +238,9 @@ export class RunTab extends ViewPlugin {
await addProvider(51, 'vm-paris', 'Remix VM (Paris)', false, true, 'paris', 'settingsVMParisMode', titleVM) await addProvider(51, 'vm-paris', 'Remix VM (Paris)', false, true, 'paris', 'settingsVMParisMode', titleVM)
await addProvider(52, 'vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', titleVM) await addProvider(52, 'vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', titleVM)
await addProvider(53, 'vm-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM) await addProvider(53, 'vm-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM)
await addProvider(2, 'vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'cancun', 'settingsVMMainnetMode', titleVM) await addProvider(2, 'vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'cancun', 'settingsVMMainnetMode', titleVM, true)
await addProvider(3, 'vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'cancun', 'settingsVMSepoliaMode', titleVM) await addProvider(3, 'vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'cancun', 'settingsVMSepoliaMode', titleVM, true)
await addProvider(4, 'vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM) await addProvider(4, 'vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM, true)
// wallet connect // wallet connect
await addProvider(6, 'walletconnect', 'WalletConnect', false, false) await addProvider(6, 'walletconnect', 'WalletConnect', false, false)

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

@ -39,7 +39,7 @@ export type Transaction = {
to: string to: string
value: string value: string
data: string data: string
gasLimit: number gasLimit: string
useCall: boolean useCall: boolean
timestamp?: number timestamp?: number
} }
@ -55,6 +55,7 @@ export type Provider = {
description?: string description?: string
isInjected: boolean isInjected: boolean
isVM: boolean isVM: boolean
isForkedVM: boolean
title: string title: string
init: () => Promise<void> init: () => Promise<void>
provider:{ provider:{
@ -780,6 +781,8 @@ export class Blockchain extends Plugin {
sendTransaction(tx: Transaction) { sendTransaction(tx: Transaction) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.executionContext.detectNetwork((error, network) => { this.executionContext.detectNetwork((error, network) => {
tx.gasLimit = '0x0' // force using gas estimation
if (error) return reject(error) if (error) return reject(error)
if (network.name === 'Main' && network.id === '1') { if (network.name === 'Main' && network.id === '1') {
return reject(new Error('It is not allowed to make this action against mainnet')) return reject(new Error('It is not allowed to make this action against mainnet'))

@ -1,6 +1,6 @@
/* global ethereum */ /* global ethereum */
'use strict' 'use strict'
import Web3 from 'web3' import { Web3 } from 'web3'
import { execution } from '@remix-project/remix-lib' import { execution } from '@remix-project/remix-lib'
import EventManager from '../lib/events' import EventManager from '../lib/events'
import { bytesToHex } from '@ethereumjs/util' import { bytesToHex } from '@ethereumjs/util'

@ -1,4 +1,4 @@
import Web3 from 'web3' import { Web3 } from 'web3'
import { hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util' import { hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util'
import { ExecutionContext } from '../execution-context' import { ExecutionContext } from '../execution-context'

@ -1,4 +1,4 @@
import Web3 from 'web3' import { Web3 } from 'web3'
import { hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util' import { hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util'
import { Personal } from 'web3-eth-personal' import { Personal } from 'web3-eth-personal'
import { ExecutionContext } from '../execution-context' import { ExecutionContext } from '../execution-context'

@ -1,4 +1,4 @@
import Web3, { FMT_BYTES, FMT_NUMBER, LegacySendAsyncProvider } from 'web3' import { Web3, FMT_BYTES, FMT_NUMBER, LegacySendAsyncProvider } from 'web3'
import { fromWei, toBigInt } from 'web3-utils' import { fromWei, toBigInt } from 'web3-utils'
import { privateToAddress, hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util' import { privateToAddress, hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util'
import { extend, JSONRPCRequestPayload, JSONRPCResponseCallback } from '@remix-project/remix-simulator' import { extend, JSONRPCRequestPayload, JSONRPCResponseCallback } from '@remix-project/remix-simulator'

@ -0,0 +1,14 @@
const fs = require('fs-extra');
const path = require('path');
exports.default = async function (context) {
console.log('Running after-pack hook', context);
const resourcesPath = context.appOutDir;
console.log('resourcesPath', resourcesPath);
console.log('context outdir', context.appOutDir);
// Copy the node-pty module to the app folder
await fs.copy(
path.join('./node_modules', 'node-pty'),
path.join(resourcesPath, 'node_modules', 'node-pty')
);
};

@ -0,0 +1,12 @@
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['src/main.ts', 'src/preload.ts'], // Your TypeScript entry point
outdir: 'build', // Output bundled file
bundle: true, // Bundle all dependencies
platform: 'node', // Target Node.js platform
external: ['electron', 'fsevents', 'node-pty'], // Exclude native modules
target: ['node20'], // Match the Node.js version for Electron
tsconfig: 'tsconfig.json', // Your TypeScript config
minify: false, // Optional: Minify for production
}).catch(() => process.exit(1));

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -e
TEST_EXITCODE=0
yarn run build:e2e && node ./splice_tests.js
TESTFILES=$(node ./splice_tests.js | grep -i 'git' | circleci tests split --split-by=timings)
for TESTFILE in $TESTFILES; do
yarn run test --use-isogit --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || yarn run test --use-isogit --test ./build-e2e/remixdesktop/test/tests/app/${TESTFILE} || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ]
then
exit 1
fi

@ -0,0 +1,24 @@
#!/bin/bash
# Read the version from package.json
version=$(awk -F'"' '/"version":/ {print $4}' package.json)
# Determine the command to run based on the version
if [[ $version == *"beta"* ]]; then
command="yarn esbuild -c beta.json"
elif [[ $version == *"alpha"* ]]; then
command="yarn esbuild -c alpha.json"
elif [[ $version == *"insiders"* ]]; then
command="yarn esbuild -c insiders.json"
else
command="yarn esbuild -c latest.json"
fi
# Append any arguments passed in CLI
for arg in "$@"; do
command+=" $arg"
done
# Print and run the command
echo "Running command: $command"
$command

@ -0,0 +1,24 @@
#!/bin/bash
# Read the version from package.json
version=$(awk -F'"' '/"version":/ {print $4}' package.json)
# Determine the command to run based on the version
if [[ $version == *"beta"* ]]; then
command="yarn tscbuild -c beta.json"
elif [[ $version == *"alpha"* ]]; then
command="yarn tscbuild -c alpha.json"
elif [[ $version == *"insiders"* ]]; then
command="yarn tscbuild -c insiders.json"
else
command="yarn tscbuild -c latest.json"
fi
# Append any arguments passed in CLI
for arg in "$@"; do
command+=" $arg"
done
# Print and run the command
echo "Running command: $command"
$command

@ -0,0 +1,24 @@
#!/bin/bash
# Read the version from package.json
version=$(awk -F'"' '/"version":/ {print $4}' package.json)
# Determine the command to run based on the version
if [[ $version == *"beta"* ]]; then
command="yarn dist -c beta.json"
elif [[ $version == *"alpha"* ]]; then
command="yarn dist -c alpha.json"
elif [[ $version == *"insiders"* ]]; then
command="yarn dist -c insiders.json"
else
command="yarn dist -c latest.json"
fi
# Append any arguments passed in CLI
for arg in "$@"; do
command+=" $arg"
done
# Print and run the command
echo "Running command: $command"
$command

@ -0,0 +1,248 @@
import { Profile } from "@remixproject/plugin-utils";
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import chokidar from 'chokidar'
import { ElectronBasePluginRemixdClient } from "../lib/remixd"
import fs from 'fs'
import * as utils from '../lib/utils'
import { basename, join } from "path";
import { spawn } from "child_process";
const profile: Profile = {
name: 'foundry',
displayName: 'electron foundry',
description: 'electron foundry',
}
export class FoundryPlugin extends ElectronBasePlugin {
clients: any[]
constructor() {
super(profile, clientProfile, FoundryPluginClient)
this.methods = [...super.methods]
}
}
const clientProfile: Profile = {
name: 'foundry',
displayName: 'electron foundry',
description: 'electron foundry',
methods: ['sync', 'compile']
}
class FoundryPluginClient extends ElectronBasePluginRemixdClient {
watcher: chokidar.FSWatcher
warnlog: boolean
buildPath: string
cachePath: string
logTimeout: NodeJS.Timeout
processingTimeout: NodeJS.Timeout
async onActivation(): Promise<void> {
console.log('Foundry plugin activated')
this.call('terminal', 'log', { type: 'log', value: 'Foundry plugin activated' })
this.on('fs' as any, 'workingDirChanged', async (path: string) => {
console.log('workingDirChanged foundry', path)
this.currentSharedFolder = path
this.startListening()
})
this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir')
if(this.currentSharedFolder) this.startListening()
}
startListening() {
this.buildPath = utils.absolutePath('out', this.currentSharedFolder)
this.cachePath = utils.absolutePath('cache', this.currentSharedFolder)
console.log('Foundry plugin checking for', this.buildPath, this.cachePath)
if (fs.existsSync(this.buildPath) && fs.existsSync(this.cachePath)) {
this.listenOnFoundryCompilation()
} else {
this.listenOnFoundryFolder()
}
}
listenOnFoundryFolder() {
console.log('Foundry out folder doesn\'t exist... waiting for the compilation.')
try {
if (this.watcher) this.watcher.close()
this.watcher = chokidar.watch(this.currentSharedFolder, { depth: 1, ignorePermissionErrors: true, ignoreInitial: true })
// watch for new folders
this.watcher.on('addDir', (path: string) => {
console.log('add dir foundry', path)
if (fs.existsSync(this.buildPath) && fs.existsSync(this.cachePath)) {
this.listenOnFoundryCompilation()
}
})
} catch (e) {
console.log(e)
}
}
compile() {
return new Promise((resolve, reject) => {
const cmd = `forge build`
const options = { cwd: this.currentSharedFolder, shell: true }
const child = spawn(cmd, options)
let result = ''
let error = ''
child.stdout.on('data', (data) => {
const msg = `[Foundry Compilation]: ${data.toString()}`
console.log('\x1b[32m%s\x1b[0m', msg)
result += msg + '\n'
})
child.stderr.on('data', (err) => {
error += `[Foundry Compilation]: ${err.toString()} \n`
})
child.on('close', () => {
if (error && result) resolve(error + result)
else if (error) reject(error)
else resolve(result)
})
})
}
checkPath() {
if (!fs.existsSync(this.buildPath) || !fs.existsSync(this.cachePath)) {
this.listenOnFoundryFolder()
return false
}
if (!fs.existsSync(join(this.cachePath, 'solidity-files-cache.json'))) return false
return true
}
private async processArtifact() {
if (!this.checkPath()) return
const folderFiles = await fs.promises.readdir(this.buildPath) // "out" folder
try {
const cache = JSON.parse(await fs.promises.readFile(join(this.cachePath, 'solidity-files-cache.json'), { encoding: 'utf-8' }))
// name of folders are file names
for (const file of folderFiles) {
const path = join(this.buildPath, file) // out/Counter.sol/
const compilationResult = {
input: {},
output: {
contracts: {},
sources: {}
},
inputSources: { sources: {}, target: '' },
solcVersion: null,
compilationTarget: null
}
compilationResult.inputSources.target = file
await this.readContract(path, compilationResult, cache)
this.emit('compilationFinished', compilationResult.compilationTarget, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion)
}
clearTimeout(this.logTimeout)
this.logTimeout = setTimeout(() => {
// @ts-ignore
this.call('terminal', 'log', { type: 'log', value: `receiving compilation result from Foundry. Select a file to populate the contract interaction interface.` })
console.log('Syncing compilation result from Foundry')
}, 1000)
} catch (e) {
console.log(e)
}
}
async triggerProcessArtifact() {
// prevent multiple calls
clearTimeout(this.processingTimeout)
this.processingTimeout = setTimeout(async () => await this.processArtifact(), 1000)
}
listenOnFoundryCompilation() {
try {
console.log('Foundry out folder exists... processing the artifact.')
if (this.watcher) this.watcher.close()
this.watcher = chokidar.watch(this.cachePath, { depth: 0, ignorePermissionErrors: true, ignoreInitial: true })
this.watcher.on('change', async () => await this.triggerProcessArtifact())
this.watcher.on('add', async () => await this.triggerProcessArtifact())
this.watcher.on('unlink', async () => await this.triggerProcessArtifact())
// process the artifact on activation
this.triggerProcessArtifact()
} catch (e) {
console.log(e)
}
}
async readContract(contractFolder, compilationResultPart, cache) {
const files = await fs.promises.readdir(contractFolder)
for (const file of files) {
const path = join(contractFolder, file)
const content = await fs.promises.readFile(path, { encoding: 'utf-8' })
compilationResultPart.inputSources.sources[file] = { content }
await this.feedContractArtifactFile(file, content, compilationResultPart, cache)
}
}
async feedContractArtifactFile(path, content, compilationResultPart, cache) {
const contentJSON = JSON.parse(content)
const contractName = basename(path).replace('.json', '')
let sourcePath = ''
if (contentJSON?.metadata?.settings?.compilationTarget) {
for (const key in contentJSON.metadata.settings.compilationTarget) {
if (contentJSON.metadata.settings.compilationTarget[key] === contractName) {
sourcePath = key
break
}
}
}
if (!sourcePath) return
const currentCache = cache.files[sourcePath]
if (!currentCache.artifacts[contractName]) return
// extract source and version
const metadata = contentJSON.metadata
if (metadata.compiler && metadata.compiler.version) {
compilationResultPart.solcVersion = metadata.compiler.version
} else {
compilationResultPart.solcVersion = ''
console.log('\x1b[32m%s\x1b[0m', 'compiler version not found, please update Foundry to the latest version.')
}
if (metadata.sources) {
for (const path in metadata.sources) {
const absPath = utils.absolutePath(path, this.currentSharedFolder)
try {
const content = await fs.promises.readFile(absPath, { encoding: 'utf-8' })
compilationResultPart.input[path] = { content }
} catch (e) {
compilationResultPart.input[path] = { content: '' }
}
}
} else {
console.log('\x1b[32m%s\x1b[0m', 'sources input not found, please update Foundry to the latest version.')
}
compilationResultPart.compilationTarget = sourcePath
// extract data
if (!compilationResultPart.output['sources'][sourcePath]) compilationResultPart.output['sources'][sourcePath] = {}
compilationResultPart.output['sources'][sourcePath] = {
ast: contentJSON['ast'],
id: contentJSON['id']
}
if (!compilationResultPart.output['contracts'][sourcePath]) compilationResultPart.output['contracts'][sourcePath] = {}
contentJSON.bytecode.object = contentJSON.bytecode.object.replace('0x', '')
contentJSON.deployedBytecode.object = contentJSON.deployedBytecode.object.replace('0x', '')
compilationResultPart.output['contracts'][sourcePath][contractName] = {
abi: contentJSON.abi,
evm: {
bytecode: contentJSON.bytecode,
deployedBytecode: contentJSON.deployedBytecode,
methodIdentifiers: contentJSON.methodIdentifiers
}
}
}
async sync() {
console.log('syncing Foundry with Remix...')
this.processArtifact()
}
}

@ -0,0 +1,220 @@
import { Profile } from "@remixproject/plugin-utils";
import { ElectronBasePlugin, ElectronBasePluginClient } from "@remixproject/plugin-electron"
import chokidar from 'chokidar'
import { ElectronBasePluginRemixdClient } from "../lib/remixd"
import fs from 'fs'
import * as utils from '../lib/utils'
import { basename, join } from "path";
import { spawn } from "child_process";
const profile: Profile = {
name: 'hardhat',
displayName: 'electron slither',
description: 'electron slither',
}
export class HardhatPlugin extends ElectronBasePlugin {
clients: any[]
constructor() {
super(profile, clientProfile, HardhatPluginClient)
this.methods = [...super.methods]
}
}
const clientProfile: Profile = {
name: 'hardhat',
displayName: 'electron hardhat',
description: 'electron hardhat',
methods: ['sync', 'compile']
}
class HardhatPluginClient extends ElectronBasePluginRemixdClient {
watcher: chokidar.FSWatcher
warnlog: boolean
buildPath: string
cachePath: string
logTimeout: NodeJS.Timeout
processingTimeout: NodeJS.Timeout
async onActivation(): Promise<void> {
console.log('Hardhat plugin activated')
this.call('terminal', 'log', { type: 'log', value: 'Hardhat plugin activated' })
this.on('fs' as any, 'workingDirChanged', async (path: string) => {
console.log('workingDirChanged hardhat', path)
this.currentSharedFolder = path
this.startListening()
})
this.currentSharedFolder = await this.call('fs' as any, 'getWorkingDir')
if(this.currentSharedFolder) this.startListening()
}
startListening() {
this.buildPath = utils.absolutePath('artifacts/contracts', this.currentSharedFolder)
if (fs.existsSync(this.buildPath)) {
this.listenOnHardhatCompilation()
} else {
console.log('If you are using Hardhat, run `npx hardhat compile` or run the compilation with `Enable Hardhat Compilation` checked from the Remix IDE.')
this.listenOnHardHatFolder()
}
}
compile(configPath: string) {
return new Promise((resolve, reject) => {
const cmd = `npx hardhat compile --config ${utils.normalizePath(configPath)}`
const options = { cwd: this.currentSharedFolder, shell: true }
const child = spawn(cmd, options)
let result = ''
let error = ''
child.stdout.on('data', (data) => {
const msg = `[Hardhat Compilation]: ${data.toString()}`
console.log('\x1b[32m%s\x1b[0m', msg)
result += msg + '\n'
})
child.stderr.on('data', (err) => {
error += `[Hardhat Compilation]: ${err.toString()} \n`
})
child.on('close', () => {
if (error && result) resolve(error + result)
else if (error) reject(error)
else resolve(result)
})
})
}
checkPath() {
if (!fs.existsSync(this.buildPath)) {
this.listenOnHardHatFolder()
return false
}
return true
}
private async processArtifact() {
console.log('processing artifact')
if (!this.checkPath()) return
// resolving the files
const folderFiles = await fs.promises.readdir(this.buildPath)
const targetsSynced = []
// name of folders are file names
for (const file of folderFiles) { // ["artifacts/contracts/Greeter.sol/"]
const contractFilePath = join(this.buildPath, file)
const stat = await fs.promises.stat(contractFilePath)
if (!stat.isDirectory()) continue
const files = await fs.promises.readdir(contractFilePath)
const compilationResult = {
input: {},
output: {
contracts: {},
sources: {}
},
solcVersion: null,
target: null
}
for (const file of files) {
if (file.endsWith('.dbg.json')) { // "artifacts/contracts/Greeter.sol/Greeter.dbg.json"
const stdFile = file.replace('.dbg.json', '.json')
const contentStd = await fs.promises.readFile(join(contractFilePath, stdFile), { encoding: 'utf-8' })
const contentDbg = await fs.promises.readFile(join(contractFilePath, file), { encoding: 'utf-8' })
const jsonDbg = JSON.parse(contentDbg)
const jsonStd = JSON.parse(contentStd)
compilationResult.target = jsonStd.sourceName
targetsSynced.push(compilationResult.target)
const path = join(contractFilePath, jsonDbg.buildInfo)
const content = await fs.promises.readFile(path, { encoding: 'utf-8' })
await this.feedContractArtifactFile(content, compilationResult)
}
if (compilationResult.target) {
// we are only interested in the contracts that are in the target of the compilation
compilationResult.output = {
...compilationResult.output,
contracts: { [compilationResult.target]: compilationResult.output.contracts[compilationResult.target] }
}
this.emit('compilationFinished', compilationResult.target, { sources: compilationResult.input }, 'soljson', compilationResult.output, compilationResult.solcVersion)
}
}
}
clearTimeout(this.logTimeout)
this.logTimeout = setTimeout(() => {
this.call('terminal', 'log', { value: 'receiving compilation result from Hardhat. Select a file to populate the contract interaction interface.', type: 'log' })
if (targetsSynced.length) {
console.log(`Processing artifacts for files: ${[...new Set(targetsSynced)].join(', ')}`)
// @ts-ignore
this.call('terminal', 'log', { type: 'log', value: `synced with Hardhat: ${[...new Set(targetsSynced)].join(', ')}` })
} else {
console.log('No artifacts to process')
// @ts-ignore
this.call('terminal', 'log', { type: 'log', value: 'No artifacts from Hardhat to process' })
}
}, 1000)
}
listenOnHardHatFolder() {
console.log('Hardhat artifacts folder doesn\'t exist... waiting for the compilation.')
try {
if (this.watcher) this.watcher.close()
this.watcher = chokidar.watch(this.currentSharedFolder, { depth: 2, ignorePermissionErrors: true, ignoreInitial: true })
// watch for new folders
this.watcher.on('addDir', (path: string) => {
console.log('add dir hardhat', path)
if (fs.existsSync(this.buildPath)) {
this.listenOnHardhatCompilation()
}
})
} catch (e) {
console.log('listenOnHardHatFolder', e)
}
}
async triggerProcessArtifact() {
console.log('triggerProcessArtifact')
// prevent multiple calls
clearTimeout(this.processingTimeout)
this.processingTimeout = setTimeout(async () => await this.processArtifact(), 1000)
}
listenOnHardhatCompilation() {
try {
console.log('listening on Hardhat compilation...', this.buildPath)
if (this.watcher) this.watcher.close()
this.watcher = chokidar.watch(this.buildPath, { depth: 1, ignorePermissionErrors: true, ignoreInitial: true })
this.watcher.on('change', async () => await this.triggerProcessArtifact())
this.watcher.on('add', async () => await this.triggerProcessArtifact())
this.watcher.on('unlink', async () => await this.triggerProcessArtifact())
// process the artifact on activation
this.processArtifact()
} catch (e) {
console.log('listenOnHardhatCompilation', e)
}
}
async sync() {
console.log('syncing from Hardhat')
this.processArtifact()
}
async feedContractArtifactFile(artifactContent, compilationResultPart) {
const contentJSON = JSON.parse(artifactContent)
compilationResultPart.solcVersion = contentJSON.solcVersion
for (const file in contentJSON.input.sources) {
const source = contentJSON.input.sources[file]
const absPath = join(this.currentSharedFolder, file)
if (fs.existsSync(absPath)) { // if not that is a lib
const contentOnDisk = await fs.promises.readFile(absPath, { encoding: 'utf-8' })
if (contentOnDisk === source.content) {
compilationResultPart.input[file] = source
compilationResultPart.output['sources'][file] = contentJSON.output.sources[file]
compilationResultPart.output['contracts'][file] = contentJSON.output.contracts[file]
if (contentJSON.output.errors && contentJSON.output.errors.length) {
compilationResultPart.output['errors'] = contentJSON.output.errors.filter(error => error.sourceLocation.file === file)
}
}
}
}
}
}

@ -1,9 +0,0 @@
export type branch = {
name: string
remote: remote
}
export type remote = {
name: string
url: string
}

@ -0,0 +1,195 @@
import { spawn, ChildProcess } from "child_process"
export async function getBranches(path: string): Promise<string> {
return new Promise((resolve, reject) => {
const git = spawn('git', ['branch'], { cwd: path })
let branches = ''
git.stdout.on('data', function (data) {
console.log('stdout git branches', data.toString())
branches += data.toString()
})
git.stderr.on('data', function (data) {
console.log('stderr git branches', data.toString())
reject(data.toString())
})
git.on('close', function () {
resolve(branches)
})
})
}
export async function getGitLog(path: string): Promise<string> {
return new Promise((resolve, reject) => {
const git = spawn('git', ['log'], { cwd: path })
let logs = ''
git.stdout.on('data', function (data) {
logs += data.toString()
})
git.stderr.on('err', function (data) {
reject(data.toString())
})
git.on('close', function () {
resolve(logs)
})
})
}
export async function cloneOnServer(repo: string, path: string, name: string = 'bare') {
console.log('cloning', repo, path)
return new Promise((resolve, reject) => {
const git = spawn(`rm -rf ${name} && git`, ['clone', repo], { cwd: path, shell: true, detached: true });
git.stdout.on('data', function (data) {
console.log('stdout data cloning', data.toString());
if (data.toString().includes('done')) {
resolve(git);
}
});
git.stderr.on('data', function (data) {
console.log('stderr data cloning', data.toString());
if (data.toString().includes('into')) {
setTimeout(() => {
resolve(git);
}, 5000)
}
});
git.on('error', (error) => {
reject(`Process error: ${error.message}`);
});
git.on('exit', (code, signal) => {
if (code !== 0) {
reject(`Process exited with code: ${code} and signal: ${signal}`);
}
});
});
}
export async function onLocalGitRepoAddFile(path: string, file: string) {
console.log('adding file', file)
return new Promise((resolve, reject) => {
const git = spawn('touch', [file], { cwd: path });
git.stdout.on('data', function (data) {
console.log('stdout data adding file', data.toString());
if (data.toString().includes('done')) {
resolve(git);
}
});
git.stderr.on('data', function (data) {
console.error('stderr adding file', data.toString());
reject(data.toString());
});
git.on('error', (error) => {
reject(`Process error: ${error.message}`);
});
git.on('exit', (code, signal) => {
if (code !== 0) {
reject(`Process exited with code: ${code} and signal: ${signal}`);
} else {
resolve(git);
}
});
});
}
export async function onLocalGitRepoPush(path: string, branch: string = 'master') {
console.log('pushing', path)
return new Promise((resolve, reject) => {
const git = spawn('git', ['push', 'origin', branch], { cwd: path, shell: true, detached: true });
git.stdout.on('data', function (data) {
console.log('stdout data pushing', data.toString());
if (data.toString().includes('done')) {
resolve(git);
}
});
git.stderr.on('data', function (data) {
console.error('stderr data pushing', data.toString());
if (data.toString().includes(branch)) {
resolve(git);
}
});
git.on('error', (error) => {
reject(`Process error: ${error.message}`);
});
git.on('exit', (code, signal) => {
if (code !== 0) {
reject(`Process exited with code: ${code} and signal: ${signal}`);
} else {
resolve(git);
}
});
});
}
export async function createCommitOnLocalServer(path: string, message: string) {
console.log('committing', message, path)
return new Promise((resolve, reject) => {
const git = spawn('git add . && git', ['commit', '-m', message], { cwd: path, shell: true, detached: true });
git.stdout.on('data', function (data) {
console.log('data stdout committing', data.toString());
if (data.toString().includes(message)) {
setTimeout(() => {
resolve(git);
}, 1000)
}
});
git.stderr.on('data', function (data) {
console.error('data commiting', data.toString());
reject(data.toString());
});
git.on('error', (error) => {
console.error('error', error);
reject(`Process error: ${error.message}`);
});
git.on('exit', (code, signal) => {
if (code !== 0) {
console.error('exit', code, signal);
reject(`Process exited with code: ${code} and signal: ${signal}`);
} else {
resolve(git);
}
});
});
}
export async function spawnGitServer(path: string): Promise<ChildProcess> {
console.log(process.cwd())
try {
const server = spawn('yarn && sh setup.sh && yarn start:server', [`${path}`], { cwd: process.cwd() + '/../remix-ide-e2e/src/githttpbackend/', shell: true, detached: true })
console.log('spawned', server.stdout.closed, server.stderr.closed)
return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes('is listening')
|| data.toString().includes('address already in use')
) {
console.log('resolving')
resolve(server)
}
})
server.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString())
})
})
} catch (e) {
console.log(e)
}
}

@ -0,0 +1,157 @@
import { NightwatchBrowser } from 'nightwatch'
import { ChildProcess, spawn, execSync } from 'child_process'
import { homedir } from 'os'
import path from 'path'
import os from 'os'
const projectDir = path.join('remix-desktop-test-' + Date.now().toString())
const dir = '/tmp/' + projectDir
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
done()
},
installFoundry: function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
await downloadFoundry()
await installFoundry()
await initFoundryProject()
done()
})
},
addScript: function (browser: NightwatchBrowser) {
// run script in console
browser.executeAsync(function (dir, done) {
(window as any).electronAPI.openFolderInSameWindow(dir + '/hello_foundry/').then(done)
}, [dir], () => {
console.log('done window opened')
})
.waitForElementVisible('*[data-id="treeViewDivDraggableItemfoundry.toml"]', 10000)
},
compile: function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
console.log('generating compilation result')
await buildFoundryProject()
done()
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Foundry').before(60000)
let contractAaddress
browser.clickLaunchIcon('filePanel')
.openFile('src')
.openFile('src/Counter.sol')
.clickLaunchIcon('udapp')
.selectContract('Counter')
.createContract('')
.getAddressAtPosition(0, (address) => {
console.log(contractAaddress)
contractAaddress = address
})
.clickInstance(0)
.clickFunction('increment - transact (not payable)')
.perform((done) => {
browser.testConstantFunction(contractAaddress, 'number - call', null, '0:\nuint256: 1').perform(() => {
done()
})
})
}
}
async function downloadFoundry(): Promise<void> {
console.log('downloadFoundry', process.cwd())
try {
const server = spawn('curl -L https://foundry.paradigm.xyz | bash', [], { cwd: process.cwd(), shell: true, detached: true })
return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes("simply run 'foundryup' to install Foundry")
|| data.toString().includes("foundryup: could not detect shell, manually add")
) {
console.log('resolving')
resolve()
}
})
server.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString())
})
})
} catch (e) {
console.log(e)
}
}
async function installFoundry(): Promise<void> {
console.log('installFoundry', process.cwd())
try {
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && foundryup', [], { cwd: process.cwd(), shell: true, detached: true })
return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) {
console.log(data.toString())
if (
data.toString().includes("foundryup: done!")
) {
console.log('resolving')
resolve()
}
})
server.stderr.on('err', function (data) {
console.log(data.toString())
reject(data.toString())
})
})
} catch (e) {
console.log(e)
}
}
async function initFoundryProject(): Promise<void> {
console.log('initFoundryProject', homedir())
try {
if (process.env.CIRCLECI) {
spawn('git config --global user.email \"you@example.com\"', [], { cwd: homedir(), shell: true, detached: true })
spawn('git config --global user.name \"Your Name\"', [], { cwd: homedir(), shell: true, detached: true })
}
spawn('mkdir ' + projectDir, [], { cwd: '/tmp/', shell: true, detached: true })
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && forge init hello_foundry', [], { cwd: dir, shell: true, detached: true })
server.stdout.pipe(process.stdout)
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
server.stderr.on('err', function (data) {
console.log('err', data.toString())
})
server.stdout.on('data', function (data) {
console.log('data', data.toString())
})
})
} catch (e) {
console.log(e)
}
}
async function buildFoundryProject(): Promise<void> {
console.log('buildFoundryProject', homedir())
try {
const server = spawn('export PATH="' + homedir() + '/.foundry/bin:$PATH" && forge build', [], { cwd: dir + '/hello_foundry', shell: true, detached: true })
server.stdout.pipe(process.stdout)
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
})
} catch (e) {
console.log(e)
}
}
module.exports = {
...{}//...process.platform.startsWith('linux') ? tests : {}
}

@ -0,0 +1,203 @@
import { ChildProcess, spawn } from "child_process"
import kill from 'tree-kill'
import { Nightwatch, NightwatchBrowser } from "nightwatch"
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git"
let gitserver: ChildProcess
/*
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) )
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD
/ GROUP 2: branch operations CREATE & PUBLISH
/ GROUP 3: file operations rename delete
*/
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.hideToolTips()
done()
},
after: function (browser: NightwatchBrowser) {
browser.perform((done) => {
console.log('kill server', gitserver.pid)
kill(gitserver.pid)
done()
})
},
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
gitserver = await spawnGitServer('/tmp/')
console.log('working directory', process.cwd())
done()
})
},
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(5000)
.waitForElementVisible('*[data-id="cloneButton"]')
.click('*[data-id="cloneButton"]')
.pause(1000)
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git')
.click('[data-id="fileSystem-modal-footer-ok-react"]')
.pause(5000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[1])
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]')
.hideToolTips()
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]')
},
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.
clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
.modalFooterOKClick('github-credentials-error')
},
// GROUP 1
'check file added #group1 #group3': function (browser: NightwatchBrowser) {
browser
.addFile('test.txt', { content: 'hello world' }, 'README.md')
.clickLaunchIcon('dgit')
.pause(1000)
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible({
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]')
.pause(1000)
.click('*[data-id="addToGitChangestest.txt"]')
.waitForElementVisible({
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.setValue('*[data-id="commitMessage"]', 'testcommit')
.click('*[data-id="commitButton"]')
},
'look at the commit #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commits-panel"]')
.waitForElementPresent({
selector: '//*[@data-id="commit-summary-testcommit-ahead"]',
locateStrategy: 'xpath'
})
},
'sync the commit #group1': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.waitForElementVisible('*[data-id="sourcecontrol-panel"]')
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible('*[data-id="syncButton"]')
.click('*[data-id="syncButton"]')
.pause(2000)
.waitForElementVisible('*[data-id="commitButton"]')
.click('*[data-id="commits-panel"]')
.waitForElementPresent({
selector: '//*[@data-id="commit-summary-testcommit-"]',
locateStrategy: 'xpath'
})
},
'check the log #group1': async function (browser: NightwatchBrowser) {
const logs = await getGitLog('/tmp/git/bare.git')
console.log(logs)
browser.assert.ok(logs.includes('testcommit'))
},
'change a file #group1': function (browser: NightwatchBrowser) {
browser.
openFile('test.txt').
pause(1000).
setEditorValue('changes', null)
},
'stage changed file #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible({
selector: "//*[@data-status='modified-unstaged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]')
.click('*[data-id="addToGitChangestest.txt"]')
.waitForElementVisible({
selector: "//*[@data-status='modified-staged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.setValue('*[data-id="commitMessage"]', 'testcommit2')
.click('*[data-id="commitButton"]')
},
'push the commit #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commands-panel"]')
.waitForElementVisible('*[data-id="sourcecontrol-push"]')
.click('*[data-id="sourcecontrol-push"]')
.pause(2000)
.click('*[data-id="commits-panel"]')
.waitForElementPresent({
selector: '//*[@data-id="commit-summary-testcommit2-"]',
locateStrategy: 'xpath'
}).pause(2000)
},
'check the log for testcommit2 #group1': async function (browser: NightwatchBrowser) {
const logs = await getGitLog('/tmp/git/bare.git')
console.log(logs)
browser.assert.ok(logs.includes('testcommit2'))
},
'clone locally and add a file and push #group1': async function (browser: NightwatchBrowser) {
await cloneOnServer('http://localhost:6868/bare.git', '/tmp/')
await onLocalGitRepoAddFile('/tmp/bare/', 'test2.txt')
await createCommitOnLocalServer('/tmp/bare/', 'testlocal')
await onLocalGitRepoPush('/tmp/bare/', 'master')
},
'run a git fetch #group1': function (browser: NightwatchBrowser) {
browser
.pause(2000)
.click('*[data-id="commands-panel"]')
.waitForElementVisible('*[data-id="sourcecontrol-fetch-branch"]')
.click('*[data-id="sourcecontrol-fetch-branch"]')
.pause(2000)
.click('*[data-id="commits-panel"]')
.waitForElementVisible('*[data-id="commits-panel-behind"]')
.click('*[data-id="commits-panel-behind"]')
.waitForElementPresent({
selector: '//*[@data-id="commit-summary-testlocal-"]',
locateStrategy: 'xpath'
})
},
'run pull from the header #group1': function (browser: NightwatchBrowser) {
browser.
click('*[data-id="sourcecontrol-button-pull"]')
.waitForElementNotPresent('*[data-id="commits-panel-behind"]')
},
'check if the file is added #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest2.txt"]')
},
}
const useIsoGit = process.argv.includes('--use-isogit');
if (process.platform.startsWith('win')) {
module.exports = {}
}
else
module.exports = { ...tests }

@ -0,0 +1,181 @@
import { ChildProcess, spawn } from "child_process"
import kill from 'tree-kill'
import { Nightwatch, NightwatchBrowser } from "nightwatch"
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git"
let gitserver: ChildProcess
/*
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) )
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD
/ GROUP 2: branch operations CREATE & PUBLISH
/ GROUP 3: file operations rename delete
*/
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.hideToolTips()
done()
},
after: function (browser: NightwatchBrowser) {
browser.perform((done) => {
console.log('kill server', gitserver.pid)
kill(gitserver.pid)
done()
})
},
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
gitserver = await spawnGitServer('/tmp/')
console.log('working directory', process.cwd())
done()
})
},
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(5000)
.waitForElementVisible('*[data-id="cloneButton"]')
.click('*[data-id="cloneButton"]')
.pause(1000)
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git')
.click('[data-id="fileSystem-modal-footer-ok-react"]')
.pause(5000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[1])
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]')
.hideToolTips()
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]')
},
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.
clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
.modalFooterOKClick('github-credentials-error')
},
// GROUP 2
'create a branch #group2': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="branches-panel"]')
.waitForElementVisible('*[data-id="newbranchname"]')
.setValue('*[data-id="newbranchname"]', 'testbranch')
.click('*[data-id="sourcecontrol-create-branch"]')
.waitForElementVisible('*[data-id="branches-current-branch-testbranch"]')
.pause(1000)
},
'check if the branch is in the filePanel #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.pause(1000)
.waitForElementVisible('*[data-id="workspaceGitBranchesDropdown"]')
.click('[data-id="workspaceGitBranchesDropdown"]')
.waitForElementVisible('*[data-id="workspaceGit-testbranch"]')
.expect.element('[data-id="workspaceGit-testbranch"]').text.to.contain('✓ ')
},
'publish the branch #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="sourcecontrol-panel"]')
.click('*[data-id="sourcecontrol-panel"]')
.pause(1000)
.click('*[data-id="publishBranchButton"]')
.pause(2000)
.waitForElementNotVisible('*[data-id="publishBranchButton"]')
},
'check if the branch is published #group2': async function (browser: NightwatchBrowser) {
const branches = await getBranches('/tmp/git/bare.git')
browser.assert.ok(branches.includes('testbranch'))
},
'add file to new branch #group2': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.addFile('test.txt', { content: 'hello world' }, 'README.md')
.clickLaunchIcon('dgit')
.pause(2000)
.waitForElementVisible({
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]')
.pause(1000)
.click('*[data-id="addToGitChangestest.txt"]')
.waitForElementVisible({
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.setValue('*[data-id="commitMessage"]', 'testcommit')
.click('*[data-id="commitButton"]')
.pause(1000)
},
'check if the commit is ahead in the branches list #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="branches-panel"]')
.click('*[data-id="branches-panel"]')
.waitForElementVisible('*[data-id="branches-current-branch-testbranch"]')
.click({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branches-current-branch-testbranch']",
locateStrategy: 'xpath',
suppressNotFoundErrors: true
})
.click({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='commits-panel-ahead']",
locateStrategy: 'xpath',
suppressNotFoundErrors: true
})
.click({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branchdifference-commits-testbranch-ahead']//*[@data-id='commit-summary-testcommit-ahead']",
locateStrategy: 'xpath',
})
.click({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branchdifference-commits-testbranch-ahead']//*[@data-id='commit-change-added-test.txt']",
locateStrategy: 'xpath',
})
.click({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='local-branch-commits-testbranch']//*[@data-id='commit-summary-testcommit-ahead']",
locateStrategy: 'xpath',
})
.waitForElementVisible({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='local-branch-commits-testbranch']//*[@data-id='commit-change-added-test.txt']",
locateStrategy: 'xpath',
})
},
'switch back to master #group2': function (browser: NightwatchBrowser) {
browser
.click({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branches-toggle-branch-master']",
locateStrategy: 'xpath',
})
.waitForElementVisible({
selector: "//*[@data-id='branches-panel-content']//*[@data-id='branches-toggle-current-branch-master']",
locateStrategy: 'xpath',
})
},
'check if test file is gone #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementNotPresent('*[data-id="treeViewLitreeViewItemtest.txt"]')
}
}
const useIsoGit = process.argv.includes('--useIsoGit');
if (process.platform.startsWith('win')) {
module.exports = {}
}
else
module.exports = { ...tests }

@ -0,0 +1,153 @@
import { ChildProcess, spawn } from "child_process"
import kill from 'tree-kill'
import { Nightwatch, NightwatchBrowser } from "nightwatch"
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git"
let gitserver: ChildProcess
/*
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) )
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD
/ GROUP 2: branch operations CREATE & PUBLISH
/ GROUP 3: file operations rename delete
*/
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.hideToolTips()
done()
},
after: function (browser: NightwatchBrowser) {
browser.perform((done) => {
console.log('kill server', gitserver.pid)
kill(gitserver.pid)
done()
})
},
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
gitserver = await spawnGitServer('/tmp/')
console.log('working directory', process.cwd())
done()
})
},
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(5000)
.waitForElementVisible('*[data-id="cloneButton"]')
.click('*[data-id="cloneButton"]')
.pause(1000)
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git')
.click('[data-id="fileSystem-modal-footer-ok-react"]')
.pause(5000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[1])
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]')
.hideToolTips()
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]')
},
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.
clickLaunchIcon('dgit')
.saveScreenshot('./reports/screenshots/gitui.png')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.saveScreenshot('./reports/screenshots/gitui2.png')
.pause(1000)
.saveScreenshot('./reports/screenshots/gitui3.png')
.click('*[data-id="github-panel"]')
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
.pause(1000)
.modalFooterOKClick('github-credentials-error')
},
'check file added #group1 #group3': function (browser: NightwatchBrowser) {
browser
.addFile('test.txt', { content: 'hello world' }, 'README.md')
.clickLaunchIcon('dgit')
.pause(1000)
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible({
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]')
.pause(1000)
.click('*[data-id="addToGitChangestest.txt"]')
.waitForElementVisible({
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.setValue('*[data-id="commitMessage"]', 'testcommit')
.click('*[data-id="commitButton"]')
},
// group 3
'rename a file #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.txt"]')
.click('*[data-id="treeViewLitreeViewItemtest.txt"]')
.renamePath('test.txt', 'test_rename', 'test_rename.txt')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest_rename.txt"]')
.pause(1000)
},
'stage renamed file #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.waitForElementVisible({
selector: "//*[@data-status='deleted-unstaged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]')
.waitForElementVisible({
selector: "//*[@data-status='new-untracked' and @data-file='/test_rename.txt']",
locateStrategy: 'xpath'
})
.pause(2000)
.click('*[data-id="sourcecontrol-add-all"]')
.waitForElementVisible({
selector: "//*[@data-status='added-staged' and @data-file='/test_rename.txt']",
locateStrategy: 'xpath'
})
},
'undo the rename #group3': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="unStageStagedtest_rename.txt"]')
.click('*[data-id="unStageStagedtest_rename.txt"]')
.pause(1000)
.click('*[data-id="unDoStagedtest.txt"]')
.pause(1000)
.waitForElementNotPresent({
selector: "//*[@data-file='/test.txt']",
locateStrategy: 'xpath'
})
},
'check if file is returned #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtest.txt"]')
},
}
const useIsoGit = process.argv.includes('--use-isogit');
if (process.platform.startsWith('win')) {
module.exports = {}
}
else
module.exports = { ...tests }

@ -0,0 +1,200 @@
import { ChildProcess, spawn } from "child_process"
import kill from 'tree-kill'
import { Nightwatch, NightwatchBrowser } from "nightwatch"
import { spawnGitServer, getGitLog, cloneOnServer, onLocalGitRepoAddFile, createCommitOnLocalServer, onLocalGitRepoPush, getBranches } from "../../lib/git"
let gitserver: ChildProcess
/*
/ uses the git-http-backend package to create a git server ( if needed kill the server: kill -9 $(sudo lsof -t -i:6868) )
/ GROUP 1: file operations PUSH PULL COMMIT SYNC FETCH CLONE ADD
/ GROUP 2: branch operations CREATE & PUBLISH
/ GROUP 3: file operations rename delete
*/
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.hideToolTips()
done()
},
after: function (browser: NightwatchBrowser) {
browser.perform((done) => {
console.log('kill server', gitserver.pid)
kill(gitserver.pid)
done()
})
},
'run server #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
gitserver = await spawnGitServer('/tmp/')
console.log('working directory', process.cwd())
done()
})
},
'clone a repo #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(5000)
.waitForElementVisible('*[data-id="cloneButton"]')
.click('*[data-id="cloneButton"]')
.pause(1000)
.waitForElementVisible('[data-id="fileSystemModalDialogModalBody-react"]')
.click('[data-id="fileSystemModalDialogModalBody-react"]')
.waitForElementVisible('[data-id="modalDialogCustomPromptTextClone"]')
.setValue('*[data-id="modalDialogCustomPromptTextClone"]', 'http://localhost:6868/bare.git')
.click('[data-id="fileSystem-modal-footer-ok-react"]')
.pause(5000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[1])
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]')
.hideToolTips()
})
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]')
},
'Update settings for git #group1 #group2 #group3': function (browser: NightwatchBrowser) {
browser.
clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
.pause(1000)
.modalFooterOKClick('github-credentials-error')
},
// GROUP 1
'check file added #group1 #group3': function (browser: NightwatchBrowser) {
browser
.addFile('test.txt', { content: 'hello world' }, 'README.md')
.clickLaunchIcon('dgit')
.pause(1000)
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible({
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.waitForElementVisible('*[data-id="addToGitChangestest.txt"]')
.pause(1000)
.click('*[data-id="addToGitChangestest.txt"]')
.waitForElementVisible({
selector: "//*[@data-status='added-staged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
})
.setValue('*[data-id="commitMessage"]', 'testcommit')
.click('*[data-id="commitButton"]')
},
'look at the commit #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commits-panel"]')
.waitForElementPresent({
selector: '//*[@data-id="commit-summary-testcommit-ahead"]',
locateStrategy: 'xpath'
})
},
'add second remote #group4': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.click('*[data-id="remotes-panel"]')
.waitForElementVisible('*[data-id="add-manual-remoteurl"]')
.setValue('*[data-id="add-manual-remoteurl"]', 'http://localhost:6868/bare2.git')
.waitForElementVisible('*[data-id="add-manual-remotename"]')
.setValue('*[data-id="add-manual-remotename"]', 'origin2')
.waitForElementVisible('*[data-id="add-manual-remotebtn"]')
.click('*[data-id="add-manual-remotebtn"]')
},
'check the buttons #group4': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="default-remote-check-origin"]')
.waitForElementVisible('*[data-id="set-as-default-origin2"]')
},
'check the commands #group4': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commands-panel"]')
.waitForElementVisible({
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]",
locateStrategy: 'xpath'
})
},
'switch to origin2 #group4': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="remotes-panel"]')
.waitForElementVisible('*[data-id="set-as-default-origin2"]')
.click('*[data-id="set-as-default-origin2"]')
},
'check the commands for origin2 #group4': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commands-panel"]')
.waitForElementVisible({
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin2')]",
locateStrategy: 'xpath'
})
},
'sync the commit #group4': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.waitForElementVisible('*[data-id="sourcecontrol-panel"]')
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible('*[data-id="syncButton"]')
.click('*[data-id="syncButton"]')
.waitForElementVisible('*[data-id="commitButton"]')
.click('*[data-id="commits-panel"]')
.waitForElementPresent({
selector: '//*[@data-id="commit-summary-testcommit-"]',
locateStrategy: 'xpath'
})
},
'check the log #group4': async function (browser: NightwatchBrowser) {
const logs = await getGitLog('/tmp/git/bare2.git')
console.log(logs)
browser.assert.ok(logs.includes('testcommit'))
const logs2 = await getGitLog('/tmp/git/bare.git')
console.log(logs2)
console.log(logs2.includes('testcommit3'))
browser.assert.ok(logs2.includes('testcommit3'))
},
'switch to origin #group4': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.click('*[data-id="remotes-panel"]')
.waitForElementVisible('*[data-id="set-as-default-origin"]')
.pause(1000)
.click('*[data-id="set-as-default-origin"]')
},
'check the commands for origin #group4': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commands-panel"]')
.waitForElementVisible({
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]",
locateStrategy: 'xpath'
})
},
'check the commit ahead #group4': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.waitForElementVisible('*[data-id="sourcecontrol-panel"]')
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible('*[data-id="syncButton"]')
// do not sync
.click('*[data-id="commits-panel"]')
.waitForElementPresent({
selector: '//*[@data-id="commit-summary-testcommit-ahead"]',
locateStrategy: 'xpath'
})
},
}
const useIsoGit = process.argv.includes('--use-isogit');
if (process.platform.startsWith('win')) {
module.exports = {}
}
else
module.exports = { ...tests }

@ -0,0 +1,255 @@
import { NightwatchBrowser } from "nightwatch"
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.hideToolTips()
done()
},
'open default template': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]')
.click('button[data-id="landingPageImportFromTemplate"]')
.waitForElementPresent('*[data-id="create-remixDefault"]')
.scrollAndClick('*[data-id="create-remixDefault"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.pause(3000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[1])
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
})
},
'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(1000)
.waitForElementVisible('*[data-id="initgit-btn"]')
.click('*[data-id="initgit-btn"]')
.waitForElementNotPresent('*[data-id="initgit-btn"]')
},
'launch github login via FE #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.pause(1000)
.waitForElementVisible('*[data-id="filepanel-login-github"]')
.click('*[data-id="filepanel-login-github"]')
},
'login to github #group1 #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="gitubUsername"]')
.setValue('*[data-id="githubToken"]', process.env.dgit_token)
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
},
'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="connected-as-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-img-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-link-bunsenstraat"]')
.waitForElementVisible('*[data-id="remotes-panel"]')
},
'check the FE for the auth user #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="filepanel-connected-img-bunsenstraat"]')
},
'clone a repository #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.click('*[data-id="clone-panel"]')
.click({
selector: '//*[@data-id="clone-panel-content"]//*[@data-id="fetch-repositories"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="clone-panel-content"]//*[@id="repository-select"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="clone-panel-content"]//*[@id="repository-select"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="clone-panel-content"]//*[contains(text(), "awesome-remix")]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="clone-panel-content"]//*[contains(text(), "awesome-remix")]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="clone-panel-content"]//*[@id="branch-select"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="clone-panel-content"]//*[@id="branch-select"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="clone-panel-content"]//*[contains(text(), "master")]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="clone-panel-content"]//*[@data-id="clonebtn-ethereum/awesome-remix-master"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="clone-panel-content"]//*[@data-id="clonebtn-ethereum/awesome-remix-master"]',
locateStrategy: 'xpath'
})
.pause(5000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[2])
.pause(1000)
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]')
})
},
'check if there is a README.md file #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="treeViewLitreeViewItemREADME.md"]')
},
'check the commands panel #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.click('*[data-id="commands-panel"]')
.waitForElementVisible({
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'master')]",
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]",
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'master')]",
locateStrategy: 'xpath'
})
},
'check the remotes #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="remotes-panel"]')
.waitForElementVisible('*[data-id="remotes-panel-content"]')
.pause(2000)
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-origin-default"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-current-branch-master"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-sync-origin"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-branch-links"]',
locateStrategy: 'xpath',
timeout: 10000
})
},
'check the commits of branch links #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-branch-links"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="branches-branch-links"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="commit-summary-linking fixed-"]',
locateStrategy: 'xpath'
})
},
'switch to branch links #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="branches-panel"]')
.waitForElementVisible({
selector: '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-id="branches-branch-links"]',
locateStrategy: 'xpath'
})
.pause(1000)
.click({
selector: '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-id="branches-toggle-branch-links"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-id="branches-toggle-current-branch-links"]',
locateStrategy: 'xpath'
})
},
'check the local branches #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible({
selector: '//*[@data-id="branches-panel-content-local-branches"]//*[@data-id="branches-toggle-current-branch-links"]',
locateStrategy: 'xpath'
})
},
'check the local commits #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commits-panel"]')
.pause(1000)
.waitForElementVisible({
selector: '//*[@data-id="commits-current-branch-links"]//*[@data-id="commit-summary-linking fixed-"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="commits-current-branch-links"]//*[@data-id="commit-summary-linking fixed-"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="commits-current-branch-links"]//*[@data-id="commit-change-modified-README.md"]',
locateStrategy: 'xpath'
})
},
'check the commands panel for links #group1': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="commands-panel"]')
.waitForElementVisible({
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'links')]",
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'origin')]",
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'links')]",
locateStrategy: 'xpath'
})
},
'disconnect github #group1': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="disconnect-github"]')
.pause(1000)
.click('*[data-id="disconnect-github"]')
.waitForElementNotPresent('*[data-id="connected-as-bunsenstraat"]')
},
'check the FE for the disconnected auth user #group1': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementNotPresent('*[data-id="filepanel-connected-img-bunsenstraat"]')
.waitForElementVisible('*[data-id="filepanel-login-github"]')
},
}
module.exports = tests

@ -0,0 +1,190 @@
import { NightwatchBrowser } from "nightwatch"
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.hideToolTips()
done()
},
'open default template': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]')
.click('button[data-id="landingPageImportFromTemplate"]')
.waitForElementPresent('*[data-id="create-remixDefault"]')
.scrollAndClick('*[data-id="create-remixDefault"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.pause(3000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[1])
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
})
},
'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(1000)
.waitForElementVisible('*[data-id="initgit-btn"]')
.click('*[data-id="initgit-btn"]')
.waitForElementNotPresent('*[data-id="initgit-btn"]')
},
'launch github login via FE #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.pause(1000)
.waitForElementVisible('*[data-id="filepanel-login-github"]')
.click('*[data-id="filepanel-login-github"]')
},
'login to github #group1 #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="gitubUsername"]')
.setValue('*[data-id="githubToken"]', process.env.dgit_token)
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
},
'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="connected-as-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-img-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-link-bunsenstraat"]')
.waitForElementVisible('*[data-id="remotes-panel"]')
},
'check the FE for the auth user #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="filepanel-connected-img-bunsenstraat"]')
},
'add a remote #group2': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="remotes-panel"]')
.click('*[data-id="remotes-panel"]')
.click({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="fetch-repositories"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@id="repository-select"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="remotes-panel-content"]//*[@id="repository-select"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[contains(text(), "awesome-remix")]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="remotes-panel-content"]//*[contains(text(), "awesome-remix")]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-remotename"]',
locateStrategy: 'xpath'
})
.setValue({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-remotename"]',
locateStrategy: 'xpath'
}, 'newremote')
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-addremote"]',
locateStrategy: 'xpath'
})
.click({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-panel-addremote"]',
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-newremote-default"]',
locateStrategy: 'xpath'
})
},
'check the commands panel for newremote #group2': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.click('*[data-id="commands-panel"]')
.waitForElementVisible({
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]",
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'newremote')]",
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]",
locateStrategy: 'xpath'
})
.pause(1000)
.getAttribute({
selector: '//*[@data-id="sourcecontrol-pull"]',
locateStrategy: 'xpath'
}, 'disabled', (result) => {
if (result.value) {
browser.assert.fail('Button is disabled')
} else {
browser.assert.ok(true)
}
})
},
'remove the remote #group2': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.click('*[data-id="remotes-panel"]')
.waitForElementVisible({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-rm-newremote"]',
locateStrategy: 'xpath'
})
.pause(2000)
.click({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-rm-newremote"]',
locateStrategy: 'xpath'
})
.pause(1000)
.waitForElementNotPresent({
selector: '//*[@data-id="remotes-panel-content"]//*[@data-id="remote-detail-newremote-default"]',
locateStrategy: 'xpath'
})
},
'check the commands panel for removed remote #group2': function (browser: NightwatchBrowser) {
browser
.pause(1000)
.click('*[data-id="commands-panel"]')
.waitForElementVisible({
selector: "//div[@id='commands-remote-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]",
locateStrategy: 'xpath'
})
.waitForElementNotPresent({
selector: "//div[@id='commands-remote-origin-select']//div[contains(@class, 'singleValue') and contains(text(), 'newremote')]",
locateStrategy: 'xpath'
})
.waitForElementVisible({
selector: "//div[@id='commands-local-branch-select']//div[contains(@class, 'singleValue') and contains(text(), 'main')]",
locateStrategy: 'xpath'
})
.getAttribute({
selector: '//*[@data-id="sourcecontrol-pull"]',
locateStrategy: 'xpath'
}, 'disabled', (result) => {
if (result.value) {
browser.assert.ok(true)
} else {
browser.assert.fail('Button is not disabled')
}
})
},
}
module.exports = tests

@ -0,0 +1,180 @@
import { NightwatchBrowser } from "nightwatch"
const useIsoGit = process.argv.includes('--use-isogit');
let commitCount = 0
let branchCount = 0
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.hideToolTips()
done()
},
'open default template': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.waitForElementVisible('button[data-id="landingPageImportFromTemplate"]')
.click('button[data-id="landingPageImportFromTemplate"]')
.waitForElementPresent('*[data-id="create-remixDefault"]')
.scrollAndClick('*[data-id="create-remixDefault"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.waitForElementPresent('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.click('[data-id="TemplatesSelectionModalDialogContainer-react"] .modal-ok')
.pause(3000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[1])
.waitForElementVisible('*[data-id="treeViewLitreeViewItemtests"]')
})
},
'Update settings for git #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(1000)
.waitForElementVisible('*[data-id="initgit-btn"]')
.click('*[data-id="initgit-btn"]')
.waitForElementNotPresent('*[data-id="initgit-btn"]')
},
'launch github login via FE #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.pause(1000)
.waitForElementVisible('*[data-id="filepanel-login-github"]')
.click('*[data-id="filepanel-login-github"]')
},
'login to github #group1 #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="github-panel"]')
.waitForElementVisible('*[data-id="gitubUsername"]')
.setValue('*[data-id="githubToken"]', process.env.dgit_token)
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
},
'check if the settings are loaded #group1 #group2': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="connected-as-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-img-bunsenstraat"]')
.waitForElementVisible('*[data-id="connected-link-bunsenstraat"]')
.waitForElementVisible('*[data-id="remotes-panel"]')
},
'check the FE for the auth user #group1 #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="filepanel-connected-img-bunsenstraat"]')
},
// pagination test
'clone repo #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="clone-panel"]')
.click('*[data-id="clone-panel"]')
.waitForElementVisible('*[data-id="clone-url"]')
.setValue('*[data-id="clone-url"]', 'https://github.com/yann300/remix-reward')
.waitForElementVisible('*[data-id="clone-branch"]')
.setValue('*[data-id="clone-branch"]', 'master')
.waitForElementVisible('*[data-id="clone-btn"]')
.click('*[data-id="clone-btn"]')
.clickLaunchIcon('filePanel')
.pause(5000)
.windowHandles(function (result) {
console.log(result.value)
browser.switchWindow(result.value[2])
.pause(1000)
.waitForElementVisible('*[data-id="treeViewLitreeViewItem.git"]')
})
},
'Update settings for git #group3': function (browser: NightwatchBrowser) {
browser.
clickLaunchIcon('dgit')
.waitForElementVisible('*[data-id="github-panel"]')
.pause(1000)
.click('*[data-id="github-panel"]')
.pause(1000)
.setValue('*[data-id="githubToken"]', 'invalidtoken')
.pause(1000)
.setValue('*[data-id="gitubUsername"]', 'git')
.pause(1000)
.setValue('*[data-id="githubEmail"]', 'git@example.com')
.pause(1000)
.click('*[data-id="saveGitHubCredentials"]')
.pause(1000)
.modalFooterOKClick('github-credentials-error')
},
'check the commits panel for pagination #group3': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="commits-panel"]')
.click('*[data-id="commits-panel"]')
.elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) {
console.log('Number of commit-summary elements:', (result.value as any).length);
if (useIsoGit) {
commitCount = (result.value as any).length
browser.assert.ok((result.value as any).length == 1)
} else {
commitCount = (result.value as any).length
browser.assert.ok((result.value as any).length > 2)
}
})
},
'load more commits #group3': function (browser: NightwatchBrowser) {
console.log('commitCount:', commitCount)
browser
.waitForElementVisible('*[data-id="load-more-commits"]')
.click('*[data-id="load-more-commits"]')
.waitForElementVisible('*[data-id="loader-indicator"]')
.waitForElementNotPresent('*[data-id="loader-indicator"]')
.pause(2000)
.elements('xpath', '//*[@data-id="commits-current-branch-master"]//*[@data-type="commit-summary"]', function (result) {
console.log('Number of commit-summary elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length > commitCount)
})
},
'load more branches from remote #group3': function (browser: NightwatchBrowser) {
browser
.click('*[data-id="branches-panel"]')
.waitForElementVisible({
selector: '//*[@data-id="branches-panel-content-remote-branches"]',
locateStrategy: 'xpath'
})
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) {
console.log('Number of branches elements:', (result.value as any).length);
if (useIsoGit) {
branchCount = (result.value as any).length
browser.assert.ok((result.value as any).length == 1)
} else {
branchCount = (result.value as any).length
browser.assert.ok((result.value as any).length > 2)
}
})
if (useIsoGit) {
browser.waitForElementVisible('*[data-id="remote-sync-origin"]')
.click('*[data-id="remote-sync-origin"]')
.waitForElementVisible('*[data-id="loader-indicator"]')
.waitForElementNotPresent('*[data-id="loader-indicator"]')
.pause(2000)
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) {
console.log('Number of branches elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length > branchCount)
})
} else {
browser.waitForElementVisible('*[data-id="show-more-branches-on-remote"]')
.click('*[data-id="show-more-branches-on-remote"]')
.pause(1000)
.elements('xpath', '//*[@data-id="branches-panel-content-remote-branches"]//*[@data-type="branches-branch"]', function (result) {
console.log('Number of branches elements:', (result.value as any).length);
browser.assert.ok((result.value as any).length > branchCount)
})
}
}
}
module.exports = tests

@ -0,0 +1,90 @@
import { NightwatchBrowser } from 'nightwatch'
import { ChildProcess, spawn, execSync } from 'child_process'
import { homedir } from 'os'
import path from 'path'
import os from 'os'
const dir = path.join('remix-desktop-test-' + Date.now().toString())
const tests = {
before: function (browser: NightwatchBrowser, done: VoidFunction) {
done()
},
setuphardhat: function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
await setupHardhatProject()
done()
})
},
addScript: function (browser: NightwatchBrowser) {
// run script in console
browser.executeAsync(function (dir, done) {
(window as any).electronAPI.openFolderInSameWindow('/tmp/' + dir).then(done)
}, [dir], () => {
console.log('done window opened')
})
.waitForElementVisible('*[data-id="treeViewDivDraggableItemhardhat.config.js"]', 10000)
},
compile: function (browser: NightwatchBrowser) {
browser.perform(async (done) => {
console.log('generating compilation result')
await compileHardhatProject()
done()
})
.expect.element('*[data-id="terminalJournal"]').text.to.contain('receiving compilation result from Hardhat').before(60000)
let addressRef
browser.clickLaunchIcon('filePanel')
.openFile('contracts')
.openFile('contracts/Token.sol')
.clickLaunchIcon('udapp')
.selectAccount('0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c')
.selectContract('Token')
.createContract('')
.clickInstance(0)
.clickFunction('balanceOf - call', { types: 'address account', values: '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c' })
.getAddressAtPosition(0, (address) => {
addressRef = address
})
.perform((done) => {
browser.verifyCallReturnValue(addressRef, ['0:uint256: 1000000'])
.perform(() => done())
})
}
}
async function compileHardhatProject(): Promise<void> {
console.log(process.cwd())
try {
const server = spawn('npx hardhat compile', [], { cwd: '/tmp/' + dir, shell: true, detached: true })
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
})
} catch (e) {
console.log(e)
}
}
async function setupHardhatProject(): Promise<void> {
console.log('setup hardhat project', dir)
try {
const server = spawn(`git clone https://github.com/NomicFoundation/hardhat-boilerplate ${dir} && cd ${dir} && yarn install && yarn add "@typechain/ethers-v5@^10.1.0" && yarn add "@typechain/hardhat@^6.1.2" && yarn add "typechain@^8.1.0" && echo "END"`, [], { cwd: '/tmp/', shell: true, detached: true })
return new Promise((resolve, reject) => {
server.on('exit', function (exitCode) {
console.log("Child exited with code: " + exitCode);
console.log('end')
resolve()
})
})
} catch (e) {
console.log(e)
}
}
module.exports = {
...tests
}

@ -55,4 +55,8 @@ export class CompilerClientApi extends CompilerApiMixin(PluginClient) implements
getFileManagerMode () { getFileManagerMode () {
return 'browser' return 'browser'
} }
isDesktop() {
return false
}
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/ghaction-helper", "name": "@remix-project/ghaction-helper",
"version": "0.1.36", "version": "0.1.37",
"description": "Solidity Tests GitHub Action Helper", "description": "Solidity Tests GitHub Action Helper",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
@ -19,17 +19,17 @@
}, },
"homepage": "https://github.com/ethereum/remix-project#readme", "homepage": "https://github.com/ethereum/remix-project#readme",
"devDependencies": { "devDependencies": {
"@remix-project/remix-solidity": "^0.5.42", "@remix-project/remix-solidity": "^0.5.43",
"@types/chai": "^4.3.4", "@types/chai": "^4.3.4",
"typescript": "^4.9.3" "typescript": "^4.9.3"
}, },
"dependencies": { "dependencies": {
"@ethereum-waffle/chai": "^3.4.4", "@ethereum-waffle/chai": "^3.4.4",
"@remix-project/remix-simulator": "^0.2.56", "@remix-project/remix-simulator": "^0.2.57",
"chai": "^4.3.7", "chai": "^4.3.7",
"ethers": "^5.7.2", "ethers": "^5.7.2",
"web3": "^4.1.1" "web3": "^4.1.1"
}, },
"types": "./src/index.d.ts", "types": "./src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444" "gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01"
} }

@ -3,7 +3,7 @@ import { ethers } from "ethers"
import { Provider } from '@remix-project/remix-simulator' import { Provider } from '@remix-project/remix-simulator'
import { getArtifactsByContractName } from './artifacts-helper' import { getArtifactsByContractName } from './artifacts-helper'
import { SignerWithAddress } from './signer' import { SignerWithAddress } from './signer'
import Web3 from "web3" import { Web3 } from "web3"
const providerConfig = { const providerConfig = {
fork: global.fork || null, fork: global.fork || null,

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-analyzer", "name": "@remix-project/remix-analyzer",
"version": "0.5.65", "version": "0.5.66",
"description": "Tool to perform static analysis on Solidity smart contracts", "description": "Tool to perform static analysis on Solidity smart contracts",
"scripts": { "scripts": {
"test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts" "test": "./../../node_modules/.bin/ts-node --project ../../tsconfig.base.json --require tsconfig-paths/register ./../../node_modules/.bin/tape ./test/tests.ts"
@ -24,9 +24,9 @@
"@ethereumjs/block": "5.3.0", "@ethereumjs/block": "5.3.0",
"@ethereumjs/tx": "5.4.0", "@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0", "@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.0", "@ethereumjs/vm": "8.1.1",
"@remix-project/remix-astwalker": "^0.0.86", "@remix-project/remix-astwalker": "^0.0.87",
"@remix-project/remix-lib": "^0.5.63", "@remix-project/remix-lib": "^0.5.64",
"async": "^2.6.2", "async": "^2.6.2",
"ethers": "^5.4.2", "ethers": "^5.4.2",
"ethjs-util": "^0.1.6", "ethjs-util": "^0.1.6",
@ -50,6 +50,6 @@
"typescript": "^3.7.5" "typescript": "^3.7.5"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444", "gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01",
"main": "./src/index.js" "main": "./src/index.js"
} }

@ -1 +1,2 @@
export * from './lib/remix-api' export * from './lib/remix-api'
export * from './lib/types/git'

@ -7,6 +7,6 @@ export interface IFilePanelApi {
switchToWorkspace: (workspace: string) => Promise<void>; switchToWorkspace: (workspace: string) => Promise<void>;
} & StatusEvents } & StatusEvents
methods: IFilePanel['methods'] & { methods: IFilePanel['methods'] & {
clone: () => Promise<void>;
} }
} }

@ -1,10 +1,13 @@
import { commitChange } from "@remix-ui/git"; import { commitChange } from "@remix-api";
import { IFileSystem } from "@remixproject/plugin-api" import { IFileSystem } from "@remixproject/plugin-api"
// Extended interface with 'diff' method // Extended interface with 'diff' method
export interface IExtendedFileSystem extends IFileSystem { export interface IExtendedFileSystem extends IFileSystem {
methods: IFileSystem['methods'] & { methods: IFileSystem['methods'] & {
/** Compare the differences between two files */
diff(change: commitChange): Promise<void> diff(change: commitChange): Promise<void>
refresh(): Promise<void>
hasGitSubmodules(): Promise<boolean>
isGitRepo(): Promise<boolean> isGitRepo(): Promise<boolean>
}; };
} }

@ -0,0 +1,13 @@
import { StatusEvents } from "@remixproject/plugin-utils";
export interface IFs {
events: {
workingDirChanged(path: string): Promise<void>,
} & StatusEvents,
methods: {
selectFolder(path?: string, title?: string, button?: string): Promise<string>
openWindow(path?: string): Promise<void>,
getWorkingDir(): Promise<string>,
openFolderInSameWindow(path: string): Promise<void>,
}
}

@ -0,0 +1,43 @@
import { StatusEvents } from "@remixproject/plugin-utils"
import { ReadBlobResult, ReadCommitResult, StatusRow } from "isomorphic-git"
import { commitChange, repositoriesInput, repository, cloneInputType, branchesInputType, branch, remote, logInputType, remoteCommitsInputType, pagedCommits, fetchInputType, pullInputType, pushInputType, currentBranchInput, branchInputType, checkoutInputType, addInputType, rmInputType, resolveRefInput, readBlobInput, commitInputType, statusInput, compareBranchesInput, branchDifference, initInputType, updateSubmodulesInput } from "../types/git"
export interface IGitApi {
events: {
"checkout": () => void
"clone": () => void
"add": () => void
"rm": () => void
"commit": () => void
"branch": () => void
"init": () => void
} & StatusEvents,
methods: {
getCommitChanges(oid1: string, oid2: string): Promise<commitChange[]>
repositories(input: repositoriesInput): Promise<repository[]>
clone(input: cloneInputType): Promise<any>
branches(input?: branchesInputType): Promise<branch[]>,
remotes(): Promise<remote[]>,
log(input: logInputType): Promise<ReadCommitResult[]>,
remotecommits(input: remoteCommitsInputType): Promise<pagedCommits[]>
fetch(input: fetchInputType): Promise<any>
pull(input: pullInputType): Promise<any>
push(input: pushInputType): Promise<any>
currentbranch(input?: currentBranchInput): Promise<branch>
branch(input: branchInputType): Promise<void>
checkout(input: checkoutInputType): Promise<void>
add(input: addInputType): Promise<void>
rm(input: rmInputType): Promise<void>
resolveref(input: resolveRefInput): Promise<string>
readblob(input: readBlobInput): Promise<ReadBlobResult>
commit(input: commitInputType): Promise<string>
addremote(input: remote): Promise<void>
delremote(input: remote): Promise<void>
status(input?: statusInput): Promise<Array<StatusRow>>
compareBranches(input: compareBranchesInput): Promise<branchDifference>
init(input?: initInputType): Promise<void>
updateSubmodules: (input: updateSubmodulesInput) => Promise<void>
version: () => Promise<string>
}
}

@ -0,0 +1,10 @@
import { ITerminal } from "@remixproject/plugin-api/src/lib/terminal"
import { StatusEvents } from "@remixproject/plugin-utils"
export interface IExtendedTerminalApi extends ITerminal {
events: {
} & StatusEvents
methods: ITerminal['methods'] & {
logHtml(html: string): void
}
}

@ -1,13 +1,14 @@
import { IGitApi } from "@remix-ui/git"
import { IRemixApi } from "@remixproject/plugin-api" import { IRemixApi } from "@remixproject/plugin-api"
import { StatusEvents } from "@remixproject/plugin-utils" import { StatusEvents } from "@remixproject/plugin-utils"
import { IConfigApi } from "./plugins/config-api" import { IConfigApi } from "./plugins/config-api"
import { IFileDecoratorApi } from "./plugins/filedecorator-api" import { IFileDecoratorApi } from "./plugins/filedecorator-api"
import { IExtendedFileSystem } from "./plugins/fileSystem-api" import { IExtendedFileSystem } from "./plugins/fileSystem-api"
import { IFs } from "./plugins/fs-api"
import { IGitApi } from "./plugins/git-api"
import { INotificationApi } from "./plugins/notification-api" import { INotificationApi } from "./plugins/notification-api"
import { ISettings } from "./plugins/settings-api" import { ISettings } from "./plugins/settings-api"
import { IExtendedTerminalApi } from "./plugins/terminal-api"
import { IFilePanelApi } from "./plugins/filePanel-api" import { IFilePanelApi } from "./plugins/filePanel-api"
import { Plugin } from "@remixproject/engine"
import { ISidePanelApi } from "./plugins/sidePanel-api" import { ISidePanelApi } from "./plugins/sidePanel-api"
import { IPinnedPanelApi } from "./plugins/pinned-panel-api" import { IPinnedPanelApi } from "./plugins/pinned-panel-api"
import { ILayoutApi } from "./plugins/layout-api" import { ILayoutApi } from "./plugins/layout-api"
@ -20,6 +21,9 @@ export interface ICustomRemixApi extends IRemixApi {
settings: ISettings settings: ISettings
fileDecorator: IFileDecoratorApi fileDecorator: IFileDecoratorApi
fileManager: IExtendedFileSystem fileManager: IExtendedFileSystem
isogit: IGitApi,
terminal: IExtendedTerminalApi
fs: IFs
filePanel: IFilePanelApi filePanel: IFilePanelApi
sidePanel: ISidePanelApi sidePanel: ISidePanelApi
pinnedPanel: IPinnedPanelApi pinnedPanel: IPinnedPanelApi

@ -0,0 +1,206 @@
import { Endpoints } from "@octokit/types"
import { AuthCallback, HttpClient, ReadCommitResult } from "isomorphic-git"
export type branchDifference = {
uniqueHeadCommits: ReadCommitResult[],
uniqueRemoteCommits: ReadCommitResult[],
}
export type commitChange = {
type: commitChangeType
path: string,
hashModified: string,
hashOriginal: string,
original?: string,
modified?: string,
readonly?: boolean
}
export type commitChangeTypes = {
"deleted": "D"
"modified": "M"
"added": "A",
"unknown": "?"
}
export type pagedCommits = {
page: number,
perPage: number,
total: number,
hasNextPage: boolean,
commits: ReadCommitResult[]
}
export enum syncStatus {
"sync" = "sync",
"publishBranch" = "publishBranch",
"none" = "none",
}
export type repository = {
name: string
html_url: string
owner: {
login: string
},
full_name: string
default_branch: string
id: number
url: string
}
export type branch = {
name: string
remote: remote
}
export type remote = {
name: string
url: string
}
export type remoteBranch = {
name: string
}
export type commitChangeType = keyof commitChangeTypes
export type initInputType = {
defaultBranch: string
}
export type author = {
name: string,
email: string,
}
export type updateSubmodulesInput = {
dir?: string
token?: string
}
export type remoteCommitsInputType = {
owner: string, repo: string, token: string, branch: string, length: number, page: number
}
export type compareBranchesInput = {
branch: branch, remote: remote
}
export type fetchInputType = {
remote: remote,
ref?: branch,
remoteRef?: branch,
depth?: number,
singleBranch?: boolean,
relative?: boolean,
quiet?: boolean
author?: author
token?: string
}
export type logInputType = {
ref: string,
depth?: number,
}
export type pullInputType = {
remote: remote,
ref: branch,
remoteRef?: branch
author?: author
token?: string
}
export type pushInputType = {
remote: remote,
ref: branch,
remoteRef?: branch,
force?: boolean,
author?: author,
token?: string
}
export type branchInputType = {
ref: string,
checkout?: boolean
refresh?: boolean
force?: boolean
}
export type currentBranchInput = {
fs: any,
dir: string
}
export type checkoutInputType = {
ref: string,
force?: boolean,
remote?: string
refresh?: boolean
fetch?: boolean
}
export type addInputType = {
filepath: string | string[]
}
export type rmInputType = {
filepath: string
}
export type resolveRefInput = {
ref: string
}
export type readBlobInput = {
oid: string,
filepath: string
}
export type commitInputType = {
author: {
name: string,
email: string,
},
message: string,
}
export type branchesInputType = {
fs?: any
dir?: string
}
export interface cloneInputType {
url: string,
branch?: string,
depth?: number,
singleBranch?: boolean
workspaceName?: string
workspaceExists?: boolean
token?: string
dir?: string // where the clone should happen on desktop
}
export interface repositoriesInput { token: string, page?: number, per_page?: number }
export interface statusInput { ref: string, filepaths?: string[] }
export type isoGitFSConfig = {
fs: any,
dir: string,
}
export type isoGitProxyConfig = {
corsProxy: string
http: HttpClient
onAuth: AuthCallback
}
export type GitHubUser = Partial<Endpoints["GET /user"]["response"]['data']> & {
isConnected: boolean
}
export type userEmails = Endpoints["GET /user/emails"]["response"]["data"]

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-astwalker", "name": "@remix-project/remix-astwalker",
"version": "0.0.86", "version": "0.0.87",
"description": "Tool to walk through Solidity AST", "description": "Tool to walk through Solidity AST",
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
@ -36,8 +36,8 @@
"@ethereumjs/block": "5.3.0", "@ethereumjs/block": "5.3.0",
"@ethereumjs/tx": "5.4.0", "@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0", "@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.0", "@ethereumjs/vm": "8.1.1",
"@remix-project/remix-lib": "^0.5.63", "@remix-project/remix-lib": "^0.5.64",
"@types/tape": "^4.2.33", "@types/tape": "^4.2.33",
"async": "^2.6.2", "async": "^2.6.2",
"ethers": "^5.4.2", "ethers": "^5.4.2",
@ -53,6 +53,6 @@
"tap-spec": "^5.0.0" "tap-spec": "^5.0.0"
}, },
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444", "gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-debug", "name": "@remix-project/remix-debug",
"version": "0.5.56", "version": "0.5.57",
"description": "Tool to debug Ethereum transactions", "description": "Tool to debug Ethereum transactions",
"contributors": [ "contributors": [
{ {
@ -25,11 +25,11 @@
"@ethereumjs/common": "4.4.0", "@ethereumjs/common": "4.4.0",
"@ethereumjs/tx": "5.4.0", "@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0", "@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.0", "@ethereumjs/vm": "8.1.1",
"@remix-project/remix-astwalker": "^0.0.86", "@remix-project/remix-astwalker": "^0.0.87",
"@remix-project/remix-lib": "^0.5.63", "@remix-project/remix-lib": "^0.5.64",
"@remix-project/remix-simulator": "^0.2.56", "@remix-project/remix-simulator": "^0.2.57",
"@remix-project/remix-solidity": "^0.5.42", "@remix-project/remix-solidity": "^0.5.43",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.2", "async": "^2.6.2",
"color-support": "^1.1.3", "color-support": "^1.1.3",
@ -69,6 +69,6 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-debug#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444", "gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -1,4 +1,4 @@
import Web3 from 'web3' import { Web3 } from 'web3'
import { Debugger } from '../debugger/debugger' import { Debugger } from '../debugger/debugger'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'

@ -1,5 +1,5 @@
'use strict' 'use strict'
import Web3, { Web3PluginBase } from 'web3' import { Web3, Web3PluginBase } from 'web3'
import { toNumber } from 'web3-utils' import { toNumber } from 'web3-utils'
export function extendWeb3 (web3) { export function extendWeb3 (web3) {

@ -0,0 +1 @@
export { isoGit } from './src/isogit'

@ -0,0 +1,345 @@
import { GitHubUser, author, branch, cloneInputType, commitChange, compareBranchesInput, currentBranchInput, fetchInputType, isoGitFSConfig, isoGitProxyConfig, pullInputType, pushInputType, remote, userEmails } from "@remix-api"
import git from 'isomorphic-git'
import {
Plugin
} from '@remixproject/engine'
import http from 'isomorphic-git/http/web'
import { Octokit } from "octokit"
import { ElectronBasePluginClient } from "@remixproject/plugin-electron"
const currentbranch = async (input: currentBranchInput, fsConfig: isoGitFSConfig) => {
try {
const cmd = input ? fsConfig ? { ...fsConfig, ...input } : input : fsConfig
const name = await git.currentBranch(cmd)
let remote: remote = undefined
try {
const remoteName = await git.getConfig({
...fsConfig,
path: `branch.${name}.remote`
})
if (remoteName) {
const remoteUrl = await git.getConfig({
...fsConfig,
path: `remote.${remoteName}.url`
})
remote = { name: remoteName, url: remoteUrl }
}
} catch (e) {
// do nothing
}
return {
remote: remote,
name: name || ''
}
} catch (e) {
return undefined
}
}
const branches = async (fsConfig: isoGitFSConfig) => {
try {
const remotes = await isoGit.remotes(fsConfig)
let branches: branch[] = []
branches = (await git.listBranches(fsConfig)).map((branch) => { return { remote: undefined, name: branch } })
for (const remote of remotes) {
const cmd = {
...fsConfig,
remote: remote.name
}
const remotebranches = (await git.listBranches(cmd)).map((branch) => { return { remote: remote, name: branch } })
branches = [...branches, ...remotebranches]
}
return branches
} catch (e) {
console.log(e)
return []
}
}
const remotes = async (fsConfig: isoGitFSConfig) => {
let remotes: remote[] = []
try {
remotes = (await git.listRemotes({ ...fsConfig })).map((remote) => { return { name: remote.remote, url: remote.url } }
)
} catch (e) {
// do nothing
}
return remotes
}
const push = async (input: pushInputType, fsConfig: isoGitFSConfig, plugin: Plugin | ElectronBasePluginClient) => {
const cmd = {
force: input.force,
ref: input.ref.name,
remoteRef: input.remoteRef && input.remoteRef.name,
remote: input.remote.name,
author: await getAuthor(input, plugin),
input,
}
const proxy = await isoGit.addIsomorphicGitProxyConfig(input, plugin)
console.log({ ...fsConfig, ...cmd, ...proxy })
return await git.push({ ...fsConfig, ...cmd, ...proxy })
}
const pull = async (input: pullInputType, fsConfig: isoGitFSConfig, plugin: Plugin | ElectronBasePluginClient) => {
const cmd = {
ref: input.ref.name,
remoteRef: input.remoteRef && input.remoteRef.name,
author: await getAuthor(input, plugin),
remote: input.remote.name,
input,
}
const proxy = await isoGit.addIsomorphicGitProxyConfig(input, plugin)
console.log({ ...fsConfig, ...cmd, ...proxy })
return await git.pull({ ...fsConfig, ...cmd, ...proxy })
}
const fetch = async (input: fetchInputType, fsConfig: isoGitFSConfig, plugin: Plugin | ElectronBasePluginClient) => {
const cmd = {
ref: input.ref && input.ref.name,
remoteRef: input.remoteRef && input.remoteRef.name,
author: await getAuthor(input, plugin),
remote: input.remote && input.remote.name,
depth: input.depth || 5,
singleBranch: input.singleBranch,
relative: input.relative,
input
}
const proxy = await isoGit.addIsomorphicGitProxyConfig(input, plugin)
console.log({ ...fsConfig, ...cmd, ...proxy })
return await git.fetch({ ...fsConfig, ...cmd, ...proxy })
}
const clone = async (input: cloneInputType, fsConfig: isoGitFSConfig, plugin: Plugin | ElectronBasePluginClient) => {
const proxy = await isoGit.addIsomorphicGitProxyConfig(input, plugin)
const cmd = {
url: input.url,
singleBranch: input.singleBranch,
ref: input.branch,
depth: input.depth || 10,
dir: input.dir,
input
}
await git.clone({ ...fsConfig, ...cmd, ...proxy })
}
const getAuthor = async (input, plugin: any) => {
const author: author = {
name: '',
email: ''
}
if (input && input.name && input.email) {
author.name = input.name
author.email = input.email
} else {
const username = await plugin.call('config' as any, 'getAppParameter', 'settings/github-user-name')
const email = await plugin.call('config' as any, 'getAppParameter', 'settings/github-email')
const token = await plugin.call('config' as any, 'getAppParameter', 'settings/gist-access-token')
if (username && email) {
author.name = username
author.email = email
} else if (token) {
const gitHubUser = await isoGit.getGitHubUser({ token })
if (gitHubUser) {
author.name = gitHubUser.user.login
}
}
}
return author
}
const getGitHubUser = async(input: { token: string }): Promise<{
user: GitHubUser,
emails: userEmails,
scopes: string[]
}> => {
try {
const octokit = new Octokit({
auth: input.token
})
const user = await octokit.request('GET /user', {
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
})
const emails = await octokit.request('GET /user/emails')
const scopes = user.headers['x-oauth-scopes'] || ''
return {
user: {
...user.data, isConnected:
user.data.login !== undefined && user.data.login !== null && user.data.login !== ''
},
emails: emails.data,
scopes: scopes && scopes.split(',').map(scope => scope.trim())
}
} catch (e) {
return null
}
}
const addIsomorphicGitProxyConfig = async (input: {
url?: string,
remote?: remote,
provider?: 'github' | 'localhost',
token?: string,
}, plugin: any) => {
const token = await plugin.call('config' as any, 'getAppParameter', 'settings/gist-access-token')
let config: isoGitProxyConfig = {
corsProxy: 'https://corsproxy.remixproject.org/',
http,
onAuth: url => {
url
const auth = {
username: input.token || token,
password: ''
}
return auth
}
}
if (input.url) {
const url = new URL(input.url)
if (url.hostname.includes('localhost')) {
config = {
...config,
corsProxy: null
}
}
}
if ((input.remote && input.remote.url)) {
const url = new URL(input.remote.url)
if (url.hostname.includes('localhost')) {
config = {
...config,
corsProxy: null,
}
}
}
if (input.provider && input.provider === 'github') {
config = {
...config,
corsProxy: 'https://corsproxy.remixproject.org/',
}
}
if (input.provider && input.provider === 'localhost') {
config = {
...config,
corsProxy: null
}
}
return config
}
const getCommitChanges = async (commitHash1: string, commitHash2: string, fsConfig: isoGitFSConfig) => {
const result: commitChange[] = await git.walk({
...fsConfig,
trees: [git.TREE({ ref: commitHash1 }), git.TREE({ ref: commitHash2 })],
map: async function (filepath, [A, B]) {
if (filepath === '.') {
return
}
try {
if ((A && await A.type()) === 'tree' || B && (await B.type()) === 'tree') {
return
}
} catch (e) {
// ignore
}
// generate ids
const Aoid = A && await A.oid() || undefined
const Boid = B && await B.oid() || undefined
const commitChange: Partial<commitChange> = {
hashModified: commitHash1,
hashOriginal: commitHash2,
path: filepath,
}
// determine modification type
if (Aoid !== Boid) {
commitChange.type = "modified"
}
if (Aoid === undefined) {
commitChange.type = "deleted"
}
if (Boid === undefined || !commitHash2) {
commitChange.type = "added"
}
if (Aoid === undefined && Boid === undefined) {
commitChange.type = "unknown"
}
if (commitChange.type)
return commitChange
else
return undefined
},
})
return result
}
const compareBranches = async ({ branch, remote }: compareBranchesInput, fsConfig: isoGitFSConfig) => {
// Get current branch commits
const headCommits = await git.log({
...fsConfig,
ref: branch.name,
depth: 10,
});
// Get remote branch commits
const remoteCommits = await git.log({
...fsConfig,
ref: `${remote.name}/${branch.name}`,
depth: 10,
});
// Convert arrays of commit objects to sets of commit SHAs
const headCommitSHAs = new Set(headCommits.map(commit => commit.oid));
const remoteCommitSHAs = new Set(remoteCommits.map(commit => commit.oid));
// Filter out commits that are only in the remote branch
const uniqueRemoteCommits = remoteCommits.filter(commit => !headCommitSHAs.has(commit.oid));
// filter out commits that are only in the local branch
const uniqueHeadCommits = headCommits.filter(commit => !remoteCommitSHAs.has(commit.oid));
return {
uniqueHeadCommits,
uniqueRemoteCommits,
};
}
export const isoGit = {
currentbranch,
remotes,
branches,
getCommitChanges,
compareBranches,
addIsomorphicGitProxyConfig,
push,
pull,
fetch,
getGitHubUser,
clone
}

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-lib", "name": "@remix-project/remix-lib",
"version": "0.5.63", "version": "0.5.64",
"description": "Library to various Remix tools", "description": "Library to various Remix tools",
"contributors": [ "contributors": [
{ {
@ -55,6 +55,6 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444", "gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -1,6 +1,7 @@
'use strict' 'use strict'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { getFunctionFragment } from './txHelper' import { getFunctionFragment } from './txHelper'
import { Transaction } from './txRunner'
/** /**
* deploy the given contract * deploy the given contract
@ -16,11 +17,11 @@ import { getFunctionFragment } from './txHelper'
* [personal mode enabled, need password to continue] promptCb (okCb, cancelCb) * [personal mode enabled, need password to continue] promptCb (okCb, cancelCb)
* @param {Function} finalCallback - last callback. * @param {Function} finalCallback - last callback.
*/ */
export function createContract (from, data, value, gasLimit, txRunner, callbacks, finalCallback) { export function createContract ({ from, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, finalCallback) {
if (!callbacks.confirmationCb || !callbacks.gasEstimationForceSend || !callbacks.promptCb) { if (!callbacks.confirmationCb || !callbacks.gasEstimationForceSend || !callbacks.promptCb) {
return finalCallback('all the callbacks must have been defined') return finalCallback('all the callbacks must have been defined')
} }
const tx = { from: from, to: null, data: data, useCall: false, value: value, gasLimit: gasLimit } const tx = { from: from, to: null, data: data, useCall: false, value: value, gasLimit: gasLimit, signed }
txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case) // see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
finalCallback(error, txResult) finalCallback(error, txResult)
@ -42,9 +43,9 @@ export function createContract (from, data, value, gasLimit, txRunner, callbacks
* [personal mode enabled, need password to continue] promptCb (okCb, cancelCb) * [personal mode enabled, need password to continue] promptCb (okCb, cancelCb)
* @param {Function} finalCallback - last callback. * @param {Function} finalCallback - last callback.
*/ */
export function callFunction (from, to, data, value, gasLimit, funAbi, txRunner, callbacks, finalCallback) { export function callFunction ({ from, to, data, value, gasLimit, signed }: Transaction, funAbi , txRunner, callbacks, finalCallback) {
const useCall = funAbi.stateMutability === 'view' || funAbi.stateMutability === 'pure' || funAbi.constant const useCall = funAbi.stateMutability === 'view' || funAbi.stateMutability === 'pure' || funAbi.constant
const tx = { from, to, data, useCall, value, gasLimit } const tx = { from, to, data, useCall, value, gasLimit, signed }
txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case) // see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
finalCallback(error, txResult) finalCallback(error, txResult)

@ -3,13 +3,14 @@ import { EventManager } from '../eventManager'
export type Transaction = { export type Transaction = {
from: string, from: string,
to: string, to?: string,
value: string, value: string,
data: string, data: string,
gasLimit: number, gasLimit: number,
useCall: boolean, useCall?: boolean,
timestamp?: number timestamp?: number,
type: '0x1' | '0x2' signed?: boolean,
type?: '0x1' | '0x2'
} }
export class TxRunner { export class TxRunner {
@ -32,9 +33,8 @@ export class TxRunner {
} }
execute (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, callback) { execute (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, callback) {
let data = args.data if (args.data && args.data.slice(0, 2) !== '0x') {
if (data.slice(0, 2) !== '0x') { args.data = '0x' + args.data
data = '0x' + data
} }
this.internalRunner.execute(args, confirmationCb, gasEstimationForceSend, promptCb, callback) this.internalRunner.execute(args, confirmationCb, gasEstimationForceSend, promptCb, callback)
} }

@ -69,47 +69,60 @@ export class TxRunnerVM {
} }
try { try {
this.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback) this.runInVm(args, callback)
} catch (e) { } catch (e) {
callback(e, null) callback(e, null)
} }
} }
async runInVm (from: string, to: string, data: string, value: string, gasLimit: number, useCall: boolean, callback: VMExecutionCallBack) { async runInVm (tx: InternalTransaction, callback: VMExecutionCallBack) {
const { to, data, value, gasLimit, useCall, signed } = tx
let { from } = tx
let account let account
if (!from && useCall && Object.keys(this.vmaccounts).length) {
from = Object.keys(this.vmaccounts)[0]
account = this.vmaccounts[from]
} else account = this.vmaccounts[from]
if (!account) {
return callback('Invalid account selected')
}
try { try {
const res = await this.getVMObject().stateManager.getAccount(Address.fromString(from))
const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle. const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle.
let tx let tx
if (!EIP1559) { if (signed) {
tx = LegacyTransaction.fromTxData({ if (!EIP1559) {
nonce: useCall ? this.nextNonceForCall : res.nonce, tx = LegacyTransaction.fromSerializedTx(hexToBytes(data), { common: this.commonContext })
gasPrice: '0x1', } else {
gasLimit: gasLimit, tx = FeeMarketEIP1559Transaction.fromSerializedTx(hexToBytes(data), { common: this.commonContext })
to: (to as AddressLike), }
value: (value as BigIntLike),
data: hexToBytes(data)
}, { common: this.commonContext }).sign(account.privateKey)
} else {
tx = FeeMarketEIP1559Transaction.fromTxData({
nonce: useCall ? this.nextNonceForCall : res.nonce,
maxPriorityFeePerGas: '0x01',
maxFeePerGas: '0x7',
gasLimit: gasLimit,
to: (to as AddressLike),
value: (value as BigIntLike),
data: hexToBytes(data)
}).sign(account.privateKey)
} }
else {
if (!from && useCall && Object.keys(this.vmaccounts).length) {
from = Object.keys(this.vmaccounts)[0]
account = this.vmaccounts[from]
} else account = this.vmaccounts[from]
if (!account) {
return callback('Invalid account selected')
}
const res = await this.getVMObject().stateManager.getAccount(Address.fromString(from))
if (!EIP1559) {
tx = LegacyTransaction.fromTxData({
nonce: useCall ? this.nextNonceForCall : res.nonce,
gasPrice: '0x1',
gasLimit: gasLimit,
to: (to as AddressLike),
value: (value as BigIntLike),
data: hexToBytes(data)
}, { common: this.commonContext }).sign(account.privateKey)
} else {
tx = FeeMarketEIP1559Transaction.fromTxData({
nonce: useCall ? this.nextNonceForCall : res.nonce,
maxPriorityFeePerGas: '0x01',
maxFeePerGas: '0x7',
gasLimit: gasLimit,
to: (to as AddressLike),
value: (value as BigIntLike),
data: hexToBytes(data)
}).sign(account.privateKey)
}
}
if (useCall) this.nextNonceForCall++ if (useCall) this.nextNonceForCall++
const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e']

@ -1,7 +1,7 @@
'use strict' 'use strict'
import { EventManager } from '../eventManager' import { EventManager } from '../eventManager'
import type { Transaction as InternalTransaction } from './txRunner' import type { Transaction as InternalTransaction } from './txRunner'
import Web3 from 'web3' import { Web3 } from 'web3'
import { toBigInt, toHex } from 'web3-utils' import { toBigInt, toHex } from 'web3-utils'
export class TxRunnerWeb3 { export class TxRunnerWeb3 {

@ -21,6 +21,7 @@ export { ICompilerApi, ConfigurationSettings, iSolJsonBinData, iSolJsonBinDataBu
export { QueryParams } from './query-params' export { QueryParams } from './query-params'
export { VMexecutionResult } from './execution/txRunnerVM' export { VMexecutionResult } from './execution/txRunnerVM'
export { Registry } from './registry' export { Registry } from './registry'
export type { Transaction } from './execution/txRunner'
const helpers = { const helpers = {
ui: uiHelper, ui: uiHelper,

@ -1,5 +1,5 @@
'use strict' 'use strict'
import Web3, { Web3PluginBase } from 'web3' import { Web3, Web3PluginBase } from 'web3'
import { toNumber } from 'web3-utils' import { toNumber } from 'web3-utils'
export function extendWeb3 (web3) { export function extendWeb3 (web3) {

@ -18,6 +18,7 @@ export interface ICompilerApi {
setAppParameter: (name: string, value: string | boolean) => void setAppParameter: (name: string, value: string | boolean) => void
getFileManagerMode: () => string getFileManagerMode: () => string
isDesktop: () => boolean
setCompilerConfig: (settings: any) => void setCompilerConfig: (settings: any) => void
getCompilationResult: () => any getCompilationResult: () => any

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remix-simulator", "name": "@remix-project/remix-simulator",
"version": "0.2.56", "version": "0.2.57",
"description": "Ethereum IDE and tools for the web", "description": "Ethereum IDE and tools for the web",
"contributors": [ "contributors": [
{ {
@ -21,9 +21,9 @@
"@ethereumjs/common": "4.4.0", "@ethereumjs/common": "4.4.0",
"@ethereumjs/tx": "5.4.0", "@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0", "@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.0", "@ethereumjs/vm": "8.1.1",
"@metamask/eth-sig-util": "^7.0.2", "@metamask/eth-sig-util": "^7.0.2",
"@remix-project/remix-lib": "^0.5.63", "@remix-project/remix-lib": "^0.5.64",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^3.1.0", "async": "^3.1.0",
"body-parser": "^1.18.2", "body-parser": "^1.18.2",
@ -71,6 +71,6 @@
}, },
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme", "homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-simulator#readme",
"typings": "src/index.d.ts", "typings": "src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444", "gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01",
"types": "./src/index.d.ts" "types": "./src/index.d.ts"
} }

@ -63,6 +63,7 @@ export class Transactions {
methods () { methods () {
return { return {
eth_sendTransaction: this.eth_sendTransaction.bind(this), eth_sendTransaction: this.eth_sendTransaction.bind(this),
eth_sendRawTransaction: this.eth_sendRawTransaction.bind(this),
eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this), eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this),
eth_getCode: this.eth_getCode.bind(this), eth_getCode: this.eth_getCode.bind(this),
eth_call: this.eth_call.bind(this), eth_call: this.eth_call.bind(this),
@ -80,6 +81,29 @@ export class Transactions {
} }
} }
eth_sendRawTransaction (payload, cb) {
payload.params[0] = { data: payload.params[0], signed: true }
processTx(this.txRunnerInstance, payload, false, (error, result: VMexecutionResult) => {
if (!error && result) {
this.vmContext.addBlock(result.block)
const hash = bytesToHex(result.tx.hash())
this.vmContext.trackTx(hash, result.block, result.tx)
const returnValue = `${bytesToHex(result.result.execResult.returnValue) || '0x0'}`
const execResult: VMExecResult = {
exceptionError: result.result.execResult.exceptionError,
executionGasUsed: result.result.execResult.executionGasUsed,
gas: result.result.execResult.gas,
gasRefund: result.result.execResult.gasRefund,
logs: result.result.execResult.logs,
returnValue
}
this.vmContext.trackExecResult(hash, execResult)
return cb(null, result.transactionHash)
}
cb(error)
})
}
eth_sendTransaction (payload, cb) { eth_sendTransaction (payload, cb) {
// from might be lowercased address (web3) // from might be lowercased address (web3)
if (payload.params && payload.params.length > 0 && payload.params[0].from) { if (payload.params && payload.params.length > 0 && payload.params[0].from) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save