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.activate": "Load & Activate copilot",
"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.projectID": "ID DEL 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.projectID": "ID 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.projectID": "ID 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.projectID": "项目 ID",
"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) {
const contextExists = await this.call('fileManager', 'exists', '.context')
const contextExists = await this.call('fileManager', 'exists', `.states/${context}/state.json`)
if (contextExists) {
const stateDb = await this.call('fileManager', 'readFile', `.states/${context}/state.json`)
await this.getCurrentProvider().loadContext(stateDb)
await this.getCurrentProvider().resetEnvironment(stateDb)
} else {
await this.getCurrentProvider().resetEnvironment()
}

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

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

@ -29,45 +29,64 @@ export class VMProvider {
})
}
async resetEnvironment () {
if (this.worker) {
const provider = this.executionContext.getProviderObject()
async resetEnvironment (stringifiedStateDb?: string) {
if (this.worker) this.worker.terminate()
this.worker = new Worker(new URL('./worker-vm', import.meta.url))
const provider = this.executionContext.getProviderObject()
this.worker.postMessage({
cmd: 'init',
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()
let incr = 0
const stamps = {}
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({
cmd: 'init',
fork: this.executionContext.getCurrentFork(),
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
// can be removed later when we update the API
createVMAccount (newAccount) {
@ -105,48 +124,50 @@ export class VMProvider {
return this.executionContext.getProvider()
}
private setWorkerEventListeners (worker: Worker) {
if (!worker) throw new Error('Worker not initialized')
let incr = 0
const stamps = {}
private async setWorkerEventListeners () {
return new Promise((resolve, reject) => {
if (!this.worker) throw new Error('Worker not initialized')
let incr = 0
const stamps = {}
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 }
worker.postMessage({ cmd: 'sendAsync', query, stamp })
})
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.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.blockParentHash = block.hash()
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)
})
} 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) */
startListening(txlistener: any): void;
resetEnvironment(): Promise<void>;
loadContext(): Promise<void>;
/**
* Create a VM Account
* @param {{privateKey: string, balance: string}} newAccount The new account to create

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

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

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

@ -19,7 +19,8 @@ import {
saveIpfsSettingsToast,
useAutoCompletion,
useShowGasInEditor,
useDisplayErrors
useDisplayErrors,
saveEnvState
} from './settingsAction'
import {initialState, toastInitialState, toastReducer, settingReducer} from './settingsReducer'
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')
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(), [])
@ -200,6 +204,11 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
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) => {
if (props.config.get(key)) {
return textDark
@ -217,6 +226,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const isAutoCompleteChecked = props.config.get('settings/auto-completion') || false
const isShowGasInEditorChecked = props.config.get('settings/show-gas') || false
const displayErrorsChecked = props.config.get('settings/display-errors') || false
const isSaveEnvStateChecked = props.config.get('settings/save-env-state') || false
return (
<div className="$border-top">
<div className="d-flex justify-content-end pr-4">
@ -333,6 +343,18 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
</a>
</label>
</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>
)

@ -90,3 +90,8 @@ export const saveIpfsSettingsToast = (config, dispatch, ipfsURL, ipfsProtocol, i
config.set('settings/ipfs-project-secret', ipfsProjectSecret)
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',
value: 0.5,
textClass: textSecondary
}
},
{
name: 'save-env-state',
isChecked: false,
textClass: textSecondary
},
]
}
@ -173,6 +178,17 @@ export const settingReducer = (state, action) => {
return {
...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:
return initialState
}

Loading…
Cancel
Save