Skip to content

Commit

Permalink
Merge pull request #1044 from DarkFlorist/gnosis-meta-transaction-par…
Browse files Browse the repository at this point in the history
…sing

gnosis meta transaction parsing
  • Loading branch information
KillariDev committed Jun 21, 2024
2 parents 76169df + f36d1bd commit 694e7d0
Show file tree
Hide file tree
Showing 22 changed files with 586 additions and 548 deletions.
3 changes: 2 additions & 1 deletion app/ts/background/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getSimulationResults, getTabState, setLatestUnexpectedError, updateSimu
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 { CompleteVisualizedSimulation, ProtectorResults, SimulationState, VisualizedSimulatorState, 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'
Expand Down Expand Up @@ -38,6 +38,7 @@ 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'

async function updateMetadataForSimulation(simulationState: SimulationState, ethereum: EthereumClientService, requestAbortController: AbortController | undefined, eventsForEachTransaction: readonly EnrichedEthereumEvents[], inputData: readonly EnrichedEthereumInputData[], protectorResults: readonly ProtectorResults[]) {
const settingsPromise = getSettings()
Expand Down
13 changes: 9 additions & 4 deletions app/ts/background/metadataUtils.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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)

Expand Down Expand Up @@ -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<AddressBookEntry[]> {
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<AddressBookEntry[]> {
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) {
Expand Down
40 changes: 25 additions & 15 deletions app/ts/background/windows/personalSign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion app/ts/components/formVisualizerResults.ts
Original file line number Diff line number Diff line change
@@ -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<string, AddressBookEntry>) => {
const getNameHash = (node: bigint) => ens.ensNameHashes.find((nameHash) => nameHash.nameHash === node) ?? { nameHash: node, name: undefined }
Expand Down
6 changes: 4 additions & 2 deletions app/ts/components/pages/PersonalSign.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { AddressBookEntry } from '../../types/addressBookTypes.js'
import { EnrichedEIP712, EnrichedEIP712Message, TypeEnrichedEIP712MessageRecord } from '../../types/eip721.js'
import { TransactionCreated } from '../simulationExplaining/SimulationSummary.js'
import { EnrichedSolidityTypeComponent } from '../subcomponents/solidityType.js'
import { QuarantineReasons } from '../simulationExplaining/Transactions.js'
import { ParsedInputData, QuarantineReasons } from '../simulationExplaining/Transactions.js'

type SignatureCardParams = {
visualizedPersonalSignRequest: VisualizedPersonalSignRequest
Expand Down Expand Up @@ -226,10 +226,12 @@ function SafeTx({ visualizedPersonalSignRequestSafeTx, renameAddressCallBack }:
<CellElement text = 'value: '/>
<CellElement text = { <Ether amount = { visualizedPersonalSignRequestSafeTx.message.message.value } rpcNetwork = { visualizedPersonalSignRequestSafeTx.rpcNetwork } fontSize = 'normal'/> }/>
</span>
<p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>Raw transaction input: </p>
<p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>Gnosis Safe meta transaction input: </p>
<div class = 'textbox'>
<p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>{ dataStringWith0xStart(visualizedPersonalSignRequestSafeTx.message.message.data) }</p>
</div>
<p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>Parsed Gnosis Safe meta transaction: </p>
{ visualizedPersonalSignRequestSafeTx.parsedMessageData?.type !== 'Parsed' ? <p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>No ABI available</p> : <ParsedInputData inputData = { visualizedPersonalSignRequestSafeTx.parsedMessageData } addressMetaData = { visualizedPersonalSignRequestSafeTx.parsedMessageDataAddressBookEntries } renameAddressCallBack = { renameAddressCallBack }/> }
</>
}

Expand Down
7 changes: 4 additions & 3 deletions app/ts/components/simulationExplaining/SimulationSummary.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Erc1155TokenBalanceChange, Erc721and1155OperatorChange, LogSummarizer, SummaryOutcome } from '../../simulation/services/LogSummarizer.js'
import { RenameAddressCallBack, RpcConnectionStatus } from '../../types/user-interface-types.js'
import { Erc721TokenApprovalChange, SimulatedAndVisualizedTransaction, SimulationAndVisualisationResults, ERC20TokenApprovalChange, Erc20TokenBalanceChange, TransactionWithAddressBookEntries, NamedTokenId, EnrichedEthereumInputData } from '../../types/visualizer-types.js'
import { Erc721TokenApprovalChange, SimulatedAndVisualizedTransaction, SimulationAndVisualisationResults, ERC20TokenApprovalChange, Erc20TokenBalanceChange, TransactionWithAddressBookEntries, NamedTokenId } from '../../types/visualizer-types.js'
import { BigAddress, SmallAddress, WebsiteOriginText } from '../subcomponents/address.js'
import { Ether, EtherAmount, EtherSymbol, TokenWithAmount, TokenAmount, TokenPrice, TokenSymbol, TokenOrEth } from '../subcomponents/coins.js'
import { NonTokenLogAnalysis, ParsedInputData, TokenLogAnalysis } from './Transactions.js'
Expand All @@ -17,6 +17,7 @@ import { AddressBookEntry, Erc1155Entry, Erc20TokenEntry, Erc721Entry } from '..
import { Website } from '../../types/websiteAccessTypes.js'
import { extractTokenEvents } from '../../background/metadataUtils.js'
import { EditEnsNamedHashCallBack } from '../subcomponents/ens.js'
import { EnrichedEthereumInputData } from '../../types/EnrichedEthereumData.js'

type Erc20BalanceChangeParams = {
erc20TokenBalanceChanges: Erc20TokenBalanceChange[]
Expand Down Expand Up @@ -723,7 +724,7 @@ export function SimulationSummary(param: SimulationSummaryParams) {

type RawTransactionDetailsCardParams = {
addressMetaData: readonly AddressBookEntry[]
parsedInputData: EnrichedEthereumInputData | undefined
parsedInputData: EnrichedEthereumInputData
transaction: TransactionWithAddressBookEntries
renameAddressCallBack: RenameAddressCallBack
gasSpent: bigint
Expand Down Expand Up @@ -782,7 +783,7 @@ export function RawTransactionDetailsCard({ transaction, renameAddressCallBack,
<p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>{ dataStringWith0xStart(transaction.input) }</p>
</div>
<p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>Parsed transaction input: </p>
{ parsedInputData?.type !== 'Parsed' ? <></> : <ParsedInputData inputData = { parsedInputData } addressMetaData = { addressMetaData } renameAddressCallBack = { renameAddressCallBack }/> }
{ parsedInputData?.type !== 'Parsed' ? <p class = 'paragraph' style = 'color: var(--subtitle-text-color)'>No ABI available</p> : <ParsedInputData inputData = { parsedInputData } addressMetaData = { addressMetaData } renameAddressCallBack = { renameAddressCallBack }/> }
</div>
</div>
}
Expand Down
3 changes: 2 additions & 1 deletion app/ts/components/simulationExplaining/SwapTransactions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SimulatedAndVisualizedTransaction, TokenVisualizerResultWithMetadata } from '../../types/visualizer-types.js'
import { SimulatedAndVisualizedTransaction } from '../../types/visualizer-types.js'
import * as funtypes from 'funtypes'
import { EthereumQuantity } from '../../types/wire-types.js'
import { addressString } from '../../utils/bigint.js'
Expand All @@ -7,6 +7,7 @@ import { AddressBookEntry, Erc1155Entry, Erc20TokenEntry, Erc721Entry } from '..
import { assertNever, getWithDefault } from '../../utils/typescript.js'
import { RenameAddressCallBack } from '../../types/user-interface-types.js'
import { extractTokenEvents } from '../../background/metadataUtils.js'
import { TokenVisualizerResultWithMetadata } from '../../types/EnrichedEthereumData.js'

type BeforeAfterBalance = funtypes.Static<typeof SwapAsset>
const BeforeAfterBalance = funtypes.ReadonlyObject({
Expand Down
3 changes: 2 additions & 1 deletion app/ts/components/simulationExplaining/Transactions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EnrichedEthereumEventWithMetadata, EnrichedEthereumInputData, SimulatedAndVisualizedTransaction, SimulationAndVisualisationResults, TokenVisualizerResultWithMetadata, TransactionVisualizationParameters } from '../../types/visualizer-types.js'
import { SimulatedAndVisualizedTransaction, SimulationAndVisualisationResults, TransactionVisualizationParameters } from '../../types/visualizer-types.js'
import { SmallAddress } from '../subcomponents/address.js'
import { TokenSymbol, TokenAmount, AllApproval } from '../subcomponents/coins.js'
import { LogAnalysisParams, NonLogAnalysisParams, RenameAddressCallBack } from '../../types/user-interface-types.js'
Expand All @@ -24,6 +24,7 @@ import { ProxyTokenTransferVisualisation } from './customExplainers/ProxySendVis
import { extractTokenEvents } from '../../background/metadataUtils.js'
import { EditEnsNamedHashCallBack, EnsNamedHashComponent } from '../subcomponents/ens.js'
import { insertBetweenElements } from '../subcomponents/misc.js'
import { EnrichedEthereumEventWithMetadata, EnrichedEthereumInputData, TokenVisualizerResultWithMetadata } from '../../types/EnrichedEthereumData.js'

function isPositiveEvent(visResult: TokenVisualizerResultWithMetadata, ourAddressInReferenceFrame: bigint) {
if (visResult.type === 'ERC20') {
Expand Down
Loading

0 comments on commit 694e7d0

Please sign in to comment.