From 2459405964be246b410ca174d82d6eca8afd9667 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Sun, 5 Jan 2025 19:50:11 +0700 Subject: [PATCH] Add environmental values for interacting with contracts --- .../src/app/AppContext.tsx | 4 +- .../src/app/actions/index.ts | 31 +++- .../src/app/components/NavMenu.tsx | 3 +- .../src/app/reducers/state.ts | 86 +++++++++--- apps/contract-interaction/src/app/routes.tsx | 15 +- .../{LookupABIView.tsx => GetABIView.tsx} | 20 +-- .../src/app/views/InteractView.tsx | 132 ++++++++++++++++++ .../src/app/views/index.ts | 5 +- .../run-tab/src/lib/components/settingsUI.tsx | 5 + 9 files changed, 255 insertions(+), 46 deletions(-) rename apps/contract-interaction/src/app/views/{LookupABIView.tsx => GetABIView.tsx} (95%) create mode 100644 apps/contract-interaction/src/app/views/InteractView.tsx diff --git a/apps/contract-interaction/src/app/AppContext.tsx b/apps/contract-interaction/src/app/AppContext.tsx index 7ad042cc26..d796bee7e2 100644 --- a/apps/contract-interaction/src/app/AppContext.tsx +++ b/apps/contract-interaction/src/app/AppContext.tsx @@ -1,7 +1,7 @@ import React from 'react' import type { ThemeType, Chain, ContractInteractionSettings } from './types' import { ContractInteractionPluginClient } from './ContractInteractionPluginClient' -import { State } from './reducers/state' +import { appInitialState, State } from './reducers/state' // Define the type for the context type AppContextType = { @@ -20,7 +20,7 @@ const defaultContextValue: AppContextType = { // setThemeType: (themeType: ThemeType) => { }, plugin: {} as ContractInteractionPluginClient, settings: { chains: {} }, - appState: { loading: { screen: true }, contractInstances: [] }, + appState: appInitialState, setSettings: () => { }, chains: [], } diff --git a/apps/contract-interaction/src/app/actions/index.ts b/apps/contract-interaction/src/app/actions/index.ts index 07dad0dea5..a8ae5bc59e 100644 --- a/apps/contract-interaction/src/app/actions/index.ts +++ b/apps/contract-interaction/src/app/actions/index.ts @@ -1,7 +1,7 @@ import { PluginClient } from '@remixproject/plugin'; import ContractInteractionPluginClient from '../ContractInteractionPluginClient'; -import { CLEAR_INSTANCES, PIN_INSTANCE, REMOVE_INSTANCE, SET_INSTANCE, UNPIN_INSTANCE } from "../reducers/state" +import { CLEAR_INSTANCES, PIN_INSTANCE, REMOVE_INSTANCE, SET_GAS_LIMIT, SET_INSTANCE, SET_SELECTED_ACCOUNT, SET_SEND_UNIT, SET_SEND_VALUE, UNPIN_INSTANCE } from "../reducers/state" import { Chain, ContractInstance } from '../types/AbiProviderTypes'; let dispatch: React.Dispatch @@ -96,3 +96,32 @@ export const clearInstancesAction = () => { type: CLEAR_INSTANCES }) } + +export const setSelectedAccountAction = (selectedAccount: string) => { + dispatch({ + type: SET_SELECTED_ACCOUNT, + payload: selectedAccount + }) +} + +export const setSendValue = (sendValue: string) => { + dispatch({ + type: SET_SEND_VALUE, + payload: sendValue + }) +} + +export const setSendUnit = (sendUnit: string) => { + dispatch({ + type: SET_SEND_UNIT, + payload: sendUnit + }) +} + +export const setGasLimit = (gasLimit: number) => { + dispatch({ + type: SET_GAS_LIMIT, + payload: gasLimit + }) +} + diff --git a/apps/contract-interaction/src/app/components/NavMenu.tsx b/apps/contract-interaction/src/app/components/NavMenu.tsx index 3a84fba21d..288654783d 100644 --- a/apps/contract-interaction/src/app/components/NavMenu.tsx +++ b/apps/contract-interaction/src/app/components/NavMenu.tsx @@ -21,7 +21,8 @@ const NavItem: React.FC = ({ to, icon, title }) => { export const NavMenu = () => { return ( diff --git a/apps/contract-interaction/src/app/reducers/state.ts b/apps/contract-interaction/src/app/reducers/state.ts index 801131b9d1..3621845ec6 100644 --- a/apps/contract-interaction/src/app/reducers/state.ts +++ b/apps/contract-interaction/src/app/reducers/state.ts @@ -2,29 +2,54 @@ import { ContractInstance } from "../types"; export const appInitialState: State = { loading: { screen: true }, - contractInstances: [] - // settings: { - // sendValue: '0', - // sendUnit: 'wei', - // gasLimit: 3000000, - // networkName: 'Goerli', - // loadedAccounts: {}, - // isRequesting: false, - // isSuccessful: false, - // error: null, - // selectedAccount: '',s - // provider: window.ethereum ? 'metamask' : 'walletconnect', - // theme: 'Dark', - // }, + contractInstances: [], + interaction_environment: { + sendValue: '0', + sendUnit: 'wei', + gasLimit: 3000000, + proxyAddress: '', + error: '', + selectedAccount: '', + // TODO + accounts: { + loadedAccounts: {}, + isRequesting: false, + isSuccessful: false, + error: null, + selectedAccount: '' + }, + }, + // isRequesting: false, + // isSuccessful: false, // terminal: { journalBlocks: [], hidden: false, height: 250 }, }; export interface State { loading: { screen: boolean }, contractInstances: ContractInstance[] + interaction_environment: InteractionEnvironment }; -export type ActionType = 'SET_LOADING' | 'SET_INSTANCE' | 'SET_SETTINGS' | 'SET_TERMINAL' | 'PIN_INSTANCE' | 'UNPIN_INSTANCE' | 'REMOVE_INSTANCE' | 'CLEAR_INSTANCES'; +export interface InteractionEnvironment { + sendValue: string, + sendUnit: string, + gasLimit: number, + proxyAddress: string, + error: string | null, + selectedAccount: string, + // TODO + accounts: { + loadedAccounts: {}, + isRequesting: boolean, + isSuccessful: boolean, + error: string | null, + selectedAccount: string + }, + // isRequesting: boolean, + // isSuccessful: boolean, +} + +export type ActionType = 'SET_LOADING' | 'SET_INSTANCE' | 'SET_SETTINGS' | 'SET_TERMINAL' | 'PIN_INSTANCE' | 'UNPIN_INSTANCE' | 'REMOVE_INSTANCE' | 'CLEAR_INSTANCES' | 'SET_SELECTED_ACCOUNT' | 'SET_SEND_VALUE' | 'SET_SEND_UNIT' | 'SET_GAS_LIMIT'; export const SET_LOADING: ActionType = 'SET_LOADING'; export const SET_INSTANCE: ActionType = 'SET_INSTANCE'; @@ -34,6 +59,10 @@ export const PIN_INSTANCE: ActionType = 'PIN_INSTANCE'; export const UNPIN_INSTANCE: ActionType = 'UNPIN_INSTANCE'; export const REMOVE_INSTANCE: ActionType = 'REMOVE_INSTANCE'; export const CLEAR_INSTANCES: ActionType = 'CLEAR_INSTANCES'; +export const SET_SELECTED_ACCOUNT: ActionType = 'SET_SELECTED_ACCOUNT'; +export const SET_SEND_VALUE: ActionType = 'SET_SEND_VALUE'; +export const SET_SEND_UNIT: ActionType = 'SET_SEND_UNIT'; +export const SET_GAS_LIMIT: ActionType = 'SET_GAS_LIMIT'; export type Action = { @@ -93,9 +122,32 @@ export const appReducer = (state = appInitialState, action: Action): State => { } } + case SET_SELECTED_ACCOUNT: + return { + ...state, + interaction_environment: { ...state.interaction_environment, selectedAccount: action.payload }, + }; + + case SET_SEND_VALUE: + return { + ...state, + interaction_environment: { ...state.interaction_environment, sendValue: action.payload }, + }; + + case SET_SEND_UNIT: + return { + ...state, + interaction_environment: { ...state.interaction_environment, sendUnit: action.payload } + }; + + case SET_GAS_LIMIT: + return { + ...state, + interaction_environment: { ...state.interaction_environment, gasLimit: action.payload }, + }; + // case UPDATE_INSTANCES_BALANCE: { // const payload: Array<{ contractData: ContractData, address: string, balance: number, name: string, abi?: any, decodedResponse?: Record }> = action.payload - // return { // ...state, // instances: { @@ -105,8 +157,6 @@ export const appReducer = (state = appInitialState, action: Action): State => { // } // } - - // case 'SET_SETTINGS': // return { // ...state, diff --git a/apps/contract-interaction/src/app/routes.tsx b/apps/contract-interaction/src/app/routes.tsx index 335863ff9b..54ffe35b4d 100644 --- a/apps/contract-interaction/src/app/routes.tsx +++ b/apps/contract-interaction/src/app/routes.tsx @@ -1,6 +1,6 @@ import { HashRouter as Router, Route, Routes } from 'react-router-dom' -import { LookupABIView, SettingsView } from './views' +import { GetABIView, InteractView, SettingsView } from './views' import { DefaultLayout } from './layouts' const DisplayRoutes = () => ( @@ -10,8 +10,17 @@ const DisplayRoutes = () => ( - + + + + } + /> + + + } /> diff --git a/apps/contract-interaction/src/app/views/LookupABIView.tsx b/apps/contract-interaction/src/app/views/GetABIView.tsx similarity index 95% rename from apps/contract-interaction/src/app/views/LookupABIView.tsx rename to apps/contract-interaction/src/app/views/GetABIView.tsx index 2a2ee7e506..a02fc7c1fc 100644 --- a/apps/contract-interaction/src/app/views/LookupABIView.tsx +++ b/apps/contract-interaction/src/app/views/GetABIView.tsx @@ -13,7 +13,7 @@ import { InstanceContainerUI } from '../components/InstanceContainerUI' import { clearInstancesAction, loadPinnedContractsAction, setInstanceAction } from '../actions' import { FormattedMessage } from 'react-intl' -export const LookupABIView = () => { +export const GetABIView = () => { const { appState, settings, plugin } = useContext(AppContext); const contractInstances = appState.contractInstances; @@ -27,7 +27,6 @@ export const LookupABIView = () => { const navigate = useNavigate() const chainSettings = useMemo(() => (selectedChain ? mergeChainSettingsWithDefaults(selectedChain.chainId.toString(), settings) : undefined), [selectedChain, settings]) - const [toggleExpander, setToggleExpander] = useState(false) const [deriveFromContractAddress, setDeriveFromContractAddress] = useState(true) const sourcifySupported = useSourcifySupported(selectedChain, chainSettings) @@ -223,10 +222,6 @@ export const LookupABIView = () => { } } - const toggleConfigurations = () => { - setToggleExpander(!toggleExpander) - } - return ( <>
@@ -395,19 +390,6 @@ export const LookupABIView = () => { })} -
-
- -
-
- - - -
-
- {contractInstances.length > 0 && ( { + const { appState, plugin } = useContext(AppContext); + const contractInstances = appState.contractInstances; + const environment = appState.interaction_environment; + + const { selectedChain, setSelectedChain } = useContext(InteractionFormContext) + + const handleSelectedChain = async (newSelectedChain: Chain) => { + setSelectedChain(newSelectedChain) + + // Clear all contract interfaces for the old chain. + await clearInstancesAction(); + + // Load pinned contracts for the new chain. + await loadPinnedContractsAction(plugin, newSelectedChain); + } + + const [modals, setModals] = useState([]) + const modal = ( + title: string, + message: string | JSX.Element, + okLabel: string, + okFn: () => void, + cancelLabel?: string, + cancelFn?: () => void, + okBtnClass?: string, + cancelBtnClass?: string + ) => { + setModals((modals) => { + modals.push({ + message, + title, + okLabel, + okFn, + cancelLabel, + cancelFn, + okBtnClass, + cancelBtnClass + }) + return [...modals] + }) + } + + const [toasters, setToasters] = useState([]) + + const toast = (toasterMsg: string) => { + setToasters((messages) => { + messages.push(toasterMsg) + return [...messages] + }) + } + + return ( + <> + {/* INTERACTION_ENVIRONMENT */} + + + { console.error('not supported in this plugin') }} + personalMode={false} + selectExEnv={'blockchain'} + accounts={environment.accounts} + setAccount={setSelectedAccountAction} + createNewBlockchainAccount={() => { console.error('not supported in this plugin') }} + setPassphrase={() => { console.error('not supported in this plugin') }} + setMatchPassphrase={() => { console.error('not supported in this plugin') }} + tooltip={toast} + modal={modal} + signMessageWithAddress={() => { console.error('not supported in this plugin') }} + passphrase={''} + /> + + + + {/* INSTANCES */} + {contractInstances.length > 0 && ( + { }} + // unpinInstance={(index) => { }} + // pinInstance={(index, pinnedAt) => { }} + // removeInstance={(index) => { }} + + // "TODO: runTransactions + sendValue" + // runTransactions={executeTransactions} + // sendValue={runTab.sendValue} + // gasEstimationPrompt={gasEstimationPrompt} + // passphrasePrompt={passphrasePrompt} + // mainnetPrompt={mainnetPrompt} + + // solcVersion={solcVersion} + // getVersion={getVersion} + // getFuncABIInputs={getFuncABIValues} + // TODO + solcVersion={{ + version: '', + canReceive: false + }} + evmCheckComplete={true} + exEnvironment={"TODO: environment"} + chain={selectedChain} + editInstance={(instance) => { + // TODO + // const {metadata, abi, object} = instance.contractData + // plugin.call('quick-dapp', 'edit', { + // address: instance.address, + // abi: abi, + // name: instance.name, + // network: runTab.networkName, + // devdoc: object.devdoc, + // methodIdentifiers: object.evm.methodIdentifiers, + // solcVersion: JSON.parse(metadata).compiler.version, + // }) + }} + /> + )} + + ) +} diff --git a/apps/contract-interaction/src/app/views/index.ts b/apps/contract-interaction/src/app/views/index.ts index 5881307514..e54b172f4a 100644 --- a/apps/contract-interaction/src/app/views/index.ts +++ b/apps/contract-interaction/src/app/views/index.ts @@ -1,2 +1,3 @@ -export { SettingsView } from './SettingsView' -export { LookupABIView } from './LookupABIView' +export { GetABIView } from './GetABIView' +export { InteractView } from './InteractView' +export { SettingsView } from './SettingsView' \ No newline at end of file diff --git a/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx b/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx index 12a7da58dd..1f90d48a98 100644 --- a/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx @@ -7,6 +7,11 @@ import { AccountUI } from './account' import { GasLimitUI } from './gasLimit' import { ValueUI } from './value' +// Re-exporting components to be re-used. +export { NetworkUI } from './network' +export { ValueUI } from './value' +export { GasLimitUI } from './gasLimit' + export function SettingsUI(props: SettingsProps) { // this._deps.config.events.on('settings/personal-mode_changed', this.onPersonalChange.bind(this))