diff --git a/src/components/tx/SignOrExecuteForm/ExecuteForm.tsx b/src/components/tx/SignOrExecuteForm/ExecuteForm.tsx
index 3afcb8ca1e..09a17f1894 100644
--- a/src/components/tx/SignOrExecuteForm/ExecuteForm.tsx
+++ b/src/components/tx/SignOrExecuteForm/ExecuteForm.tsx
@@ -26,8 +26,6 @@ import commonCss from '@/components/tx-flow/common/styles.module.css'
import { TxSecurityContext } from '../security/shared/TxSecurityContext'
import useIsSafeOwner from '@/hooks/useIsSafeOwner'
import NonOwnerError from '@/components/tx/SignOrExecuteForm/NonOwnerError'
-import { useAppSelector } from '@/store'
-import { selectQueuedTransactionById } from '@/store/txQueueSlice'
const ExecuteForm = ({
safeTx,
@@ -52,8 +50,6 @@ const ExecuteForm = ({
const { setTxFlow } = useContext(TxModalContext)
const { needsRiskConfirmation, isRiskConfirmed, setIsRiskIgnored } = useContext(TxSecurityContext)
- const tx = useAppSelector((state) => selectQueuedTransactionById(state, txId))
-
// Check that the transaction is executable
const isExecutionLoop = useIsExecutionLoop()
@@ -89,7 +85,7 @@ const ExecuteForm = ({
const txOptions = getTxOptions(advancedParams, currentChain)
try {
- const executedTxId = await executeTx(txOptions, safeTx, txId, origin, willRelay, tx)
+ const executedTxId = await executeTx(txOptions, safeTx, txId, origin, willRelay)
setTxFlow(, undefined, false)
} catch (_err) {
const err = asError(_err)
diff --git a/src/components/tx/SignOrExecuteForm/SignForm.tsx b/src/components/tx/SignOrExecuteForm/SignForm.tsx
index 7f9b48675f..01f6cc9215 100644
--- a/src/components/tx/SignOrExecuteForm/SignForm.tsx
+++ b/src/components/tx/SignOrExecuteForm/SignForm.tsx
@@ -14,8 +14,6 @@ import commonCss from '@/components/tx-flow/common/styles.module.css'
import { TxSecurityContext } from '../security/shared/TxSecurityContext'
import NonOwnerError from '@/components/tx/SignOrExecuteForm/NonOwnerError'
import BatchButton from './BatchButton'
-import { useAppSelector } from '@/store'
-import { selectQueuedTransactionById } from '@/store/txQueueSlice'
const SignForm = ({
safeTx,
@@ -40,8 +38,6 @@ const SignForm = ({
const { needsRiskConfirmation, isRiskConfirmed, setIsRiskIgnored } = useContext(TxSecurityContext)
const hasSigned = useAlreadySigned(safeTx)
- const tx = useAppSelector((state) => selectQueuedTransactionById(state, txId))
-
// On modal submit
const handleSubmit = async (e: SyntheticEvent, isAddingToBatch = false) => {
e.preventDefault()
@@ -57,7 +53,7 @@ const SignForm = ({
setSubmitError(undefined)
try {
- await (isAddingToBatch ? addToBatch(safeTx, origin) : signTx(safeTx, txId, origin, tx))
+ await (isAddingToBatch ? addToBatch(safeTx, origin) : signTx(safeTx, txId, origin))
} catch (_err) {
const err = asError(_err)
trackError(Errors._805, err)
diff --git a/src/components/tx/SignOrExecuteForm/hooks.ts b/src/components/tx/SignOrExecuteForm/hooks.ts
index a0a4a712c0..ca40563468 100644
--- a/src/components/tx/SignOrExecuteForm/hooks.ts
+++ b/src/components/tx/SignOrExecuteForm/hooks.ts
@@ -18,18 +18,17 @@ import type { OnboardAPI } from '@web3-onboard/core'
import { getSafeTxGas, getRecommendedNonce } from '@/services/tx/tx-sender/recommendedNonce'
import useAsync from '@/hooks/useAsync'
import { useUpdateBatch } from '@/hooks/useDraftBatch'
-import { type Transaction, type TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
+import { type TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
type TxActions = {
addToBatch: (safeTx?: SafeTransaction, origin?: string) => Promise
- signTx: (safeTx?: SafeTransaction, txId?: string, origin?: string, transaction?: Transaction) => Promise
+ signTx: (safeTx?: SafeTransaction, txId?: string, origin?: string) => Promise
executeTx: (
txOptions: TransactionOptions,
safeTx?: SafeTransaction,
txId?: string,
origin?: string,
isRelayed?: boolean,
- transaction?: Transaction,
) => Promise
}
@@ -85,30 +84,28 @@ export const useTxActions = (): TxActions => {
return await dispatchTxSigning(safeTx, version, onboard, chainId, txId)
}
- const signTx: TxActions['signTx'] = async (safeTx, txId, origin, transaction) => {
+ const signTx: TxActions['signTx'] = async (safeTx, txId, origin) => {
assertTx(safeTx)
assertWallet(wallet)
assertOnboard(onboard)
- const humanDescription = transaction?.transaction?.txInfo?.humanDescription
-
// Smart contract wallets must sign via an on-chain tx
if (await isSmartContractWallet(wallet)) {
// If the first signature is a smart contract wallet, we have to propose w/o signatures
// Otherwise the backend won't pick up the tx
// The signature will be added once the on-chain signature is indexed
const id = txId || (await proposeTx(wallet.address, safeTx, txId, origin)).txId
- await dispatchOnChainSigning(safeTx, id, onboard, chainId, humanDescription)
+ await dispatchOnChainSigning(safeTx, id, onboard, chainId)
return id
}
// Otherwise, sign off-chain
- const signedTx = await dispatchTxSigning(safeTx, version, onboard, chainId, txId, humanDescription)
+ const signedTx = await dispatchTxSigning(safeTx, version, onboard, chainId, txId)
const tx = await proposeTx(wallet.address, signedTx, txId, origin)
return tx.txId
}
- const executeTx: TxActions['executeTx'] = async (txOptions, safeTx, txId, origin, isRelayed, transaction) => {
+ const executeTx: TxActions['executeTx'] = async (txOptions, safeTx, txId, origin, isRelayed) => {
assertTx(safeTx)
assertWallet(wallet)
assertOnboard(onboard)
@@ -127,13 +124,11 @@ export const useTxActions = (): TxActions => {
txId = tx.txId
}
- const humanDescription = tx?.txInfo?.humanDescription || transaction?.transaction?.txInfo?.humanDescription
-
// Relay or execute the tx via connected wallet
if (isRelayed) {
- await dispatchTxRelay(safeTx, safe, txId, txOptions.gasLimit, humanDescription)
+ await dispatchTxRelay(safeTx, safe, txId, txOptions.gasLimit)
} else {
- await dispatchTxExecution(safeTx, txOptions, txId, onboard, chainId, safeAddress, humanDescription)
+ await dispatchTxExecution(safeTx, txOptions, txId, onboard, chainId, safeAddress)
}
return txId
diff --git a/src/hooks/useTxNotifications.ts b/src/hooks/useTxNotifications.ts
index cf3924fb37..e2b5db3657 100644
--- a/src/hooks/useTxNotifications.ts
+++ b/src/hooks/useTxNotifications.ts
@@ -14,6 +14,7 @@ import useIsSafeOwner from '@/hooks/useIsSafeOwner'
import useWallet from './wallets/useWallet'
import useSafeAddress from './useSafeAddress'
import { getExplorerLink } from '@/utils/gateway'
+import { getTxDetails } from '@/services/tx/txDetails'
const TxNotifications = {
[TxEvent.SIGN_FAILED]: 'Failed to sign. Please try again.',
@@ -69,15 +70,22 @@ const useTxNotifications = (): void => {
const entries = Object.entries(TxNotifications) as [keyof typeof TxNotifications, string][]
const unsubFns = entries.map(([event, baseMessage]) =>
- txSubscribe(event, (detail) => {
+ txSubscribe(event, async (detail) => {
const isError = 'error' in detail
const isSuccess = successEvents.includes(event)
const message = isError ? `${baseMessage} ${formatError(detail.error)}` : baseMessage
const txId = 'txId' in detail ? detail.txId : undefined
const txHash = 'txHash' in detail ? detail.txHash : undefined
const groupKey = 'groupKey' in detail && detail.groupKey ? detail.groupKey : txId || ''
- const humanDescription =
- 'humanDescription' in detail && detail.humanDescription ? detail.humanDescription : 'Transaction'
+
+ let humanDescription = 'Transaction'
+ const id = txId || txHash
+ if (id) {
+ try {
+ const txDetails = await getTxDetails(chain.chainId, id)
+ humanDescription = txDetails.txInfo.humanDescription || humanDescription
+ } catch {}
+ }
dispatch(
showNotification({
diff --git a/src/hooks/useTxTracking.ts b/src/hooks/useTxTracking.ts
index 1efc9adde2..d62aa118ce 100644
--- a/src/hooks/useTxTracking.ts
+++ b/src/hooks/useTxTracking.ts
@@ -1,23 +1,43 @@
import { trackEvent, WALLET_EVENTS } from '@/services/analytics'
+import { getTxDetails } from '@/services/tx/txDetails'
import { TxEvent, txSubscribe } from '@/services/tx/txEvents'
import { useEffect } from 'react'
+import useChainId from './useChainId'
const events = {
- [TxEvent.SIGNED]: WALLET_EVENTS.OFF_CHAIN_SIGNATURE,
- [TxEvent.PROCESSING]: WALLET_EVENTS.ON_CHAIN_INTERACTION,
- [TxEvent.RELAYING]: WALLET_EVENTS.RELAYED_EXECUTION,
+ [TxEvent.SIGNED]: WALLET_EVENTS.OFFCHAIN_SIGNATURE,
+ [TxEvent.PROCESSING]: WALLET_EVENTS.ONCHAIN_INTERACTION,
+ [TxEvent.PROCESSING_MODULE]: WALLET_EVENTS.ONCHAIN_INTERACTION,
+ [TxEvent.RELAYING]: WALLET_EVENTS.ONCHAIN_INTERACTION,
}
export const useTxTracking = (): void => {
+ const chainId = useChainId()
+
useEffect(() => {
const unsubFns = Object.entries(events).map(([txEvent, analyticsEvent]) =>
- txSubscribe(txEvent as TxEvent, () => {
- trackEvent(analyticsEvent)
+ txSubscribe(txEvent as TxEvent, async (detail) => {
+ const txId = 'txId' in detail ? detail.txId : undefined
+ const txHash = 'txHash' in detail ? detail.txHash : undefined
+ const id = txId || txHash
+
+ let origin = ''
+ if (id) {
+ try {
+ const txDetails = await getTxDetails(chainId, id)
+ origin = txDetails.safeAppInfo?.url || ''
+ } catch {}
+ }
+
+ trackEvent({
+ ...analyticsEvent,
+ label: origin,
+ })
}),
)
return () => {
unsubFns.forEach((unsub) => unsub())
}
- }, [])
+ }, [chainId])
}
diff --git a/src/services/analytics/events/wallet.ts b/src/services/analytics/events/wallet.ts
index d634562d68..00266e46ca 100644
--- a/src/services/analytics/events/wallet.ts
+++ b/src/services/analytics/events/wallet.ts
@@ -13,14 +13,12 @@ export const WALLET_EVENTS = {
action: 'WalletConnect peer',
category: WALLET_CATEGORY,
},
- OFF_CHAIN_SIGNATURE: {
+ OFFCHAIN_SIGNATURE: {
event: EventType.META,
action: 'Off-chain signature',
category: WALLET_CATEGORY,
},
- // Please note that this event isn't triggered for any on-chain interaction.
- // It's only triggered on safe tx execution and batch execution.
- ON_CHAIN_INTERACTION: {
+ ONCHAIN_INTERACTION: {
event: EventType.META,
action: 'On-chain interaction',
category: WALLET_CATEGORY,
diff --git a/src/services/tx/tx-sender/dispatch.ts b/src/services/tx/tx-sender/dispatch.ts
index a5a885d0e5..30f758e74f 100644
--- a/src/services/tx/tx-sender/dispatch.ts
+++ b/src/services/tx/tx-sender/dispatch.ts
@@ -63,7 +63,6 @@ export const dispatchTxProposal = async ({
txDispatch(txId ? TxEvent.SIGNATURE_PROPOSED : TxEvent.PROPOSED, {
txId: proposedTx.txId,
signerAddress: txId ? sender : undefined,
- humanDescription: proposedTx?.txInfo?.humanDescription,
})
}
@@ -79,7 +78,6 @@ export const dispatchTxSigning = async (
onboard: OnboardAPI,
chainId: SafeInfo['chainId'],
txId?: string,
- humanDescription?: string,
): Promise => {
const sdk = await getSafeSDKWithSigner(onboard, chainId)
@@ -90,7 +88,6 @@ export const dispatchTxSigning = async (
txDispatch(TxEvent.SIGN_FAILED, {
txId,
error: asError(error),
- humanDescription,
})
throw error
}
@@ -108,11 +105,10 @@ export const dispatchOnChainSigning = async (
txId: string,
onboard: OnboardAPI,
chainId: SafeInfo['chainId'],
- humanDescription?: string,
) => {
const sdkUnchecked = await getUncheckedSafeSDK(onboard, chainId)
const safeTxHash = await sdkUnchecked.getTransactionHash(safeTx)
- const eventParams = { txId, humanDescription }
+ const eventParams = { txId }
try {
// With the unchecked signer, the contract call resolves once the tx
@@ -140,10 +136,9 @@ export const dispatchTxExecution = async (
onboard: OnboardAPI,
chainId: SafeInfo['chainId'],
safeAddress: string,
- humanDescription?: string,
): Promise => {
const sdkUnchecked = await getUncheckedSafeSDK(onboard, chainId)
- const eventParams = { txId, humanDescription }
+ const eventParams = { txId }
// Execute the tx
let result: TransactionResult | undefined
@@ -327,7 +322,6 @@ export const dispatchTxRelay = async (
safe: SafeInfo,
txId: string,
gasLimit?: string | number,
- humanDescription?: string,
) => {
const readOnlySafeContract = getReadOnlyCurrentGnosisSafeContract(safe)
@@ -356,9 +350,9 @@ export const dispatchTxRelay = async (
txDispatch(TxEvent.RELAYING, { taskId, txId })
// Monitor relay tx
- waitForRelayedTx(taskId, [txId], safe.address.value, humanDescription)
+ waitForRelayedTx(taskId, [txId], safe.address.value)
} catch (error) {
- txDispatch(TxEvent.FAILED, { txId, error: asError(error), humanDescription })
+ txDispatch(TxEvent.FAILED, { txId, error: asError(error) })
throw error
}
}
diff --git a/src/services/tx/txDetails.ts b/src/services/tx/txDetails.ts
new file mode 100644
index 0000000000..79b40397d7
--- /dev/null
+++ b/src/services/tx/txDetails.ts
@@ -0,0 +1,15 @@
+import memoize from 'lodash/memoize'
+import { getTransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
+
+/**
+ * Get and memoize transaction details from Safe Gatewa
+ * @param id Transaction id or hash
+ * @param chainId Chain id
+ * @returns Transaction details
+ */
+export const getTxDetails = memoize(
+ (id: string, chainId: string) => {
+ return getTransactionDetails(id, chainId)
+ },
+ (id: string, chainId: string) => `${id}-${chainId}`,
+)
diff --git a/src/services/tx/txEvents.ts b/src/services/tx/txEvents.ts
index 6e63e527b1..0adc2102de 100644
--- a/src/services/tx/txEvents.ts
+++ b/src/services/tx/txEvents.ts
@@ -24,26 +24,25 @@ export enum TxEvent {
}
type Id = { txId: string; groupKey?: string } | { txId?: string; groupKey: string }
-type HumanDescription = { humanDescription?: string }
interface TxEvents {
[TxEvent.SIGNED]: { txId?: string }
- [TxEvent.SIGN_FAILED]: HumanDescription & { txId?: string; error: Error }
- [TxEvent.PROPOSE_FAILED]: HumanDescription & { error: Error }
- [TxEvent.PROPOSED]: HumanDescription & { txId: string }
- [TxEvent.SIGNATURE_PROPOSE_FAILED]: HumanDescription & { txId: string; error: Error }
- [TxEvent.SIGNATURE_PROPOSED]: HumanDescription & { txId: string; signerAddress: string }
+ [TxEvent.SIGN_FAILED]: { txId?: string; error: Error }
+ [TxEvent.PROPOSE_FAILED]: { error: Error }
+ [TxEvent.PROPOSED]: { txId: string }
+ [TxEvent.SIGNATURE_PROPOSE_FAILED]: { txId: string; error: Error }
+ [TxEvent.SIGNATURE_PROPOSED]: { txId: string; signerAddress: string }
[TxEvent.SIGNATURE_INDEXED]: { txId: string }
- [TxEvent.ONCHAIN_SIGNATURE_REQUESTED]: Id & HumanDescription
- [TxEvent.ONCHAIN_SIGNATURE_SUCCESS]: Id & HumanDescription
- [TxEvent.EXECUTING]: Id & HumanDescription
- [TxEvent.PROCESSING]: Id & HumanDescription & { txHash: string }
- [TxEvent.PROCESSING_MODULE]: Id & HumanDescription & { txHash: string }
- [TxEvent.PROCESSED]: Id & HumanDescription & { safeAddress: string }
- [TxEvent.REVERTED]: Id & HumanDescription & { error: Error }
+ [TxEvent.ONCHAIN_SIGNATURE_REQUESTED]: Id
+ [TxEvent.ONCHAIN_SIGNATURE_SUCCESS]: Id
+ [TxEvent.EXECUTING]: Id
+ [TxEvent.PROCESSING]: Id & { txHash: string }
+ [TxEvent.PROCESSING_MODULE]: Id & { txHash: string }
+ [TxEvent.PROCESSED]: Id & { safeAddress: string }
+ [TxEvent.REVERTED]: Id & { error: Error }
[TxEvent.RELAYING]: Id & { taskId: string }
- [TxEvent.FAILED]: Id & HumanDescription & { error: Error }
- [TxEvent.SUCCESS]: Id & HumanDescription
+ [TxEvent.FAILED]: Id & { error: Error }
+ [TxEvent.SUCCESS]: Id
[TxEvent.SAFE_APPS_REQUEST]: { safeAppRequestId: RequestId; safeTxHash: string }
[TxEvent.BATCH_ADD]: Id
}
diff --git a/src/services/tx/txMonitor.ts b/src/services/tx/txMonitor.ts
index 878b47d61a..b75ac03f8d 100644
--- a/src/services/tx/txMonitor.ts
+++ b/src/services/tx/txMonitor.ts
@@ -91,13 +91,7 @@ const getRelayTxStatus = async (taskId: string): Promise<{ task: TransactionStat
const WAIT_FOR_RELAY_TIMEOUT = 3 * 60_000 // 3 minutes
-export const waitForRelayedTx = (
- taskId: string,
- txIds: string[],
- safeAddress: string,
- groupKey?: string,
- humanDescription?: string,
-): void => {
+export const waitForRelayedTx = (taskId: string, txIds: string[], safeAddress: string, groupKey?: string): void => {
let intervalId: NodeJS.Timeout
let failAfterTimeoutId: NodeJS.Timeout
@@ -116,7 +110,6 @@ export const waitForRelayedTx = (
txId,
groupKey,
safeAddress,
- humanDescription,
}),
)
break
@@ -126,7 +119,6 @@ export const waitForRelayedTx = (
txId,
error: new Error(`Relayed transaction reverted by EVM.`),
groupKey,
- humanDescription,
}),
)
break
@@ -136,7 +128,6 @@ export const waitForRelayedTx = (
txId,
error: new Error(`Relayed transaction was blacklisted by relay provider.`),
groupKey,
- humanDescription,
}),
)
break
@@ -146,7 +137,6 @@ export const waitForRelayedTx = (
txId,
error: new Error(`Relayed transaction was cancelled by relay provider.`),
groupKey,
- humanDescription,
}),
)
break
@@ -156,7 +146,6 @@ export const waitForRelayedTx = (
txId,
error: new Error(`Relayed transaction was not found.`),
groupKey,
- humanDescription,
}),
)
break
@@ -179,7 +168,6 @@ export const waitForRelayedTx = (
} minutes. Be aware that it might still be relayed.`,
),
groupKey,
- humanDescription,
}),
)
diff --git a/src/store/txHistorySlice.ts b/src/store/txHistorySlice.ts
index 4a771de1d3..d9717d0a3b 100644
--- a/src/store/txHistorySlice.ts
+++ b/src/store/txHistorySlice.ts
@@ -28,12 +28,9 @@ export const txHistoryListener = (listenerMiddleware: typeof listenerMiddlewareI
const txId = result.transaction.id
if (pendingTxs[txId]) {
- const humanDescription = result.transaction.txInfo?.humanDescription
-
txDispatch(TxEvent.SUCCESS, {
txId,
groupKey: pendingTxs[txId].groupKey,
- humanDescription,
})
}
}
diff --git a/src/store/txQueueSlice.ts b/src/store/txQueueSlice.ts
index 6ec1a50c27..5d84b62437 100644
--- a/src/store/txQueueSlice.ts
+++ b/src/store/txQueueSlice.ts
@@ -27,14 +27,6 @@ export const selectQueuedTransactionsByNonce = createSelector(
},
)
-export const selectQueuedTransactionById = createSelector(
- selectQueuedTransactions,
- (_: RootState, txId?: string) => txId,
- (queuedTransactions, txId?: string) => {
- return (queuedTransactions || []).find((item) => item.transaction.id === txId)
- },
-)
-
export const txQueueListener = (listenerMiddleware: typeof listenerMiddlewareInstance) => {
listenerMiddleware.startListening({
actionCreator: txQueueSlice.actions.set,