Add settings for vm-state

pull/4529/head
ioedeveloper 9 months ago
parent e9a7f0ba8b
commit d2d94f1e81
  1. 3
      apps/remix-ide/src/app/tabs/locales/en/settings.json
  2. 3
      apps/remix-ide/src/app/tabs/locales/es/settings.json
  3. 3
      apps/remix-ide/src/app/tabs/locales/fr/settings.json
  4. 3
      apps/remix-ide/src/app/tabs/locales/it/settings.json
  5. 3
      apps/remix-ide/src/app/tabs/locales/zh/settings.json
  6. 5
      apps/remix-ide/src/blockchain/blockchain.tsx
  7. 4
      apps/remix-ide/src/blockchain/providers/injected.ts
  8. 4
      apps/remix-ide/src/blockchain/providers/node.ts
  9. 155
      apps/remix-ide/src/blockchain/providers/vm.ts
  10. 2
      libs/remix-lib/src/execution/txRunnerVM.ts
  11. 1
      libs/remix-ui/run-tab/src/lib/types/blockchain.d.ts
  12. 1
      libs/remix-ui/run-tab/src/lib/types/injected.d.ts
  13. 1
      libs/remix-ui/run-tab/src/lib/types/node.d.ts
  14. 1
      libs/remix-ui/run-tab/src/lib/types/vm.d.ts
  15. 24
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  16. 5
      libs/remix-ui/settings/src/lib/settingsAction.ts
  17. 18
      libs/remix-ui/settings/src/lib/settingsReducer.ts

@ -43,5 +43,6 @@
"settings.copilot": "Solidity copilot - Alpha", "settings.copilot": "Solidity copilot - Alpha",
"settings.copilot.activate": "Load & Activate copilot", "settings.copilot.activate": "Load & Activate copilot",
"settings.copilot.max_new_tokens": "Maximum number of words to generate", "settings.copilot.max_new_tokens": "Maximum number of words to generate",
"settings.copilot.temperature": "Temperature" "settings.copilot.temperature": "Temperature",
"settings.enableSaveEnvState": "Save environment state"
} }

@ -36,5 +36,6 @@
"settings.port": "PUERTO", "settings.port": "PUERTO",
"settings.projectID": "ID DEL PROYECTO", "settings.projectID": "ID DEL PROYECTO",
"settings.projectSecret": "SECRETO DE PROYECTO", "settings.projectSecret": "SECRETO DE PROYECTO",
"settings.analyticsInRemix": "Analíticas en IDE Remix" "settings.analyticsInRemix": "Analíticas en IDE Remix",
"settings.enableSaveEnvState": "Guardar el estado del entorno de implementación"
} }

@ -36,5 +36,6 @@
"settings.port": "PORT", "settings.port": "PORT",
"settings.projectID": "ID du projet", "settings.projectID": "ID du projet",
"settings.projectSecret": "SECRET DU PROJET", "settings.projectSecret": "SECRET DU PROJET",
"settings.analyticsInRemix": "Analytics dans l'IDE de Remix" "settings.analyticsInRemix": "Analytics dans l'IDE de Remix",
"settings.enableSaveEnvState": "Enregistrer l'état de l'environnement de déploiement"
} }

@ -36,5 +36,6 @@
"settings.port": "PORTA", "settings.port": "PORTA",
"settings.projectID": "ID PROGETTO", "settings.projectID": "ID PROGETTO",
"settings.projectSecret": "SEGRETO DEL PROGETTO", "settings.projectSecret": "SEGRETO DEL PROGETTO",
"settings.analyticsInRemix": "Analytics nella Remix IDE" "settings.analyticsInRemix": "Analytics nella Remix IDE",
"settings.enableSaveEnvState": "Salva lo stato dell'ambiente di distribuzione"
} }

@ -36,5 +36,6 @@
"settings.port": "端口", "settings.port": "端口",
"settings.projectID": "项目 ID", "settings.projectID": "项目 ID",
"settings.projectSecret": "项目密钥", "settings.projectSecret": "项目密钥",
"settings.analyticsInRemix": "Remix IDE 中的分析功能" "settings.analyticsInRemix": "Remix IDE 中的分析功能",
"settings.enableSaveEnvState": "保存部署环境状态"
} }

@ -646,11 +646,12 @@ export class Blockchain extends Plugin {
} }
async loadContext(context: string) { async loadContext(context: string) {
const contextExists = await this.call('fileManager', 'exists', '.context') const contextExists = await this.call('fileManager', 'exists', `.states/${context}/state.json`)
if (contextExists) { if (contextExists) {
const stateDb = await this.call('fileManager', 'readFile', `.states/${context}/state.json`) const stateDb = await this.call('fileManager', 'readFile', `.states/${context}/state.json`)
await this.getCurrentProvider().loadContext(stateDb) await this.getCurrentProvider().resetEnvironment(stateDb)
} else { } else {
await this.getCurrentProvider().resetEnvironment() await this.getCurrentProvider().resetEnvironment()
} }

@ -27,10 +27,6 @@ export class InjectedProvider {
/* Do nothing. */ /* Do nothing. */
} }
async loadContext (context) {
/* Do nothing. */
}
async getBalanceInEther (address) { async getBalanceInEther (address) {
const balance = await this.executionContext.web3().eth.getBalance(address) const balance = await this.executionContext.web3().eth.getBalance(address)
const balInString = balance.toString(10) const balInString = balance.toString(10)

@ -33,10 +33,6 @@ export class NodeProvider {
/* Do nothing. */ /* Do nothing. */
} }
async loadContext (context) {
/* Do nothing. */
}
async getBalanceInEther (address) { async getBalanceInEther (address) {
const balance = await this.executionContext.web3().eth.getBalance(address) const balance = await this.executionContext.web3().eth.getBalance(address)
const balInString = balance.toString(10) const balInString = balance.toString(10)

@ -29,45 +29,64 @@ export class VMProvider {
}) })
} }
async resetEnvironment () { async resetEnvironment (stringifiedStateDb?: string) {
if (this.worker) { if (this.worker) this.worker.terminate()
const provider = this.executionContext.getProviderObject() this.worker = new Worker(new URL('./worker-vm', import.meta.url))
const provider = this.executionContext.getProviderObject()
this.worker.postMessage({ let incr = 0
cmd: 'init', const stamps = {}
fork: this.executionContext.getCurrentFork(),
nodeUrl: provider?.options['nodeUrl'],
blockNumber: provider?.options['blockNumber']
})
} else {
this.worker = new Worker(new URL('./worker-vm', import.meta.url))
this.setWorkerEventListeners(this.worker)
const provider = this.executionContext.getProviderObject()
return new Promise((resolve, reject) => {
this.worker.addEventListener('message', (msg) => {
if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) {
if (stamps[msg.data.stamp].callback) {
stamps[msg.data.stamp].callback(msg.data.error, msg.data.result)
return
}
if (msg.data.error) {
stamps[msg.data.stamp].reject(msg.data.error)
} else {
stamps[msg.data.stamp].resolve(msg.data.result)
}
} else if (msg.data.cmd === 'initiateResult') {
if (!msg.data.error) {
this.provider = {
sendAsync: (query, callback) => {
return new Promise((resolve, reject) => {
const stamp = Date.now() + incr
incr++
stamps[stamp] = { callback, resolve, reject }
this.worker.postMessage({ cmd: 'sendAsync', query, stamp })
})
}
}
this.web3 = new Web3(this.provider as LegacySendAsyncProvider)
this.web3.setConfig({ defaultTransactionType: '0x0' })
extend(this.web3)
this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3)
resolve({})
} else {
reject(new Error(msg.data.error))
}
} else if (msg.data.cmd === 'newAccountResult') {
if (this.newAccountCallback[msg.data.stamp]) {
this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result)
delete this.newAccountCallback[msg.data.stamp]
}
}
})
this.worker.postMessage({ this.worker.postMessage({
cmd: 'init', cmd: 'init',
fork: this.executionContext.getCurrentFork(), fork: this.executionContext.getCurrentFork(),
nodeUrl: provider?.options['nodeUrl'], nodeUrl: provider?.options['nodeUrl'],
blockNumber: provider?.options['blockNumber'] blockNumber: provider?.options['blockNumber'],
stateDb: stringifiedStateDb
}) })
}
}
async loadContext (stringifiedStateDb: string) {
if (this.worker) this.worker.terminate()
this.worker = new Worker(new URL('./worker-vm', import.meta.url))
this.setWorkerEventListeners(this.worker)
const provider = this.executionContext.getProviderObject()
this.worker.postMessage({
cmd: 'init',
fork: this.executionContext.getCurrentFork(),
nodeUrl: provider?.options['nodeUrl'],
blockNumber: provider?.options['blockNumber'],
stateDb: stringifiedStateDb
}) })
} }
// TODO: is still here because of the plugin API // TODO: is still here because of the plugin API
// can be removed later when we update the API // can be removed later when we update the API
createVMAccount (newAccount) { createVMAccount (newAccount) {
@ -105,48 +124,50 @@ export class VMProvider {
return this.executionContext.getProvider() return this.executionContext.getProvider()
} }
private setWorkerEventListeners (worker: Worker) { private async setWorkerEventListeners () {
if (!worker) throw new Error('Worker not initialized') return new Promise((resolve, reject) => {
let incr = 0 if (!this.worker) throw new Error('Worker not initialized')
const stamps = {} let incr = 0
const stamps = {}
worker.addEventListener('message', (msg) => { this.worker.addEventListener('message', (msg) => {
if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) { if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) {
if (stamps[msg.data.stamp].callback) { if (stamps[msg.data.stamp].callback) {
stamps[msg.data.stamp].callback(msg.data.error, msg.data.result) stamps[msg.data.stamp].callback(msg.data.error, msg.data.result)
return return
} }
if (msg.data.error) { if (msg.data.error) {
stamps[msg.data.stamp].reject(msg.data.error) stamps[msg.data.stamp].reject(msg.data.error)
} else { } else {
stamps[msg.data.stamp].resolve(msg.data.result) stamps[msg.data.stamp].resolve(msg.data.result)
} }
} else if (msg.data.cmd === 'initiateResult') { } else if (msg.data.cmd === 'initiateResult') {
if (!msg.data.error) { if (!msg.data.error) {
this.provider = { this.provider = {
sendAsync: (query, callback) => { sendAsync: (query, callback) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const stamp = Date.now() + incr const stamp = Date.now() + incr
incr++ incr++
stamps[stamp] = { callback, resolve, reject } stamps[stamp] = { callback, resolve, reject }
worker.postMessage({ cmd: 'sendAsync', query, stamp }) this.worker.postMessage({ cmd: 'sendAsync', query, stamp })
}) })
}
} }
this.web3 = new Web3(this.provider as LegacySendAsyncProvider)
this.web3.setConfig({ defaultTransactionType: '0x0' })
extend(this.web3)
this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3)
resolve({})
} else {
reject(new Error(msg.data.error))
}
} else if (msg.data.cmd === 'newAccountResult') {
if (this.newAccountCallback[msg.data.stamp]) {
this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result)
delete this.newAccountCallback[msg.data.stamp]
} }
this.web3 = new Web3(this.provider as LegacySendAsyncProvider)
this.web3.setConfig({ defaultTransactionType: '0x0' })
extend(this.web3)
this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3)
} else {
console.error(msg.data.error)
throw new Error(msg.data.error)
}
} else if (msg.data.cmd === 'newAccountResult') {
if (this.newAccountCallback[msg.data.stamp]) {
this.newAccountCallback[msg.data.stamp](msg.data.error, msg.data.result)
delete this.newAccountCallback[msg.data.stamp]
} }
} })
}) })
} }
} }

@ -125,7 +125,7 @@ export class TxRunnerVM {
this.blockNumber = this.blockNumber + 1 this.blockNumber = this.blockNumber + 1
this.blockParentHash = block.hash() this.blockParentHash = block.hash()
this.runBlockInVm(tx, block, (err, result) => { this.runBlockInVm(tx, block, (err, result) => {
if (!err) this.getVMObject().vm.blockchain.putBlock(block) if (!err) this.getVMObject().vm.blockchain.putBlock(block) // look at putBlock for saving blocks
callback(err, result) callback(err, result)
}) })
} else { } else {

@ -61,7 +61,6 @@ export class Blockchain extends Plugin<any, any> {
/** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */ /** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */
startListening(txlistener: any): void; startListening(txlistener: any): void;
resetEnvironment(): Promise<void>; resetEnvironment(): Promise<void>;
loadContext(): Promise<void>;
/** /**
* Create a VM Account * Create a VM Account
* @param {{privateKey: string, balance: string}} newAccount The new account to create * @param {{privateKey: string, balance: string}} newAccount The new account to create

@ -5,7 +5,6 @@ declare class InjectedProvider {
getAccounts(cb: any): any; getAccounts(cb: any): any;
newAccount(passwordPromptCb: any, cb: any): void; newAccount(passwordPromptCb: any, cb: any): void;
resetEnvironment(): Promise<void>; resetEnvironment(): Promise<void>;
loadContext(context: any): Promise<void>;
getBalanceInEther(address: any): Promise<string>; getBalanceInEther(address: any): Promise<string>;
getGasPrice(cb: any): void; getGasPrice(cb: any): void;
signMessage(message: any, account: any, _passphrase: any, cb: any): void; signMessage(message: any, account: any, _passphrase: any, cb: any): void;

@ -6,7 +6,6 @@ declare class NodeProvider {
getAccounts(cb: any): any; getAccounts(cb: any): any;
newAccount(passwordPromptCb: any, cb: any): any; newAccount(passwordPromptCb: any, cb: any): any;
resetEnvironment(): Promise<void>; resetEnvironment(): Promise<void>;
loadContext(context: any): Promise<void>;
getBalanceInEther(address: any): Promise<string>; getBalanceInEther(address: any): Promise<string>;
getGasPrice(cb: any): void; getGasPrice(cb: any): void;
signMessage(message: any, account: any, passphrase: any, cb: any): void; signMessage(message: any, account: any, passphrase: any, cb: any): void;

@ -4,7 +4,6 @@ declare class VMProvider {
executionContext: any; executionContext: any;
getAccounts(cb: any): void; getAccounts(cb: any): void;
resetEnvironment(): Promise<void>; resetEnvironment(): Promise<void>;
loadEnvironment(stringifiedStateDb: string): Promise<void>;
accounts: any; accounts: any;
RemixSimulatorProvider: any; RemixSimulatorProvider: any;
web3: any; web3: any;

@ -19,7 +19,8 @@ import {
saveIpfsSettingsToast, saveIpfsSettingsToast,
useAutoCompletion, useAutoCompletion,
useShowGasInEditor, useShowGasInEditor,
useDisplayErrors useDisplayErrors,
saveEnvState
} from './settingsAction' } from './settingsAction'
import {initialState, toastInitialState, toastReducer, settingReducer} from './settingsReducer' import {initialState, toastInitialState, toastReducer, settingReducer} from './settingsReducer'
import {Toaster} from '@remix-ui/toaster' // eslint-disable-line import {Toaster} from '@remix-ui/toaster' // eslint-disable-line
@ -69,6 +70,9 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const useShowGas = props.config.get('settings/show-gas') const useShowGas = props.config.get('settings/show-gas')
if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, true, dispatch) if (useShowGas === null || useShowGas === undefined) useShowGasInEditor(props.config, true, dispatch)
const enableSaveEnvState = props.config.get('settings/save-evm-state')
if (enableSaveEnvState === null || enableSaveEnvState === undefined) saveEnvState(props.config, true, dispatch)
} }
useEffect(() => initValue(), [resetState, props.config]) useEffect(() => initValue(), [resetState, props.config])
useEffect(() => initValue(), []) useEffect(() => initValue(), [])
@ -200,6 +204,11 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
useDisplayErrors(props.config, event.target.checked, dispatch) useDisplayErrors(props.config, event.target.checked, dispatch)
} }
const onchangeSaveEnvState= (event) => {
console.log('saveEnvState', event.target.checked)
saveEnvState(props.config, event.target.checked, dispatch)
}
const getTextClass = (key) => { const getTextClass = (key) => {
if (props.config.get(key)) { if (props.config.get(key)) {
return textDark return textDark
@ -217,6 +226,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const isAutoCompleteChecked = props.config.get('settings/auto-completion') || false const isAutoCompleteChecked = props.config.get('settings/auto-completion') || false
const isShowGasInEditorChecked = props.config.get('settings/show-gas') || false const isShowGasInEditorChecked = props.config.get('settings/show-gas') || false
const displayErrorsChecked = props.config.get('settings/display-errors') || false const displayErrorsChecked = props.config.get('settings/display-errors') || false
const isSaveEnvStateChecked = props.config.get('settings/save-env-state') || false
return ( return (
<div className="$border-top"> <div className="$border-top">
<div className="d-flex justify-content-end pr-4"> <div className="d-flex justify-content-end pr-4">
@ -333,6 +343,18 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
</a> </a>
</label> </label>
</div> </div>
<div className="custom-control custom-checkbox mb-1">
<input onChange={onchangeSaveEnvState} id="settingsEnableSaveEnvState" type="checkbox" className="custom-control-input" checked={isSaveEnvStateChecked} />
<label
className={`form-check-label custom-control-label align-middle ${getTextClass('settings/save-env-state')}`}
data-id="settingsEnableSaveEnvStateLabel"
htmlFor="settingsEnableSaveEnvState"
>
<span>
<FormattedMessage id="settings.enableSaveEnvState" />
</span>
</label>
</div>
</div> </div>
</div> </div>
) )

@ -90,3 +90,8 @@ export const saveIpfsSettingsToast = (config, dispatch, ipfsURL, ipfsProtocol, i
config.set('settings/ipfs-project-secret', ipfsProjectSecret) config.set('settings/ipfs-project-secret', ipfsProjectSecret)
dispatch({ type: 'save', payload: { message: 'IPFS settings have been saved' } }) dispatch({ type: 'save', payload: { message: 'IPFS settings have been saved' } })
} }
export const saveEnvState = (config, checked, dispatch) => {
config.set('settings/save-env-state', checked)
dispatch({ type: 'save-env-state', payload: { isChecked: checked, textClass: checked ? textDark : textSecondary } })
}

@ -56,7 +56,12 @@ export const initialState = {
name: 'copilot/suggest/temperature', name: 'copilot/suggest/temperature',
value: 0.5, value: 0.5,
textClass: textSecondary textClass: textSecondary
} },
{
name: 'save-env-state',
isChecked: false,
textClass: textSecondary
},
] ]
} }
@ -173,6 +178,17 @@ export const settingReducer = (state, action) => {
return { return {
...state ...state
} }
case 'save-env-state':
state.elementState.map(element => {
if (element.name === 'save-env-state') {
element.isChecked = action.payload.isChecked
element.textClass = action.payload.textClass
}
})
return {
...state
}
default: default:
return initialState return initialState
} }

Loading…
Cancel
Save