diff --git a/app/css/interceptor.css b/app/css/interceptor.css
index 1da2f738..f50cf86e 100644
--- a/app/css/interceptor.css
+++ b/app/css/interceptor.css
@@ -1,6 +1,19 @@
@charset "utf-8";
+@font-face {
+ font-family: "Atkinson";
+ src: url("../fonts/Atkinson-Hyperlegible-Regular-102a.woff2") format("woff2");
+ font-weight: normal;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: "Inter";
+ src: url("../fonts/Inter-VariableFont.ttf") format("ttf");
+}
+
html { font-size: 100%; }
+body { font-family: "Inter" }
:root {
--bg-color: #303030;
@@ -41,6 +54,7 @@ html { font-size: 100%; }
}
button:where(:not(.btn)) {
+ font-family: "Inter";
background-color: var(--primary-color);
}
@@ -48,7 +62,7 @@ button:where(:not(.btn)):hover {
background-color: var(--highlighted-primary-color);
}
-button:not(.btn):disabled, button[disabled]) {
+button:not(.btn):disabled, button:not(.btn)[disabled] {
background-color: var(--disabled-primary-color);
border-color: transparent;
}
@@ -248,6 +262,7 @@ li {
}
.card-header {
+ column-gap: 1rem;
background-color: var(--card-header-bg-color);
}
@@ -328,6 +343,39 @@ li {
flex-direction: column;
}
+.button.is-reveal:after {
+ content: '';
+ border-style: solid;
+ border-width: 2px 2px 0 0;
+ display: inline-block;
+ height: 0.6em;
+ width: 0.6em;
+ position: relative;
+ top: 6%;
+ transform: rotate(135deg);
+ transform-origin: 66% 33%;
+ transition: transform 150ms;
+ flex-shrink: 0;
+}
+
+.dropdown.is-active > .dropdown-trigger > button:after {
+ transform: rotate(-45deg);
+}
+
+.dropdown-trigger > button.is-danger:before {
+ content: '!';
+ width: 1.1em;
+ height: 1.1em;
+ font-size: 0.9em;
+ background: white;
+ color: var(--negative-color);
+ flex: 0 0 auto;
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ border-radius: 100%;
+}
+
.dropdown-content {
background-color: var(--card-content-bg-color);
box-shadow: 0px 8px 16px 0px rgb(0 0 0 / 20%);
@@ -335,6 +383,9 @@ li {
.dropdown-item {
color: var(--text-color);
+ background: transparent;
+ border: initial;
+ cursor: pointer;
}
a.dropdown-item.is-active, button.dropdown-item.is-active {
@@ -342,9 +393,6 @@ a.dropdown-item.is-active, button.dropdown-item.is-active {
}
.card-header-icon {
- padding: 0;
- padding-left: 0.75rem;
- padding-right: 0.75rem;
background-color: transparent;
}
@@ -1106,3 +1154,14 @@ summary:where(details[open] > *) {
place-content: center;
justify-items: center;
}
+
+.text-legible {
+ font-family: 'Atkinson';
+}
+
+.truncate {
+ display: block;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
diff --git a/app/fonts/Atkinson-Hyperlegible-Regular-102a.woff2 b/app/fonts/Atkinson-Hyperlegible-Regular-102a.woff2
new file mode 100644
index 00000000..99b3c6f5
Binary files /dev/null and b/app/fonts/Atkinson-Hyperlegible-Regular-102a.woff2 differ
diff --git a/app/fonts/Inter-VariableFont.ttf b/app/fonts/Inter-VariableFont.ttf
new file mode 100644
index 00000000..e7247087
Binary files /dev/null and b/app/fonts/Inter-VariableFont.ttf differ
diff --git a/app/img/warning-sign-white.svg b/app/img/warning-sign-white.svg
deleted file mode 100644
index 7fcf89ae..00000000
--- a/app/img/warning-sign-white.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
diff --git a/app/ts/background/background.ts b/app/ts/background/background.ts
index 13d64221..0b8e8daa 100644
--- a/app/ts/background/background.ts
+++ b/app/ts/background/background.ts
@@ -1,20 +1,20 @@
-import { InpageScriptRequest, PopupMessage, RPCReply, Settings } from '../types/interceptor-messages.js'
+import { InpageScriptRequest, PopupMessage, RPCReply, Settings, SimulateExecutionReplyData } from '../types/interceptor-messages.js'
import 'webextension-polyfill'
import { Simulator, parseEvents, parseInputData, runProtectorsForTransaction } from '../simulation/simulator.js'
import { getSimulationResults, getTabState, setLatestUnexpectedError, updateSimulationResults, updateSimulationResultsWithCallBack } from './storageVariables.js'
import { changeSimulationMode, getSettings, getMakeMeRich, getWethForChainId } from './settings.js'
import { blockNumber, call, chainId, estimateGas, gasPrice, getAccounts, getBalance, getBlockByNumber, getCode, getLogs, getPermissions, getSimulationStack, getTransactionByHash, getTransactionCount, getTransactionReceipt, netVersion, personalSign, sendTransaction, subscribe, switchEthereumChain, unsubscribe, web3ClientVersion, getBlockByHash, feeHistory, installNewFilter, uninstallNewFilter, getFilterChanges, getFilterLogs, handleIterceptorError } from './simulationModeHanders.js'
-import { changeActiveAddress, changeMakeMeRich, changePage, confirmDialog, refreshSimulation, removeTransactionOrSignedMessage, requestAccountsFromSigner, refreshPopupConfirmTransactionSimulation, confirmRequestAccess, changeInterceptorAccess, changeChainDialog, popupChangeActiveRpc, enableSimulationMode, addOrModifyAddressBookEntry, getAddressBookData, removeAddressBookEntry, refreshHomeData, interceptorAccessChangeAddressOrRefresh, refreshPopupConfirmTransactionMetadata, changeSettings, importSettings, exportSettings, setNewRpcList, simulateGovernanceContractExecutionOnPass, openNewTab, settingsOpened, changeAddOrModifyAddressWindowState, popupFetchAbiAndNameFromEtherscan, openWebPage, disableInterceptor, requestNewHomeData, setEnsNameForHash } from './popupMessageHandlers.js'
-import { CompleteVisualizedSimulation, EnrichedEthereumEvents, EnrichedEthereumInputData, ProtectorResults, SimulationState, VisualizedSimulatorState, WebsiteCreatedEthereumUnsignedTransactionOrFailed } from '../types/visualizer-types.js'
+import { changeActiveAddress, changeMakeMeRich, changePage, confirmDialog, refreshSimulation, removeTransactionOrSignedMessage, requestAccountsFromSigner, refreshPopupConfirmTransactionSimulation, confirmRequestAccess, changeInterceptorAccess, changeChainDialog, popupChangeActiveRpc, enableSimulationMode, addOrModifyAddressBookEntry, getAddressBookData, removeAddressBookEntry, refreshHomeData, interceptorAccessChangeAddressOrRefresh, refreshPopupConfirmTransactionMetadata, changeSettings, importSettings, exportSettings, setNewRpcList, simulateGovernanceContractExecutionOnPass, openNewTab, settingsOpened, changeAddOrModifyAddressWindowState, popupFetchAbiAndNameFromEtherscan, openWebPage, disableInterceptor, requestNewHomeData, setEnsNameForHash, simulateGnosisSafeTransactionOnPass } from './popupMessageHandlers.js'
+import { CompleteVisualizedSimulation, ProtectorResults, SimulationState, VisualizedSimulatorState, WebsiteCreatedEthereumUnsignedTransaction, WebsiteCreatedEthereumUnsignedTransactionOrFailed } from '../types/visualizer-types.js'
import { WebsiteTabConnections } from '../types/user-interface-types.js'
import { askForSignerAccountsFromSignerIfNotAvailable, interceptorAccessMetadataRefresh, requestAccessFromUser, updateInterceptorAccessViewWithPendingRequests } from './windows/interceptorAccess.js'
import { FourByteExplanations, METAMASK_ERROR_FAILED_TO_PARSE_REQUEST, METAMASK_ERROR_NOT_AUTHORIZED, METAMASK_ERROR_NOT_CONNECTED_TO_CHAIN, ERROR_INTERCEPTOR_DISABLED, NEW_BLOCK_ABORT, ETHEREUM_LOGS_LOGGER_ADDRESS } from '../utils/constants.js'
import { sendActiveAccountChangeToApprovedWebsitePorts, sendMessageToApprovedWebsitePorts, updateWebsiteApprovalAccesses, verifyAccess } from './accessManagement.js'
import { getActiveAddressEntry, getAddressBookEntriesForVisualiser, identifyAddress, nameTokenIds, retrieveEnsLabelHashes, retrieveEnsNodeHashes } from './metadataUtils.js'
import { getActiveAddress, sendPopupMessageToOpenWindows } from './backgroundUtils.js'
-import { assertNever, assertUnreachable, modifyObject } from '../utils/typescript.js'
+import { DistributiveOmit, assertNever, assertUnreachable, modifyObject } from '../utils/typescript.js'
import { EthereumClientService } from '../simulation/services/EthereumClientService.js'
-import { appendTransaction, calculateGasPrice, copySimulationState, getEmptySimulationStateWithRichAddress, getNonceFixedSimulatedTransactions, getTokenBalancesAfter, getWebsiteCreatedEthereumUnsignedTransactions, mockSignTransaction, setSimulationTransactionsAndSignedMessages } from '../simulation/services/SimulationModeEthereumClientService.js'
+import { appendTransaction, calculateGasPrice, copySimulationState, getEmptySimulationStateWithRichAddress, getNonceFixedSimulatedTransactions, getTokenBalancesAfter, getWebsiteCreatedEthereumUnsignedTransactions, mockSignTransaction, setSimulationTransactionsAndSignedMessages, simulateEstimateGas } from '../simulation/services/SimulationModeEthereumClientService.js'
import { Semaphore } from '../utils/semaphore.js'
import { JsonRpcResponseError, handleUnexpectedError, isFailedToFetchError, isNewBlockAbort } from '../utils/errors.js'
import { formSimulatedAndVisualizedTransaction } from '../components/formVisualizerResults.js'
@@ -38,6 +38,8 @@ import { connectedToSigner, ethAccountsReply, signerChainChanged, signerReply, w
import { makeSureInterceptorIsNotSleeping } from './sleeping.js'
import { decodeEthereumError } from '../utils/errorDecoding.js'
import { estimateEthereumPricesForTokens } from '../simulation/priceEstimator.js'
+import { EnrichedEthereumEvents, EnrichedEthereumInputData } from '../types/EnrichedEthereumData.js'
+import { VisualizedPersonalSignRequestSafeTx } from '../types/personal-message-definitions.js'
async function updateMetadataForSimulation(simulationState: SimulationState, ethereum: EthereumClientService, requestAbortController: AbortController | undefined, eventsForEachTransaction: readonly EnrichedEthereumEvents[], inputData: readonly EnrichedEthereumInputData[], protectorResults: readonly ProtectorResults[]) {
const settingsPromise = getSettings()
@@ -59,8 +61,8 @@ async function updateMetadataForSimulation(simulationState: SimulationState, eth
}
}
-export const simulateGovernanceContractExecution = async (pendingTransaction: PendingTransaction, ethereum: EthereumClientService) => {
- const returnError = (text: string) => ({ success: false as const, error: { type: 'Other' as const, message: text } })
+export const simulateGovernanceContractExecution = async (pendingTransaction: PendingTransaction, ethereum: EthereumClientService): Promise> => {
+ const returnError = (errorMessage: string) => ({ success: false as const, errorType: 'Other' as const, errorMessage })
try {
// identifies compound governane call and performs simulation if the vote passes
if (pendingTransaction.transactionOrMessageCreationStatus !== 'Simulated') return returnError('Still simulating the voting transaction')
@@ -84,7 +86,7 @@ export const simulateGovernanceContractExecution = async (pendingTransaction: Pe
if (pendingTransaction.transactionToSimulate.transaction.to === null) return returnError('The transaction creates a contract instead of casting a vote')
const params = governanceContractInterface.decodeFunctionData(voteFunction, dataStringWith0xStart(pendingTransaction.transactionToSimulate.transaction.input))
const addr = await identifyAddress(ethereum, undefined, pendingTransaction.transactionToSimulate.transaction.to)
- if (!('abi' in addr) || addr.abi === undefined) return { success: false as const, error: { type: 'MissingAbi' as const, message: 'ABi for the governance contract is missing', addressBookEntry: addr } }
+ if (!('abi' in addr) || addr.abi === undefined) return { success: false as const, errorType: 'MissingAbi' as const, errorMessage: 'ABi for the governance contract is missing', errorAddressBookEntry: addr }
const contractExecutionResult = await simulateCompoundGovernanceExecution(ethereum, addr, params[0])
if (contractExecutionResult === undefined) return returnError('Failed to simulate governance execution')
const parentBlock = await ethereum.getBlock(undefined)
@@ -121,6 +123,40 @@ export const simulateGovernanceContractExecution = async (pendingTransaction: Pe
}
}
+export const simulateGnosisSafeMetaTransaction = async (gnosisSafeMessage: VisualizedPersonalSignRequestSafeTx, simulationState: SimulationState | undefined, ethereumClientService: EthereumClientService): Promise> => {
+ const returnError = (errorMessage: string) => ({ success: false as const, errorType: 'Other' as const, errorMessage })
+ try {
+ const transactionWithoutGas = {
+ value: gnosisSafeMessage.message.message.value,
+ to: gnosisSafeMessage.to.address,
+ maxPriorityFeePerGas: 0n,
+ maxFeePerGas: 0n,
+ input: gnosisSafeMessage.parsedMessageData.input,
+ type: '1559' as const,
+ from: gnosisSafeMessage.verifyingContract.address,
+ nonce: 0n,
+ chainId: ethereumClientService.getChainId(),
+ }
+ const gasLimit = gnosisSafeMessage.message.message.baseGas !== 0n ? { gas: gnosisSafeMessage.message.message.baseGas } : await simulateEstimateGas(ethereumClientService, undefined, simulationState, transactionWithoutGas)
+ if ('error' in gasLimit) return returnError(gasLimit.error.message)
+ const transaction = { ...transactionWithoutGas, gas: gasLimit.gas }
+ const metaTransaction: WebsiteCreatedEthereumUnsignedTransaction = {
+ website: gnosisSafeMessage.website,
+ created: new Date(),
+ originalRequestParameters: { method: 'eth_sendTransaction', params: [transaction] },
+ transactionIdentifier: gnosisSafeMessage.messageIdentifier,
+ success: true,
+ transaction,
+ }
+ const simulationStateAfterGnosisSafeMetaTransaction = await appendTransaction(ethereumClientService, undefined, simulationState, metaTransaction)
+ return { success: true as const, result: await visualizeSimulatorState(simulationStateAfterGnosisSafeMetaTransaction, ethereumClientService, undefined) }
+ } catch(error) {
+ console.warn(error)
+ if (error instanceof Error) return returnError(error.message)
+ return returnError('Unknown error occured')
+ }
+}
+
async function visualizeSimulatorState(simulationState: SimulationState, ethereum: EthereumClientService, requestAbortController: AbortController | undefined): Promise {
const transactions = getWebsiteCreatedEthereumUnsignedTransactions(simulationState.simulatedTransactions)
const eventsForEachTransactionPromise = Promise.all(simulationState.simulatedTransactions.map(async (simulatedTransaction) => simulatedTransaction.ethSimulateV1CallResult.status === 'failure' ? [] : await parseEvents(simulatedTransaction.ethSimulateV1CallResult.logs, ethereum, requestAbortController)))
@@ -612,6 +648,7 @@ export async function popupMessageHandler(
case 'popup_get_export_settings': return await exportSettings()
case 'popup_set_rpc_list': return await setNewRpcList(simulator, parsedRequest, settings)
case 'popup_simulateGovernanceContractExecution': return await simulateGovernanceContractExecutionOnPass(simulator.ethereum, parsedRequest)
+ case 'popup_simulateGnosisSafeTransaction': return await simulateGnosisSafeTransactionOnPass(simulator.ethereum, parsedRequest)
case 'popup_changeAddOrModifyAddressWindowState': return await changeAddOrModifyAddressWindowState(simulator.ethereum, parsedRequest)
case 'popup_fetchAbiAndNameFromEtherscan': return await popupFetchAbiAndNameFromEtherscan(parsedRequest)
case 'popup_openWebPage': return await openWebPage(parsedRequest)
diff --git a/app/ts/background/metadataUtils.ts b/app/ts/background/metadataUtils.ts
index 4f44b74f..bd764357 100644
--- a/app/ts/background/metadataUtils.ts
+++ b/app/ts/background/metadataUtils.ts
@@ -1,6 +1,6 @@
import { addressString, addressStringWithout0x, bytesToUnsigned, checksummedAddress } from '../utils/bigint.js'
import { AddressBookEntries, AddressBookEntry } from '../types/addressBookTypes.js'
-import { EnrichedEthereumEventWithMetadata, EnrichedEthereumEvents, EnrichedEthereumInputData, EnsEvent, NamedTokenId, SimulationState, TokenEvent, TokenVisualizerResultWithMetadata } from '../types/visualizer-types.js'
+import { NamedTokenId, SimulationState } from '../types/visualizer-types.js'
import { tokenMetadata, contractMetadata, erc721Metadata, erc1155Metadata } from '@darkflorist/address-metadata'
import { ethers } from 'ethers'
import { ENS_ADDR_REVERSE_NODE, ENS_TOKEN_WRAPPER, ETHEREUM_COIN_ICON, ETHEREUM_LOGS_LOGGER_ADDRESS, MOCK_ADDRESS } from '../utils/constants.js'
@@ -16,6 +16,7 @@ import { EthereumBytes32 } from '../types/wire-types.js'
import { ENSNameHashes } from '../types/ens.js'
import { keccak_256 } from '@noble/hashes/sha3'
const LOGO_URI_PREFIX = '../vendor/@darkflorist/address-metadata'
+import { EnrichedEthereumEventWithMetadata, EnrichedEthereumEvents, EnrichedEthereumInputData, EnsEvent, SolidityVariable, TokenEvent, TokenVisualizerResultWithMetadata } from '../types/EnrichedEthereumData.js'
const pathJoin = (parts: string[], sep = '/') => parts.join(sep).replace(new RegExp(sep + '{1,}', 'g'), sep)
@@ -157,13 +158,17 @@ export async function identifyAddress(ethereumClientService: EthereumClientServi
return entry
}
-export async function getAddressBookEntriesForVisualiser(ethereumClientService: EthereumClientService, requestAbortController: AbortController | undefined, events: EnrichedEthereumEvents, inputData: readonly EnrichedEthereumInputData[], simulationState: SimulationState): Promise {
- const eventAndTransactionArguments = [...events.flatMap((event) => event.type !== 'NonParsed' ? event.args : []), ...inputData.flatMap((event) => event.type !== 'NonParsed' ? event.args : [])]
- const addressesInEventsAndInputData = eventAndTransactionArguments.flatMap((argumentVariable) => {
+export const getAddressesForSolidityTypes = (variables: readonly SolidityVariable[]) => {
+ return variables.map((argumentVariable) => {
if (argumentVariable.typeValue.type === 'address') return argumentVariable.typeValue.value
if (argumentVariable.typeValue.type === 'address[]') return argumentVariable.typeValue.value
return undefined
}).filter((address): address is bigint => address !== undefined)
+}
+
+export async function getAddressBookEntriesForVisualiser(ethereumClientService: EthereumClientService, requestAbortController: AbortController | undefined, events: EnrichedEthereumEvents, inputData: readonly EnrichedEthereumInputData[], simulationState: SimulationState): Promise {
+ const eventAndTransactionArguments = [...events.flatMap((event) => event.type !== 'NonParsed' ? event.args : []), ...inputData.flatMap((event) => event.type !== 'NonParsed' ? event.args : [])]
+ const addressesInEventsAndInputData = getAddressesForSolidityTypes(eventAndTransactionArguments)
const addressesToFetchMetadata = [...addressesInEventsAndInputData, ...events.map((event) => event.address)]
for (const tx of simulationState.simulatedTransactions) {
diff --git a/app/ts/background/popupMessageHandlers.ts b/app/ts/background/popupMessageHandlers.ts
index b6faf483..af0adb2e 100644
--- a/app/ts/background/popupMessageHandlers.ts
+++ b/app/ts/background/popupMessageHandlers.ts
@@ -1,8 +1,8 @@
-import { changeActiveAddressAndChainAndResetSimulation, changeActiveRpc, refreshConfirmTransactionSimulation, updateSimulationState, updateSimulationMetadata, simulateGovernanceContractExecution, resetSimulatorStateFromConfig } from './background.js'
+import { changeActiveAddressAndChainAndResetSimulation, changeActiveRpc, refreshConfirmTransactionSimulation, updateSimulationState, updateSimulationMetadata, simulateGovernanceContractExecution, resetSimulatorStateFromConfig, simulateGnosisSafeMetaTransaction } from './background.js'
import { getSettings, setUseTabsInsteadOfPopup, setMakeMeRich, setPage, setUseSignersAddressAsActiveAddress, updateWebsiteAccess, exportSettingsAndAddressBook, importSettingsAndAddressBook, getMakeMeRich, getUseTabsInsteadOfPopup, getMetamaskCompatibilityMode, setMetamaskCompatibilityMode, getPage } from './settings.js'
import { getPendingTransactionsAndMessages, getCurrentTabId, getTabState, saveCurrentTabId, setRpcList, getRpcList, getPrimaryRpcForChain, getRpcConnectionStatus, updateUserAddressBookEntries, getSimulationResults, setIdsOfOpenedTabs, getIdsOfOpenedTabs, updatePendingTransactionOrMessage, getLatestUnexpectedError, addEnsLabelHash, addEnsNodeHash } from './storageVariables.js'
import { Simulator, parseEvents, parseInputData } from '../simulation/simulator.js'
-import { ChangeActiveAddress, ChangeMakeMeRich, ChangePage, RemoveTransaction, RequestAccountsFromSigner, TransactionConfirmation, InterceptorAccess, ChangeInterceptorAccess, ChainChangeConfirmation, EnableSimulationMode, ChangeActiveChain, AddOrEditAddressBookEntry, GetAddressBookData, RemoveAddressBookEntry, InterceptorAccessRefresh, InterceptorAccessChangeAddress, Settings, RefreshConfirmTransactionMetadata, ChangeSettings, ImportSettings, SetRpcList, UpdateHomePage, SimulateGovernanceContractExecutionReply, SimulateGovernanceContractExecution, ChangeAddOrModifyAddressWindowState, FetchAbiAndNameFromEtherscan, OpenWebPage, DisableInterceptor, SetEnsNameForHash, UpdateConfirmTransactionDialog, UpdateConfirmTransactionDialogPendingTransactions } from '../types/interceptor-messages.js'
+import { ChangeActiveAddress, ChangeMakeMeRich, ChangePage, RemoveTransaction, RequestAccountsFromSigner, TransactionConfirmation, InterceptorAccess, ChangeInterceptorAccess, ChainChangeConfirmation, EnableSimulationMode, ChangeActiveChain, AddOrEditAddressBookEntry, GetAddressBookData, RemoveAddressBookEntry, InterceptorAccessRefresh, InterceptorAccessChangeAddress, Settings, RefreshConfirmTransactionMetadata, ChangeSettings, ImportSettings, SetRpcList, UpdateHomePage, SimulateGovernanceContractExecution, ChangeAddOrModifyAddressWindowState, FetchAbiAndNameFromEtherscan, OpenWebPage, DisableInterceptor, SetEnsNameForHash, UpdateConfirmTransactionDialog, UpdateConfirmTransactionDialogPendingTransactions, SimulateGnosisSafeTransaction, SimulateExecutionReply } from '../types/interceptor-messages.js'
import { formEthSendTransaction, formSendRawTransaction, resolvePendingTransactionOrMessage, updateConfirmTransactionView } from './windows/confirmTransaction.js'
import { getAddressMetadataForAccess, requestAddressChange, resolveInterceptorAccess } from './windows/interceptorAccess.js'
import { resolveChainChange } from './windows/changeChain.js'
@@ -383,9 +383,18 @@ export async function simulateGovernanceContractExecutionOnPass(ethereum: Ethere
const transaction = pendingTransactions.find((tx) => tx.type === 'Transaction' && tx.transactionIdentifier === request.data.transactionIdentifier)
if (transaction === undefined || transaction.type !== 'Transaction') throw new Error(`Could not find transactionIdentifier: ${ request.data.transactionIdentifier }`)
const governanceContractExecutionVisualisation = await simulateGovernanceContractExecution(transaction, ethereum)
- return await sendPopupMessageToOpenWindows(serialize(SimulateGovernanceContractExecutionReply, {
- method: 'popup_simulateGovernanceContractExecutionReply' as const,
- data: { ...governanceContractExecutionVisualisation, transactionIdentifier: request.data.transactionIdentifier }
+ return await sendPopupMessageToOpenWindows(serialize(SimulateExecutionReply, {
+ method: 'popup_simulateExecutionReply' as const,
+ data: { ...governanceContractExecutionVisualisation, transactionOrMessageIdentifier: request.data.transactionIdentifier }
+ }))
+}
+
+export async function simulateGnosisSafeTransactionOnPass(ethereum: EthereumClientService, request: SimulateGnosisSafeTransaction) {
+ const simulationResults = await getSimulationResults()
+ const gnosisTransactionExecutionVisualisation = await simulateGnosisSafeMetaTransaction(request.data.gnosisSafeMessage, simulationResults.simulationState, ethereum)
+ return await sendPopupMessageToOpenWindows(serialize(SimulateExecutionReply, {
+ method: 'popup_simulateExecutionReply' as const,
+ data: { ...gnosisTransactionExecutionVisualisation, transactionOrMessageIdentifier: request.data.gnosisSafeMessage.messageIdentifier }
}))
}
diff --git a/app/ts/background/windows/confirmTransaction.ts b/app/ts/background/windows/confirmTransaction.ts
index 82b95236..4565d48b 100644
--- a/app/ts/background/windows/confirmTransaction.ts
+++ b/app/ts/background/windows/confirmTransaction.ts
@@ -317,7 +317,7 @@ export async function openConfirmTransactionDialogForTransaction(
const openedDialog = await getPendingTransactionWindow(simulator, websiteTabConnections)
if (openedDialog === undefined) throw new Error('Failed to get pending transaction window!')
- const pendingTransaction = {
+ const pendingTransaction = {
type: 'Transaction' as const,
popupOrTabId: openedDialog,
originalRequestParameters: transactionParams,
diff --git a/app/ts/background/windows/personalSign.ts b/app/ts/background/windows/personalSign.ts
index 0d6da6fb..e30d55ac 100644
--- a/app/ts/background/windows/personalSign.ts
+++ b/app/ts/background/windows/personalSign.ts
@@ -4,11 +4,12 @@ import { OpenSeaOrderMessage, PersonalSignRequestIdentifiedEIP712Message, Visual
import { assertNever } from '../../utils/typescript.js'
import { extractEIP712Message, validateEIP712Types } from '../../utils/eip712Parsing.js'
import { getRpcNetworkForChain, getTabState } from '../storageVariables.js'
-import { identifyAddress } from '../metadataUtils.js'
+import { getAddressesForSolidityTypes, identifyAddress } from '../metadataUtils.js'
import { AddressBookEntry } from '../../types/addressBookTypes.js'
import { SignedMessageTransaction } from '../../types/visualizer-types.js'
import { RpcNetwork } from '../../types/rpc.js'
import { getChainName } from '../../utils/constants.js'
+import { parseInputData } from '../../simulation/simulator.js'
async function addMetadataToOpenSeaOrder(ethereumClientService: EthereumClientService, requestAbortController: AbortController | undefined, openSeaOrder: OpenSeaOrderMessage) {
return {
@@ -123,20 +124,29 @@ export async function craftPersonalSignPopupMessage(ethereumClientService: Ether
rawMessage: stringifyJSONWithBigInts(parsed, 4),
}
}
- case 'SafeTx': return {
- method: originalParams.originalRequestParameters.method,
- ...basicParams,
- rpcNetwork: parsed.domain.chainId !== undefined && rpcNetwork.chainId !== parsed.domain.chainId ? await getRpcNetworkForChain(parsed.domain.chainId) : rpcNetwork,
- type: 'SafeTx' as const,
- message: parsed,
- account,
- to: await identifyAddress(ethereumClientService, requestAbortController, parsed.message.to),
- gasToken: await identifyAddress(ethereumClientService, requestAbortController, parsed.message.gasToken),
- refundReceiver: await identifyAddress(ethereumClientService, requestAbortController, parsed.message.refundReceiver),
- verifyingContract: await identifyAddress(ethereumClientService, requestAbortController, parsed.domain.verifyingContract),
- quarantine: false,
- quarantineReasons: [],
- rawMessage: stringifyJSONWithBigInts(parsed, 4),
+ case 'SafeTx': {
+ const addresses = {
+ to: await identifyAddress(ethereumClientService, requestAbortController, parsed.message.to),
+ gasToken: await identifyAddress(ethereumClientService, requestAbortController, parsed.message.gasToken),
+ refundReceiver: await identifyAddress(ethereumClientService, requestAbortController, parsed.message.refundReceiver),
+ verifyingContract: await identifyAddress(ethereumClientService, requestAbortController, parsed.domain.verifyingContract),
+ }
+ const parsedMessageData = await parseInputData({ to: parsed.message.to, value: 0n, input: parsed.message.data }, ethereumClientService, requestAbortController)
+ const addressesInEventsAndInputData = getAddressesForSolidityTypes(parsedMessageData.type === 'Parsed' ? parsedMessageData.args : [])
+ return {
+ method: originalParams.originalRequestParameters.method,
+ ...basicParams,
+ rpcNetwork: parsed.domain.chainId !== undefined && rpcNetwork.chainId !== parsed.domain.chainId ? await getRpcNetworkForChain(parsed.domain.chainId) : rpcNetwork,
+ type: 'SafeTx' as const,
+ message: parsed,
+ account,
+ ...addresses,
+ quarantine: false,
+ quarantineReasons: [],
+ rawMessage: stringifyJSONWithBigInts(parsed, 4),
+ parsedMessageData,
+ parsedMessageDataAddressBookEntries: await Promise.all(addressesInEventsAndInputData.map((address) => identifyAddress(ethereumClientService, requestAbortController, address)))
+ }
}
case 'OrderComponents': return {
method: originalParams.originalRequestParameters.method,
diff --git a/app/ts/components/formVisualizerResults.ts b/app/ts/components/formVisualizerResults.ts
index fcfa377f..8c842960 100644
--- a/app/ts/components/formVisualizerResults.ts
+++ b/app/ts/components/formVisualizerResults.ts
@@ -1,10 +1,11 @@
import { addressString, dataStringWith0xStart } from '../utils/bigint.js'
-import { EnrichedEthereumEventWithMetadata, EnrichedEthereumEvents, EnrichedEthereumInputData, NamedTokenId, ParsedEnsEvent, ProtectorResults, SimulatedAndVisualizedTransaction, SimulationState } from '../types/visualizer-types.js'
+import { NamedTokenId, ProtectorResults, SimulatedAndVisualizedTransaction, SimulationState } from '../types/visualizer-types.js'
import { AddressBookEntry } from '../types/addressBookTypes.js'
import { Interface } from 'ethers'
import { decodeEthereumError } from '../utils/errorDecoding.js'
import { MaybeENSLabelHashes, MaybeENSNameHashes } from '../types/ens.js'
import { assertNever } from '../utils/typescript.js'
+import { EnrichedEthereumEventWithMetadata, EnrichedEthereumEvents, EnrichedEthereumInputData, ParsedEnsEvent } from '../types/EnrichedEthereumData.js'
const enrichEnsEvent = (event: ParsedEnsEvent, ens: { ensNameHashes: MaybeENSNameHashes, ensLabelHashes: MaybeENSLabelHashes }, addressMetaData: Map) => {
const getNameHash = (node: bigint) => ens.ensNameHashes.find((nameHash) => nameHash.nameHash === node) ?? { nameHash: node, name: undefined }
diff --git a/app/ts/components/pages/ConfirmTransaction.tsx b/app/ts/components/pages/ConfirmTransaction.tsx
index 47954fe9..33d9028f 100644
--- a/app/ts/components/pages/ConfirmTransaction.tsx
+++ b/app/ts/components/pages/ConfirmTransaction.tsx
@@ -536,7 +536,7 @@ export function ConfirmTransaction() {
setActiveAddressAndInformAboutIt = { undefined }
modifyAddressWindowState = { modalState.state }
close = { () => { setModalState({ page: 'noModal' }) } }
- activeAddress = { undefined }
+ activeAddress = { currentPendingTransactionOrSignableMessage?.activeAddress }
/>
: <>> }
@@ -568,7 +568,7 @@ export function ConfirmTransaction() {
setActiveAddressAndInformAboutIt = { undefined }
modifyAddressWindowState = { modalState.state }
close = { () => { setModalState({ page: 'noModal' }) } }
- activeAddress = { undefined }
+ activeAddress = { currentPendingTransactionOrSignableMessage?.activeAddress }
/>
: <>> }
@@ -615,6 +615,7 @@ export function ConfirmTransaction() {
renameAddressCallBack = { renameAddressCallBack }
removeTransactionOrSignedMessage = { undefined }
numberOfUnderTransactions = { underTransactions.length }
+ editEnsNamedHashCallBack = { editEnsNamedHashCallBack }
/>
> }
diff --git a/app/ts/components/pages/Home.tsx b/app/ts/components/pages/Home.tsx
index cb90c077..f5cdf809 100644
--- a/app/ts/components/pages/Home.tsx
+++ b/app/ts/components/pages/Home.tsx
@@ -39,16 +39,14 @@ function SignerExplanation(param: SignerExplanationParams) {
function FirstCardHeader(param: FirstCardParams) {
return <>
-