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. 624
      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. 18
      apps/remix-ide/src/app/tabs/locales/en/settings.json
  46. 9
      apps/remix-ide/src/app/udapp/run-tab.js
  47. BIN
      apps/remix-ide/src/assets/img/remi-prof.webp
  48. 5
      apps/remix-ide/src/blockchain/blockchain.tsx
  49. 2
      apps/remix-ide/src/blockchain/execution-context.js
  50. 2
      apps/remix-ide/src/blockchain/providers/injected.ts
  51. 2
      apps/remix-ide/src/blockchain/providers/node.ts
  52. 2
      apps/remix-ide/src/blockchain/providers/vm.ts
  53. 14
      apps/remixdesktop/after-pack.js
  54. 12
      apps/remixdesktop/esbuild.js
  55. 14
      apps/remixdesktop/run_git_ui_isogit_tests.sh
  56. 24
      apps/remixdesktop/rundist_esbuild.bash
  57. 24
      apps/remixdesktop/rundist_tsc.bash
  58. 24
      apps/remixdesktop/rundist_webpack.bash
  59. 248
      apps/remixdesktop/src/plugins/foundryPlugin.ts
  60. 220
      apps/remixdesktop/src/plugins/hardhatPlugin.ts
  61. 9
      apps/remixdesktop/src/types/index.ts
  62. 195
      apps/remixdesktop/test/lib/git.ts
  63. 157
      apps/remixdesktop/test/tests/app/foundry.test.ts
  64. 203
      apps/remixdesktop/test/tests/app/git-ui.test.ts
  65. 181
      apps/remixdesktop/test/tests/app/git-ui_2.test.ts
  66. 153
      apps/remixdesktop/test/tests/app/git-ui_3.test.ts
  67. 200
      apps/remixdesktop/test/tests/app/git-ui_4.test.ts
  68. 255
      apps/remixdesktop/test/tests/app/github.test.ts
  69. 190
      apps/remixdesktop/test/tests/app/github_2.test.ts
  70. 180
      apps/remixdesktop/test/tests/app/github_3.test.ts
  71. 90
      apps/remixdesktop/test/tests/app/hardhat.test.ts
  72. 4
      apps/solidity-compiler/src/app/compiler.ts
  73. 8
      libs/ghaction-helper/package.json
  74. 2
      libs/ghaction-helper/src/methods.ts
  75. 10
      libs/remix-analyzer/package.json
  76. 1
      libs/remix-api/src/index.ts
  77. 2
      libs/remix-api/src/lib/plugins/filePanel-api.ts
  78. 5
      libs/remix-api/src/lib/plugins/fileSystem-api.ts
  79. 13
      libs/remix-api/src/lib/plugins/fs-api.ts
  80. 43
      libs/remix-api/src/lib/plugins/git-api.ts
  81. 10
      libs/remix-api/src/lib/plugins/terminal-api.ts
  82. 8
      libs/remix-api/src/lib/remix-api.ts
  83. 206
      libs/remix-api/src/lib/types/git.ts
  84. 8
      libs/remix-astwalker/package.json
  85. 14
      libs/remix-debug/package.json
  86. 2
      libs/remix-debug/src/cmdline/index.ts
  87. 2
      libs/remix-debug/src/init.ts
  88. 1
      libs/remix-git/index.ts
  89. 345
      libs/remix-git/src/isogit.ts
  90. 4
      libs/remix-lib/package.json
  91. 9
      libs/remix-lib/src/execution/txExecution.ts
  92. 14
      libs/remix-lib/src/execution/txRunner.ts
  93. 23
      libs/remix-lib/src/execution/txRunnerVM.ts
  94. 2
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  95. 1
      libs/remix-lib/src/index.ts
  96. 2
      libs/remix-lib/src/init.ts
  97. 1
      libs/remix-lib/src/types/ICompilerApi.ts
  98. 8
      libs/remix-simulator/package.json
  99. 24
      libs/remix-simulator/src/methods/transactions.ts
  100. 22
      libs/remix-simulator/src/methods/txProcess.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 }}
with:
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 { GROTH16_VERIFIER, PLONK_VERIFIER } from './constant'
import { extractNameFromKey, extractParentFromKey } from '@remix-ui/helper'
import { ethers } from 'ethers'
export const compileCircuit = async (plugin: CircomPluginClient, appState: AppState) => {
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 {
if (status !== "computing") {
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 {
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']) => {
try {
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}`
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" }
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'))
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
if (appState.exportVerificationKey) {
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) {
const templates = { groth16: GROTH16_VERIFIER }
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)
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_VERIFICATION_KEY', payload: vKey })
} 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'))
const vKey = await snarkjs.zKey.exportVerificationKey(zkey_final, zkLogger(plugin, dispatch, 'SET_SETUP_EXPORT_FEEDBACK'))
if (appState.exportVerificationKey) {
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) {
const templates = { plonk: PLONK_VERIFIER }
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)
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_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_SETUP_EXPORT_STATUS', payload: 'done' })
} catch (e) {
plugin._paq.push(['trackEvent', 'circuit-compiler', 'runSetupAndExport', 'error', e.message])
dispatch({ type: 'SET_COMPILER_STATUS', payload: 'errored' })
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']) => {
try {
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 r1csPath = extractParentFromKey(appState.filePath) + `/.bin/${fileName.replace('.circom', '.r1cs')}`
// @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 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._paq.push(['trackEvent', 'circuit-compiler', 'generateProof', 'groth16.prove', verified])
if (appState.exportVerifierCalldata) {
const calldata = await snarkjs.groth16.exportSolidityCallData(proof, publicSignals)
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') {
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'))
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._paq.push(['trackEvent', 'circuit-compiler', 'generateProof', 'plonk.prove', verified])
if (appState.exportVerifierCalldata) {
const calldata = await snarkjs.plonk.exportSolidityCallData(proof, publicSignals)
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' })

@ -140,7 +140,7 @@ export function Container () {
<RenderIf condition={circuitApp.appState.signalInputs.length > 0}>
<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'}>
<CompilerFeedback feedback={circuitApp.appState.computeFeedback} filePathToId={circuitApp.appState.filePathToId} openErrorLocation={handleOpenErrorLocation} hideWarnings={circuitApp.appState.hideWarnings} askGPT={askGPT} />
</RenderIf>

@ -1,12 +1,14 @@
import { RenderIf, RenderIfNot } from "@remix-ui/helper";
import { FormattedMessage } from "react-intl";
import { CompilerStatus } from "../types";
import { computeWitness } from "../actions";
import { useState } from "react";
import type { CircomPluginClient } from "../services/circomPluginClient";
import { useContext, useState } from "react";
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 handleSignalInput = (e: any) => {
@ -47,9 +49,22 @@ export function WitnessSection ({ plugin, signalInputs, status }: {plugin: Circo
</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
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")}
data-id="compute_witness_btn"
>

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

@ -20,7 +20,7 @@ export class CircomPluginClient extends PluginClient {
private lastParsedFiles: Record<string, string> = {}
private lastCompiledFile: string = ''
private compiler: typeof compilerV215 & typeof compilerV216 & typeof compilerV217 & typeof compilerV218
private _paq = {
public _paq = {
push: (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.emit('statusChanged', { key: 'loading', title: 'Computing...', type: 'info' })
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)
// @ts-ignore
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.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>> {

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

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

@ -1,13 +1,17 @@
{
"scripts": {
"start:server": "npx ts-node server.ts"
"start:server": "tsc && node ./dist/server.js"
},
"dependencies": {
"body-parser": "^1.20.2",
"body-parser": "^1.20.3",
"child_process": "^1.0.2",
"express": "^4.19.2",
"express": "^4.20.0",
"git-http-backend": "^1.1.2",
"path": "^0.12.7",
"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
"@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:
version "1.3.8"
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"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
body-parser@1.20.2, body-parser@^1.20.2:
version "1.20.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
body-parser@1.20.3, body-parser@^1.20.2:
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"
@ -28,7 +35,25 @@ body-parser@1.20.2, body-parser@^1.20.2:
http-errors "2.0.0"
iconv-lite "0.4.24"
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"
type-is "~1.6.18"
unpipe "1.0.0"
@ -112,6 +137,11 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
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:
version "1.0.0"
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"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^4.19.2:
version "4.19.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
express@^4.20.0:
version "4.20.0"
resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.2"
body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~1.0.2"
encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.1"
merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
path-to-regexp "0.1.10"
proxy-addr "~2.0.7"
qs "6.11.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.18.0"
serve-static "1.15.0"
send "0.19.0"
serve-static "1.16.0"
setprototypeof "1.2.0"
statuses "2.0.1"
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"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
merge-descriptors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~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"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
path-to-regexp@0.1.10:
version "0.1.10"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path@^0.12.7:
version "0.12.7"
@ -384,6 +414,13 @@ qs@6.11.0:
dependencies:
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:
version "1.2.1"
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"
statuses "2.0.1"
serve-static@1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
send@0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
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:
encodeurl "~1.0.2"
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"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel@^1.0.4:
side-channel@^1.0.4, side-channel@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
@ -483,6 +539,16 @@ type-is@~1.6.18:
media-typer "0.3.0"
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:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"

@ -102,6 +102,8 @@ module.exports = {
.waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.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')
.pause(1000)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')

@ -16,22 +16,45 @@ module.exports = {
'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.
click('*[data-id="github-panel"]')
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"]',
@ -101,6 +124,7 @@ module.exports = {
.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'
@ -191,10 +215,27 @@ module.exports = {
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) {
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"]',
@ -253,6 +294,7 @@ module.exports = {
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'
@ -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')
.waitForElementVisible('*[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="githubEmail"]', 'git@example.com')
.click('*[data-id="saveGitHubCredentials"]')
@ -603,7 +607,7 @@ async function createCommitOnLocalServer(path: string, message: string) {
async function spawnGitServer(path: string): Promise<ChildProcess> {
console.log(process.cwd())
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)
return new Promise((resolve, reject) => {
server.stdout.on('data', function (data) {

@ -90,6 +90,8 @@ module.exports = {
browser
.clickLaunchIcon('solidity')
.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', {
content: myToken
}).useXpath().waitForElementVisible("//*[@class='view-line' and contains(.,'gas')]")

@ -31,7 +31,21 @@ module.exports = {
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"]')
.scrollAndClick('*[data-id="modalDialogCustomPromptTextCreate"]')
.setValue('*[data-id="modalDialogCustomPromptTextCreate"]', 'workspace_erc721')
// eslint-disable-next-line dot-notation
.execute(function () { document.querySelector('*[data-id="modalDialogCustomPromptTextCreate"]')['value'] = 'workspace_erc721' })
.modalFooterOKClick('TemplatesSelection')
.pause(100)
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]')

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

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

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

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

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

@ -4,7 +4,7 @@ echo "Downloading specified soljson.js version based on defaultVersion in packag
set -e
# Check if curl and jq are installed
# Check if curl is installed
if ! command -v curl &> /dev/null; then
echo "curl could not be found"
exit 1
@ -14,32 +14,38 @@ fi
defaultVersion=$(grep '"defaultVersion"' package.json | awk -F '"' '{print $4}')
echo "Specified version from package.json: $defaultVersion"
# Download the list.json file containing available versions
curl -s https://binaries.soliditylang.org/wasm/list.json > list.json
# Fetch the list.json from the Solidity binaries
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
echo "The specified version $defaultVersion could not be found in the list"
exit 1
fi
echo "Path for the specified version: $defaultVersion"
fullPath="https://binaries.soliditylang.org/bin/$defaultVersion"
echo "Download fullPath: $fullPath"
# Ensure the target directory exists
if [ ! -d "./apps/remix-ide/src/assets/js/soljson" ]; then
mkdir -p ./apps/remix-ide/src/assets/js/soljson
fi
# Download the 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"
# 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"
curl -s "$fullPath" > ./apps/remix-ide/src/assets/js/soljson.js
# 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 list.json ./apps/remix-ide/src/assets/list.json
cp ./apps/remix-ide/src/assets/js/soljson.js "./apps/remix-ide/src/assets/js/soljson/$defaultVersion.js"
# Clean up by removing the list.json
rm list.json
echo "Download and setup of soljson.js complete"

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

@ -4,26 +4,16 @@ import {
Plugin
} from '@remixproject/engine'
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 FormData from 'form-data'
import axios from 'axios'
import { Registry } from '@remix-project/remix-lib'
import { Octokit, App } from "octokit"
import { OctokitResponse } from '@octokit/types'
import { Endpoints } from "@octokit/types"
import { Octokit } from "octokit"
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 { LibraryProfile, StatusEvents } from '@remixproject/plugin-utils'
import { ITerminal } from '@remixproject/plugin-api/src/lib/terminal'
import { partial } from 'lodash'
import { branch, commitChange, remote } from '@remix-ui/git'
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 { LibraryProfile } from '@remixproject/plugin-utils'
import { CustomRemixApi } from '@remix-api'
import { isoGit } from "@remix-git"
declare global {
interface Window { remixFileSystemCallback: IndexedDBStorage; remixFileSystem: any; }
}
@ -34,49 +24,17 @@ const profile: LibraryProfile = {
description: 'Decentralized git provider',
icon: 'assets/img/fileManager.webp',
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'],
kind: 'file-system'
}
class DGitProvider extends Plugin {
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[]
class DGitProvider extends Plugin<any, CustomRemixApi> {
constructor() {
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 = '') {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
return {
fs: window.remixFileSystem,
dir: '/'
}
}
const workspace = await this.call('filePanel', 'getCurrentWorkspace')
if (!workspace) return
@ -86,62 +44,12 @@ class DGitProvider extends Plugin {
}
}
async addIsomorphicGitConfig(input) {
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
}
async getToken() {
return await this.call('config' as any, 'getAppParameter', 'settings/gist-access-token')
}
return config
}
async getCommandUser(input) {
const author = {
async getAuthor(input) {
const author: author = {
name: '',
email: ''
}
@ -167,7 +75,7 @@ class DGitProvider extends Plugin {
return author
}
async init(input?: initInput): Promise<void> {
async init(input?: initInputType): Promise<void> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'init', {
defaultBranch: (input && input.defaultBranch) || 'main'
@ -192,11 +100,10 @@ class DGitProvider extends Plugin {
return version
}
async status(cmd): Promise<Array<StatusRow>> {
async status(cmd: statusInput): Promise<Array<StatusRow>> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
const status = await this.call('isogit', 'status', cmd)
return status
}
@ -208,7 +115,7 @@ class DGitProvider extends Plugin {
return status
}
async add(cmd: addInput): Promise<void> {
async add(cmd: addInputType): Promise<void> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'add', cmd)
@ -222,7 +129,7 @@ class DGitProvider extends Plugin {
this.emit('add')
}
async rm(cmd: rmInput) {
async rm(cmd: rmInputType) {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'rm', cmd)
@ -231,26 +138,11 @@ class DGitProvider extends Plugin {
...await this.addIsomorphicGitConfigFS(),
...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')
}
}
async checkout(cmd: checkoutInput): Promise<void> {
async checkout(cmd: checkoutInputType): Promise<void> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'checkout', cmd)
@ -291,12 +183,11 @@ class DGitProvider extends Plugin {
this.emit('checkout')
}
async log(cmd: { ref: string }): Promise<ReadCommitResult[]> {
async log(cmd: logInputType): Promise<ReadCommitResult[]> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
const status = await this.call('isogit', 'log', {
...cmd,
depth: 10
})
return status
@ -306,101 +197,32 @@ class DGitProvider extends Plugin {
...await this.addIsomorphicGitConfigFS(),
...cmd,
})
return status
}
async compareBranches({ branch, remote }: compareBranchesInput): Promise<branchDifference> {
// Get current branch commits
const headCommits = await git.log({
...await this.addIsomorphicGitConfigFS(),
ref: branch.name,
});
// 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[]> {
const result: commitChange[] = await git.walk({
...await this.addIsomorphicGitConfigFS(),
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
if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'compareBranches', { branch, remote })
}
} catch (e) {
// ignore
return await isoGit.compareBranches({ branch, remote }, await this.addIsomorphicGitConfigFS())
}
// 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"
async getCommitChanges(commitHash1: string, commitHash2: string): Promise<commitChange[]> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
const result = this.call('isogit', 'getCommitChanges', commitHash1, commitHash2)
return result
}
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())) {
return await this.call('isogit', 'remotes', config)
return await this.call('isogit', 'remotes')
}
let remotes: remote[] = []
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
return await isoGit.remotes(await this.addIsomorphicGitConfigFS())
}
async branch(cmd: branchInputType): Promise<void> {
@ -423,68 +245,28 @@ class DGitProvider extends Plugin {
return status
}
async currentbranch(config: currentBranchInput): Promise<branch> {
async currentbranch(input: currentBranchInput): Promise<branch> {
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 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 await isoGit.currentbranch(input, defaultConfig)
}
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())) {
return await this.call('isogit', 'branches')
const branches = await this.call('isogit', 'branches')
return 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
} catch (e) {
console.log(e)
return []
}
return await isoGit.branches(cmd)
}
async commit(cmd: commitInput): Promise<string> {
async commit(cmd: commitInputType): Promise<string> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
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> {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
@ -550,26 +319,9 @@ class DGitProvider extends Plugin {
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> {
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
}
await git.addRemote({ ...await this.addIsomorphicGitConfigFS(), url: input.url, remote: input.name })
@ -577,51 +329,39 @@ class DGitProvider extends Plugin {
async delremote(input: remote) {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
await this.call('isogit', 'delremote', { remote: input.name })
await this.call('isogit', 'delremote', input)
return
}
await git.deleteRemote({ ...await this.addIsomorphicGitConfigFS(), remote: input.name })
}
async localStorageUsed() {
return this.calculateLocalStorage()
}
async clone(input: cloneInputType) {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
try {
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 {
const result = await this.call('isogit', 'clone', cmd)
this.call('fs', 'openWindow', folder)
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) {
this.call('notification', 'alert', {
id: 'dgitAlert',
title: 'Error Cloning',
message: 'Unexpected error while cloning the repository: \n' + e.toString(),
})
}
} else {
const permission = await this.askUserPermission('clone', '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.')
if (!input.workspaceExists) await this.call('filePanel', 'createWorkspace', input.workspaceName || `workspace_${Date.now()}`, true)
const cmd = {
url: input.url,
singleBranch: input.singleBranch,
ref: input.branch,
depth: input.depth || 10,
...await this.addIsomorphicGitConfig(input),
...await isoGit.addIsomorphicGitProxyConfig(input, this),
...await this.addIsomorphicGitConfigFS()
}
this.call('terminal', 'logHtml', `Cloning ${input.url}... please wait...`)
@ -674,6 +414,9 @@ class DGitProvider extends Plugin {
}
async updateSubmodules(input) {
if ((Registry.getInstance().get('platform').api.isDesktop())) {
return await this.call('isogit', 'updateSubmodules', null)
}
try {
const currentDir = (input && input.dir) || ''
const gitmodules = await this.parseGitmodules(currentDir)
@ -705,10 +448,10 @@ class DGitProvider extends Plugin {
url: module.url,
singleBranch: true,
depth: 1,
...await this.addIsomorphicGitConfig({
...await isoGit.addIsomorphicGitProxyConfig({
...input,
provider: 'github'
}),
provider: 'github',
}, this),
...await this.addIsomorphicGitConfigFS(dir)
}
this.call('terminal', 'logHtml', `Cloning submodule ${dir}...`)
@ -732,10 +475,10 @@ class DGitProvider extends Plugin {
if (result && result.length) {
this.call('terminal', 'logHtml', `Checking out submodule ${dir} to ${result[0]} in directory ${dir}`)
await git.fetch({
...await this.addIsomorphicGitConfig({
...await isoGit.addIsomorphicGitProxyConfig({
...input,
provider: 'github'
}),
provider: 'github',
}, this),
...await this.addIsomorphicGitConfigFS(dir),
singleBranch: true,
ref: result[0]
@ -782,55 +525,22 @@ class DGitProvider extends Plugin {
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())) {
return await this.call('isogit', 'push', cmd)
return await this.call('isogit', 'push', input)
} else {
const cmd2 = {
...cmd,
...await this.addIsomorphicGitConfig(input),
}
const result = await git.push({
...await this.addIsomorphicGitConfigFS(),
...cmd2
})
const result = await isoGit.push(input, await this.addIsomorphicGitConfigFS(), this)
return result
}
}
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
if ((Registry.getInstance().get('platform').api.isDesktop())) {
result = await this.call('isogit', 'pull', cmd)
result = await this.call('isogit', 'pull', input)
}
else {
const cmd2 = {
...cmd,
...await this.addIsomorphicGitConfig(input),
}
result = await git.pull({
...await this.addIsomorphicGitConfigFS(),
...cmd2
})
result = await isoGit.pull(input, await this.addIsomorphicGitConfigFS(), this)
}
setTimeout(async () => {
await this.call('fileManager', 'refresh')
@ -839,192 +549,19 @@ class DGitProvider extends Plugin {
}
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
if ((Registry.getInstance().get('platform').api.isDesktop())) {
result = await this.call('isogit', 'fetch', cmd)
} else {
const cmd2 = {
...cmd,
...await this.addIsomorphicGitConfig(input),
}
result = await git.fetch({
...await this.addIsomorphicGitConfigFS(),
...cmd2
result = await this.call('isogit', 'fetch', {
...input,
})
}
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 {
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 () => {
await this.call('fileManager', 'refresh')
}, 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
}
@ -1056,15 +593,22 @@ class DGitProvider extends Plugin {
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 scopes = user.headers['x-oauth-scopes'] || ''
return {
user: user.data,
user: {
...user.data, isConnected:
user.data.login !== undefined && user.data.login !== null && user.data.login !== ''
},
emails: emails.data,
scopes: scopes && scopes.split(',')
scopes: scopes && scopes.split(',').map(scope => scope.trim())
}
} catch (e) {
return null
@ -1072,6 +616,7 @@ class DGitProvider extends Plugin {
}
async remotecommits(input: remoteCommitsInputType): Promise<pagedCommits[]> {
const octokit = new Octokit({
auth: input.token
})
@ -1162,23 +707,4 @@ const addSlash = (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

@ -6,8 +6,6 @@ import { FileSystemProvider } from '@remix-ui/workspace' // eslint-disable-line
import {Registry} from '@remix-project/remix-lib'
import { RemixdHandle } from '../plugins/remixd-handle'
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')
/*
@ -68,8 +66,6 @@ module.exports = class Filepanel extends ViewPlugin {
this.el.setAttribute('id', 'fileExplorerView')
this.remixdHandle = new RemixdHandle(this.fileProviders.localhost, appManager)
this.hardhatHandle = new HardhatHandle()
this.foundryHandle = new FoundryHandle()
this.truffleHandle = new TruffleHandle()
this.contentImport = contentImport
this.workspaces = []

@ -30,11 +30,14 @@ export type PanelConfiguration = {
export class Layout extends Plugin {
event: any
panels: panels
maximised: { [key: string]: boolean }
enhanced: { [key: string]: boolean }
maximized: { [key: string]: boolean }
constructor () {
super(profile)
this.maximised = {
'dgit': true
this.maximized = {}
this.enhanced = {
'dgit': true,
'LearnEth': true
}
this.event = new EventEmitter()
}
@ -80,18 +83,30 @@ export class Layout extends Plugin {
})
this.on('sidePanel', 'focusChanged', async (name) => {
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')
} else {
}
if (!this.enhanced[current] && !this.maximized[current]) {
this.event.emit('resetsidepanel')
}
})
this.on('pinnedPanel', 'pinnedPlugin', async (name) => {
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')
} else {
}
if (!this.enhanced[current] && !this.maximized[current]) {
this.event.emit('resetpinnedpanel')
}
})
@ -128,13 +143,13 @@ export class Layout extends Plugin {
async maximiseSidePanel () {
const current = await this.call('sidePanel', 'currentFocus')
this.maximised[current] = true
this.maximized[current] = true
this.event.emit('maximisesidepanel')
}
async maximisePinnedPanel () {
const current = await this.call('pinnedPanel', 'currentFocus')
this.maximised[current] = true
this.maximized[current] = true
this.event.emit('maximisepinnedpanel')
}
@ -146,13 +161,13 @@ export class Layout extends Plugin {
async resetSidePanel () {
const current = await this.call('sidePanel', 'currentFocus')
this.maximised[current] = false
this.enhanced[current] = false
this.event.emit('resetsidepanel')
}
async resetPinnedPanel () {
const current = await this.call('pinnedPanel', 'currentFocus')
this.maximised[current] = false
this.enhanced[current] = false
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"]
}
},
"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'],
['ERC1155', 'primary'],
]}
title='Template explorer'
title='Workspace Templates'
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
plugin={this}
key={template.name}
title={template.name}
tooltipTitle={template.tooltip}
hScrollable={false}
>
{template.items.map(item => {
@ -166,14 +167,14 @@ export class TemplatesSelectionPlugin extends ViewPlugin {
id={item.name}
searchKeywords={[item.displayName, item.description, template.name]}
tagList={item.tagList}
classList='TSCellStyle'
classList={'TSCellStyle'}
>
<div className='d-flex justify-content-between h-100 flex-column'>
<div className='d-flex flex-column'>
<div>
{item.description && <span className='text-dark'>{item.description}</span>}
</div>
<div className='d-flex flex-wrap'>
<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.mintable) && <span className='badgeForCell text-secondary'>mintable</span>}
{(item.opts && item.opts.burnable) && <span className='badgeForCell text-secondary'>burnable</span>}
@ -211,6 +212,20 @@ export class TemplatesSelectionPlugin extends ViewPlugin {
</div>
</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>
})}
</RemixUIGridView>

@ -1,10 +1,9 @@
export const templates = (intl) => {
export const templates = (intl, plugin) => {
return [
{
name: "Generic",
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' }
]
},
@ -15,102 +14,102 @@ export const templates = (intl) => {
value: "ozerc20",
displayName: "ERC20",
tagList: ["ERC20", "Solidity"],
description: 'A simple ERC20 project'
description: 'A simple fungible token contract'
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
tagList: ["ERC721", "Solidity"],
description: 'A simple ERC721 (aka NFT) project'
value: "ozerc20",
displayName: "ERC20",
description: "An ERC20 contract with:",
tagList: ["Solidity"],
opts: {
mintable: true
}
},
{
value: "ozerc1155",
tagList: ["Solidity"],
displayName: "ERC1155",
description: 'A simple ERC1155 (multi token) project'
value: "ozerc20",
displayName: "ERC20",
description: "An ERC20 contract with:",
tagList: ["Solidity", "ERC20"],
opts: {
mintable: true,
burnable: true
},
},
{
value: "ozerc20",
displayName: "ERC20",
description: "A standard interface for fungible tokens",
tagList: ["Solidity"],
description: "An ERC20 contract with:",
opts: {
mintable: true
}
mintable: true,
pausable: true
},
tagList: ["ERC20", "Solidity"]
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard",
tagList: ["Solidity", "ERC721"],
opts: {
mintable: true
}
tagList: ["ERC721", "Solidity"],
description: 'A simple non-fungible token (NFT) contract'
},
{
value: "ozerc1155",
displayName: "ERC1155",
tagList: ["Solidity"],
description: "A standard interface for contracts that manage multiple token types",
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "An ERC721 contract with:",
tagList: ["Solidity", "ERC721"],
opts: {
mintable: true
}
},
{
value: "ozerc20",
displayName: "ERC20",
description: "A standard interface for fungible tokens",
tagList: ["Solidity", "ERC20"],
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "An ERC721 contract with:",
opts: {
mintable: true,
burnable: true
},
tagList: ["ERC721", "Solidity"]
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard",
description: "An ERC721 contract with:",
opts: {
mintable: true,
burnable: true
pausable: true
},
tagList: ["ERC721", "Solidity"]
},
{
value: "ozerc1155",
tagList: ["Solidity"],
displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types",
opts: {
mintable: true,
burnable: true
},
tagList: ["ERC1155", "Solidity"]
description: 'A simple multi token contract'
},
{
value: "ozerc20",
displayName: "ERC20",
description: "A standard interface for fungible tokens",
value: "ozerc1155",
displayName: "ERC1155",
tagList: ["Solidity"],
description: "An ERC1155 contract with:",
opts: {
mintable: true,
pausable: true
},
tagList: ["ERC20", "Solidity"]
mintable: true
}
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard",
value: "ozerc1155",
displayName: "ERC1155",
description: "An ERC1155 contract with:",
opts: {
mintable: true,
pausable: true
burnable: true
},
tagList: ["ERC721", "Solidity"]
tagList: ["ERC1155", "Solidity"]
},
{
value: "ozerc1155",
displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types",
tagList: ["ERC20"],
description: "An ERC1155 contract with:",
tagList: ["ERC1155"],
opts: {
mintable: true,
pausable: true
@ -123,120 +122,120 @@ export const templates = (intl) => {
items: [
{
value: "ozerc20",
displayName: "ERC20",
description: "A standard interface for fungible tokens",
displayName: "UUPS ERC20",
description: "A simple ERC20 contract using the Universal Upgradeable Proxy Standard (UUPS) pattern",
opts: {
upgradeable: 'uups'
},
tagList: ["ERC20", "Solidity"]
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard",
value: "ozerc20",
displayName: "UUPS ERC20",
description: "UUPS ERC20 contract with:",
opts: {
upgradeable: 'uups'
upgradeable: 'uups',
mintable: true
},
tagList: ["ERC721", "Solidity"]
tagList: ["ERC20", "Solidity"]
},
{
value: "ozerc1155",
displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types",
value: "ozerc20",
displayName: "UUPS ERC20",
description: "UUPS ERC20 contract with:",
opts: {
upgradeable: 'uups'
upgradeable: 'uups',
mintable: true,
burnable: true
},
tagList: ["ERC1155", "Solidity"]
tagList: ["ERC20", "Solidity"]
},
{
value: "ozerc20",
displayName: "ERC20",
description: "A standard interface for fungible tokens",
displayName: "UUPS ERC20",
description: "UUPS ERC20 contract with:",
opts: {
upgradeable: 'uups',
mintable: true
mintable: true,
pausable: true
},
tagList: ["ERC20", "Solidity"]
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard",
displayName: "UUPS ERC721 (NFT)",
description: "A simple UUPS ERC721 contract",
opts: {
upgradeable: 'uups',
mintable: true
upgradeable: 'uups'
},
tagList: ["ERC721", "Solidity"]
},
{
value: "ozerc1155",
displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types",
value: "ozerc721",
displayName: "UUPS ERC721 (NFT)",
description: "UUPS ERC721 contract with:",
opts: {
upgradeable: 'uups',
mintable: true
},
tagList: ["ERC1155", "Solidity"]
tagList: ["ERC721", "Solidity"]
},
{
value: "ozerc20",
displayName: "ERC20",
description: "A standard interface for fungible tokens",
value: "ozerc721",
displayName: "UUPS ERC721 (NFT)",
description: "UUPS ERC721 contract with:",
opts: {
upgradeable: 'uups',
mintable: true,
burnable: true
},
tagList: ["ERC20", "Solidity"]
tagList: ["ERC721", "Solidity"]
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard",
displayName: "UUPS ERC721 (NFT)",
description: "UUPS ERC721 contract with:",
opts: {
upgradeable: 'uups',
mintable: true,
burnable: true
pausable: true
},
tagList: ["ERC721", "Solidity"]
},
{
value: "ozerc1155",
displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types",
displayName: "UUPS ERC1155",
description: "A simple multi token contract using the UUPS pattern",
opts: {
upgradeable: 'uups',
mintable: true,
burnable: true
upgradeable: 'uups'
},
tagList: ["ERC1155", "Solidity"]
},
{
value: "ozerc20",
displayName: "ERC20",
description: "A standard interface for fungible tokens",
value: "ozerc1155",
displayName: "UUPS ERC1155",
description: "UUPS ERC1155 with:",
opts: {
upgradeable: 'uups',
mintable: true,
pausable: true
mintable: true
},
tagList: ["ERC20", "Solidity"]
tagList: ["ERC1155", "Solidity"]
},
{
value: "ozerc721",
displayName: "ERC721 (NFT)",
description: "Non-fungible Token Standard",
value: "ozerc1155",
displayName: "UUPS ERC1155",
description: "UUPS ERC1155 with:",
opts: {
upgradeable: 'uups',
mintable: true,
pausable: true
burnable: true
},
tagList: ["ERC721", "Solidity"]
tagList: ["ERC1155", "Solidity"]
},
{
value: "ozerc1155",
displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types",
displayName: "UUPS ERC1155",
description: "UUPS ERC1155 with:",
opts: {
upgradeable: 'uups',
mintable: true,
@ -246,8 +245,8 @@ export const templates = (intl) => {
},
{
value: "ozerc1155",
displayName: "ERC1155",
description: "A standard interface for contracts that manage multiple token types",
displayName: "UUPS ERC1155",
description: "UUPS ERC1155 with:",
opts: {
upgradeable: 'uups',
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: [
{ 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",
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",
items: [
{ value: "semaphore", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.semaphore' }), description: 'Run a ZK Semaphore circom circuit.' },
{ value: "hashchecker", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.hashchecker' }), description: 'Run a ZK Hash checker circom circuit.' },
{ value: "rln", tagList: ["ZKP"], displayName: intl.formatMessage({ id: 'filePanel.rln' }), description: 'Run a Rate Limiting Nullifier circom circuit.' }
{ 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: 'Hash checker 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",
tagList: ["ZKP"],
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: [
{ value: "uniswapV4Template",
displayName: intl.formatMessage({ id: 'filePanel.uniswapV4Template' }),
description: 'Use an Uniswap hook'
description: 'Use a Uniswap hook'
},
{
value: "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",
@ -315,12 +373,12 @@ export const templates = (intl) => {
value: "contractCreate2Factory",
tagList: ["Solidity"],
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",
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",
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: [
{ value: "runJsTestAction",
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",
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",
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

@ -25,7 +25,7 @@ const profile = {
methods: []
}
type ProvidersSection = `Injected` | 'Remix VMs' | 'Externals'
type ProvidersSection = `Injected` | 'Remix VMs' | 'Externals' | 'Remix forked VMs'
export class EnvironmentExplorer extends ViewPlugin {
providers: { [key in ProvidersSection]: Provider[] }
@ -39,6 +39,7 @@ export class EnvironmentExplorer extends ViewPlugin {
this.providers = {
'Injected': [],
'Remix VMs': [],
'Remix forked VMs': [],
'Externals': []
}
}
@ -52,6 +53,8 @@ export class EnvironmentExplorer extends ViewPlugin {
addProvider (provider: Provider) {
if (provider.isInjected) {
this.providers['Injected'].push(provider)
} else if (provider.isForkedVM) {
this.providers['Remix forked VMs'].push(provider)
} else if (provider.isVM) {
this.providers['Remix VMs'].push(provider)
} else {
@ -81,7 +84,8 @@ export class EnvironmentExplorer extends ViewPlugin {
this.providers = {
'Injected': [],
'Remix VMs': [],
'Externals': []
'Externals': [],
'Remix forked VMs': []
}
for (const [key, provider] of Object.entries(this.providersFlat)) {
this.addProvider(provider)
@ -167,6 +171,40 @@ export class EnvironmentExplorer extends ViewPlugin {
<div>{provider.description}</div>
</RemixUIGridCell>
})}</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
plugin={this}
title='Deploy to an external Provider.'

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

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

@ -9,7 +9,7 @@ import { QueryParams } from '@remix-project/remix-lib'
import * as packageJson from '../../../../../package.json'
import { compilerConfigChangedToastMsg, compileToastMsg } from '@remix-ui/helper'
import { isNative } from '../../remixAppManager'
import { Registry } from '@remix-project/remix-lib'
const profile = {
name: 'solidity',
displayName: 'Solidity compiler',
@ -90,6 +90,10 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
return this.fileManager.mode
}
isDesktop () {
return Registry.getInstance().get('platform').api.isDesktop()
}
/**
* set the compiler configuration
* 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 { DebuggerApiMixin } from '@remix-ui/debugger-ui'
import { ViewPlugin } from '@remixproject/engine-web'

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

@ -1,4 +1,6 @@
{
"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.workspace": "WORKSPACES",
"filePanel.create": "Create",
"filePanel.createLabel": "Create Using Template",
"filePanel.createBlank":"Create Blank",
"filePanel.create.desktop": "Create Project",
"filePanel.clone": "Clone",
"filePanel.download": "Download",
@ -9,7 +12,8 @@
"filePanel.restore": "Restore",
"filePanel.name": "Name",
"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.rename": "Rename Workspace",
"filePanel.workspace.save_workspace": "Save Workspace",
@ -67,7 +71,7 @@
"filePanel.createNewFile": "Create new file",
"filePanel.createNewFolder": "Create new folder",
"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.uploadFolder": "Upload folder",
"filePanel.updateGist": "Update Gist",
@ -142,5 +146,7 @@
"filePanel.movingFolderFailedMsg": "Unexpected error while moving folder: {src}",
"filePanel.workspaceActions": "Workspace actions",
"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."
}

@ -16,5 +16,6 @@
"git.unstageall": "unstage all",
"git.stageall": "stage all",
"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.here": "more",
"home.featured": "Featured",
"home.jumpIntoWeb3": "JUMP INTO WEB3",
"home.jumpIntoWeb3More": "More",
"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.learnEthPromoTitle": "LearnEth: Tutorials inside Remix",
"home.learnEthPromoButton": "Start Learning",
"home.learnEthPromoText": "Check out tutorials on Remix, Solidity, and other Web3 projects. Great for all skill levels.",
"home.remixYouTube": "WATCH TO LEARN",
"home.remixYouTubeText1": "Video Tips from the Remix Team",
"home.remixYouTubeMore": "Watch",
@ -37,6 +37,7 @@
"home.ozerc1155TemplateDesc": "Create an ERC1155 token by importing OpenZeppelin library.",
"home.gnosisSafeMultisigTemplateDesc": "Create Multi-Signature wallets using this template.",
"home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.",
"home.learnEthPluginDesc": "Learn about Remix, Solidity, and other Web3 projects.",
"home.learn": "Learn",
"home.learnEth1": "Remix Basics",
"home.learnEth1Desc": "An introduction to Remix's interface and basic operations.",

@ -1,16 +1,18 @@
{
"settings.displayName": "Settings",
"settings.reset": "Reset to Default settings",
"settings.general": "General settings",
"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.general": "General",
"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.wordWrapText": "Word wrap in editor",
"settings.useAutoCompleteText": "Enable code completion in editor.",
"settings.useShowGasInEditorText": "Display gas estimates in editor.",
"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.enablePersonalModeText": " Enable Personal Mode for web3 provider. Transaction sent over Web3 will use the web3.personal API.\n",
"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.useAutoCompleteText": "Enable code completion in editor",
"settings.useShowGasInEditorText": "Display gas estimates in editor",
"settings.displayErrorsText": "Display errors in editor while typing",
"settings.matomoAnalytics": "Enable Matomo Analytics. See",
"settings.matomoAnalyticsTooltip": "We do not collect personally identifiable information (PII). The info is used to improve the site’s UX & UI.",
"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.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",

@ -165,7 +165,7 @@ export class RunTab extends ViewPlugin {
'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', {
position,
options: {},
@ -176,6 +176,7 @@ export class RunTab extends ViewPlugin {
logos: logos[name],
fork,
isInjected,
isForkedVM: forkedVM,
isVM,
title,
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(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(2, 'vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'cancun', 'settingsVMMainnetMode', titleVM)
await addProvider(3, 'vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'cancun', 'settingsVMSepoliaMode', titleVM)
await addProvider(4, 'vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', 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, true)
await addProvider(4, 'vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM, true)
// wallet connect
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
value: string
data: string
gasLimit: number
gasLimit: string
useCall: boolean
timestamp?: number
}
@ -55,6 +55,7 @@ export type Provider = {
description?: string
isInjected: boolean
isVM: boolean
isForkedVM: boolean
title: string
init: () => Promise<void>
provider:{
@ -780,6 +781,8 @@ export class Blockchain extends Plugin {
sendTransaction(tx: Transaction) {
return new Promise((resolve, reject) => {
this.executionContext.detectNetwork((error, network) => {
tx.gasLimit = '0x0' // force using gas estimation
if (error) return reject(error)
if (network.name === 'Main' && network.id === '1') {
return reject(new Error('It is not allowed to make this action against mainnet'))

@ -1,6 +1,6 @@
/* global ethereum */
'use strict'
import Web3 from 'web3'
import { Web3 } from 'web3'
import { execution } from '@remix-project/remix-lib'
import EventManager from '../lib/events'
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 { ExecutionContext } from '../execution-context'

@ -1,4 +1,4 @@
import Web3 from 'web3'
import { Web3 } from 'web3'
import { hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util'
import { Personal } from 'web3-eth-personal'
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 { privateToAddress, hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util'
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 () {
return 'browser'
}
isDesktop() {
return false
}
}

@ -1,6 +1,6 @@
{
"name": "@remix-project/ghaction-helper",
"version": "0.1.36",
"version": "0.1.37",
"description": "Solidity Tests GitHub Action Helper",
"main": "src/index.js",
"scripts": {
@ -19,17 +19,17 @@
},
"homepage": "https://github.com/ethereum/remix-project#readme",
"devDependencies": {
"@remix-project/remix-solidity": "^0.5.42",
"@remix-project/remix-solidity": "^0.5.43",
"@types/chai": "^4.3.4",
"typescript": "^4.9.3"
},
"dependencies": {
"@ethereum-waffle/chai": "^3.4.4",
"@remix-project/remix-simulator": "^0.2.56",
"@remix-project/remix-simulator": "^0.2.57",
"chai": "^4.3.7",
"ethers": "^5.7.2",
"web3": "^4.1.1"
},
"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 { getArtifactsByContractName } from './artifacts-helper'
import { SignerWithAddress } from './signer'
import Web3 from "web3"
import { Web3 } from "web3"
const providerConfig = {
fork: global.fork || null,

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

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

@ -7,6 +7,6 @@ export interface IFilePanelApi {
switchToWorkspace: (workspace: string) => Promise<void>;
} & StatusEvents
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"
// Extended interface with 'diff' method
export interface IExtendedFileSystem extends IFileSystem {
methods: IFileSystem['methods'] & {
/** Compare the differences between two files */
diff(change: commitChange): Promise<void>
refresh(): Promise<void>
hasGitSubmodules(): 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 { StatusEvents } from "@remixproject/plugin-utils"
import { IConfigApi } from "./plugins/config-api"
import { IFileDecoratorApi } from "./plugins/filedecorator-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 { ISettings } from "./plugins/settings-api"
import { IExtendedTerminalApi } from "./plugins/terminal-api"
import { IFilePanelApi } from "./plugins/filePanel-api"
import { Plugin } from "@remixproject/engine"
import { ISidePanelApi } from "./plugins/sidePanel-api"
import { IPinnedPanelApi } from "./plugins/pinned-panel-api"
import { ILayoutApi } from "./plugins/layout-api"
@ -20,6 +21,9 @@ export interface ICustomRemixApi extends IRemixApi {
settings: ISettings
fileDecorator: IFileDecoratorApi
fileManager: IExtendedFileSystem
isogit: IGitApi,
terminal: IExtendedTerminalApi
fs: IFs
filePanel: IFilePanelApi
sidePanel: ISidePanelApi
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",
"version": "0.0.86",
"version": "0.0.87",
"description": "Tool to walk through Solidity AST",
"main": "src/index.js",
"scripts": {
@ -36,8 +36,8 @@
"@ethereumjs/block": "5.3.0",
"@ethereumjs/tx": "5.4.0",
"@ethereumjs/util": "9.1.0",
"@ethereumjs/vm": "8.1.0",
"@remix-project/remix-lib": "^0.5.63",
"@ethereumjs/vm": "8.1.1",
"@remix-project/remix-lib": "^0.5.64",
"@types/tape": "^4.2.33",
"async": "^2.6.2",
"ethers": "^5.4.2",
@ -53,6 +53,6 @@
"tap-spec": "^5.0.0"
},
"typings": "src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444",
"gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01",
"types": "./src/index.d.ts"
}

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

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

@ -1,5 +1,5 @@
'use strict'
import Web3, { Web3PluginBase } from 'web3'
import { Web3, Web3PluginBase } from 'web3'
import { toNumber } from 'web3-utils'
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",
"version": "0.5.63",
"version": "0.5.64",
"description": "Library to various Remix tools",
"contributors": [
{
@ -55,6 +55,6 @@
},
"homepage": "https://github.com/ethereum/remix-project/tree/master/libs/remix-lib#readme",
"typings": "src/index.d.ts",
"gitHead": "57f4196f41faf8a2879a8013327f5e275e623444",
"gitHead": "28b25d08084ff0dd952f2cd649dbcd1c1ab30e01",
"types": "./src/index.d.ts"
}

@ -1,6 +1,7 @@
'use strict'
import { ethers } from 'ethers'
import { getFunctionFragment } from './txHelper'
import { Transaction } from './txRunner'
/**
* deploy the given contract
@ -16,11 +17,11 @@ import { getFunctionFragment } from './txHelper'
* [personal mode enabled, need password to continue] promptCb (okCb, cancelCb)
* @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) {
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) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
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)
* @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 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) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
finalCallback(error, txResult)

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

@ -69,14 +69,28 @@ export class TxRunnerVM {
}
try {
this.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback)
this.runInVm(args, callback)
} catch (e) {
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
try {
const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle.
let tx
if (signed) {
if (!EIP1559) {
tx = LegacyTransaction.fromSerializedTx(hexToBytes(data), { common: this.commonContext })
} else {
tx = FeeMarketEIP1559Transaction.fromSerializedTx(hexToBytes(data), { common: this.commonContext })
}
}
else {
if (!from && useCall && Object.keys(this.vmaccounts).length) {
from = Object.keys(this.vmaccounts)[0]
account = this.vmaccounts[from]
@ -86,10 +100,7 @@ export class TxRunnerVM {
return callback('Invalid account selected')
}
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.
let tx
if (!EIP1559) {
tx = LegacyTransaction.fromTxData({
nonce: useCall ? this.nextNonceForCall : res.nonce,
@ -110,6 +121,8 @@ export class TxRunnerVM {
data: hexToBytes(data)
}).sign(account.privateKey)
}
}
if (useCall) this.nextNonceForCall++
const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e']

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

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

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

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

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

@ -63,6 +63,7 @@ export class Transactions {
methods () {
return {
eth_sendTransaction: this.eth_sendTransaction.bind(this),
eth_sendRawTransaction: this.eth_sendRawTransaction.bind(this),
eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this),
eth_getCode: this.eth_getCode.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) {
// from might be lowercased address (web3)
if (payload.params && payload.params.length > 0 && payload.params[0].from) {

@ -1,7 +1,7 @@
import { execution } from '@remix-project/remix-lib'
import { execution, Transaction } from '@remix-project/remix-lib'
const TxExecution = execution.txExecution
function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) {
function runCall ({ from, to, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, callback) {
const finalCallback = function (err, result) {
if (err) {
return callback(err)
@ -9,10 +9,10 @@ function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks,
return callback(null, result)
}
TxExecution.callFunction(from, to, data, value, gasLimit, { constant: true }, txRunner, callbacks, finalCallback)
TxExecution.callFunction({ from, to, data, value, gasLimit, signed }, { constant: true }, txRunner, callbacks, finalCallback)
}
function runTx (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) {
function runTx ({ from, to, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, callback) {
const finalCallback = function (err, result) {
if (err) {
return callback(err)
@ -20,10 +20,10 @@ function runTx (payload, from, to, data, value, gasLimit, txRunner, callbacks, c
callback(null, result)
}
TxExecution.callFunction(from, to, data, value, gasLimit, { constant: false }, txRunner, callbacks, finalCallback)
TxExecution.callFunction({ from, to, data, value, gasLimit, signed }, { constant: false }, txRunner, callbacks, finalCallback)
}
function createContract (payload, from, data, value, gasLimit, txRunner, callbacks, callback) {
function createContract ({ from, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, callback) {
const finalCallback = function (err, result) {
if (err) {
return callback(err)
@ -31,11 +31,11 @@ function createContract (payload, from, data, value, gasLimit, txRunner, callbac
callback(null, result)
}
TxExecution.createContract(from, data, value, gasLimit, txRunner, callbacks, finalCallback)
TxExecution.createContract({ from, data, value, gasLimit, signed }, txRunner, callbacks, finalCallback)
}
export function processTx (txRunnerInstance, payload, isCall, callback) {
let { from, to, data, input, value, gas } = payload.params[0] // eslint-disable-line
let { from, to, data, input, value, gas, signed } = payload.params[0] // eslint-disable-line
gas = gas || 3000000
const callbacks = {
@ -54,10 +54,10 @@ export function processTx (txRunnerInstance, payload, isCall, callback) {
}
if (isCall) {
runCall(payload, from, to, data||input, value, gas, txRunnerInstance, callbacks, callback)
runCall({ from, to, data: data||input, value, gasLimit: gas, signed }, txRunnerInstance, callbacks, callback)
} else if (to) {
runTx(payload, from, to, data||input, value, gas, txRunnerInstance, callbacks, callback)
runTx({ from, to, data: data||input, value, gasLimit: gas, signed }, txRunnerInstance, callbacks, callback)
} else {
createContract(payload, from, data||input, value, gas, txRunnerInstance, callbacks, callback)
createContract({ from, to: undefined, data: data||input, value, gasLimit: gas, signed }, txRunnerInstance, callbacks, callback)
}
}

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

Loading…
Cancel
Save