From 32c926372af96fe2f0fa6febae8e6bfcd18ed313 Mon Sep 17 00:00:00 2001
From: katspaugh <381895+katspaugh@users.noreply.github.com>
Date: Thu, 19 Dec 2024 13:58:55 +0100
Subject: [PATCH] Feat: public transaction notes
---
.../transactions/TxDetails/TxNote.tsx | 44 +++++++++++++++++++
.../transactions/TxDetails/index.tsx | 3 ++
.../SignOrExecuteForm/SignOrExecuteForm.tsx | 26 ++++++++++-
.../tx/SignOrExecuteForm/TxNoteForm.tsx | 39 ++++++++++++++++
src/hooks/useTransactionType.tsx | 16 +++----
5 files changed, 119 insertions(+), 9 deletions(-)
create mode 100644 src/components/transactions/TxDetails/TxNote.tsx
create mode 100644 src/components/tx/SignOrExecuteForm/TxNoteForm.tsx
diff --git a/src/components/transactions/TxDetails/TxNote.tsx b/src/components/transactions/TxDetails/TxNote.tsx
new file mode 100644
index 0000000000..e174acc8f6
--- /dev/null
+++ b/src/components/transactions/TxDetails/TxNote.tsx
@@ -0,0 +1,44 @@
+import { DataRow } from '@/components/common/Table/DataRow'
+import useAsync from '@/hooks/useAsync'
+import { useCurrentChain } from '@/hooks/useChains'
+import { isMultisigDetailedExecutionInfo } from '@/utils/transaction-guards'
+import { Box, Divider } from '@mui/material'
+import { TransactionDetails } from '@safe-global/safe-gateway-typescript-sdk'
+
+const TxNote = ({ txDetails }: { txDetails: TransactionDetails }) => {
+ const currentChain = useCurrentChain()
+ const txService = currentChain?.transactionService
+
+ let safeTxHash = ''
+ if (isMultisigDetailedExecutionInfo(txDetails.detailedExecutionInfo)) {
+ safeTxHash = txDetails.detailedExecutionInfo?.safeTxHash
+ }
+
+ const [data] = useAsync(() => {
+ if (!safeTxHash || !txService) return
+ return fetch(`${txService}/api/v1/multisig-transactions/${safeTxHash}`).then((res) => res.json())
+ }, [safeTxHash, txService])
+
+ let note = ''
+ if (data) {
+ try {
+ const origin = JSON.parse(data.origin)
+ const parsedName = JSON.parse(origin.name)
+ note = parsedName.note
+ } catch {
+ // Ignore, no note
+ }
+ }
+
+ return note ? (
+ <>
+
+ {note}
+
+
+
+ >
+ ) : null
+}
+
+export default TxNote
diff --git a/src/components/transactions/TxDetails/index.tsx b/src/components/transactions/TxDetails/index.tsx
index 412df2815d..ac3211fe00 100644
--- a/src/components/transactions/TxDetails/index.tsx
+++ b/src/components/transactions/TxDetails/index.tsx
@@ -36,6 +36,7 @@ import { FEATURES } from '@/utils/chains'
import { useGetTransactionDetailsQuery } from '@/store/api/gateway'
import { asError } from '@/services/exceptions/utils'
import { POLLING_INTERVAL } from '@/config/constants'
+import TxNote from './TxNote'
export const NOT_AVAILABLE = 'n/a'
@@ -82,6 +83,8 @@ const TxDetailsBlock = ({ txSummary, txDetails }: TxDetailsProps): ReactElement
<>
{/* /Details */}
+
+
diff --git a/src/components/tx/SignOrExecuteForm/SignOrExecuteForm.tsx b/src/components/tx/SignOrExecuteForm/SignOrExecuteForm.tsx
index f5944f1a52..d0f22c7c79 100644
--- a/src/components/tx/SignOrExecuteForm/SignOrExecuteForm.tsx
+++ b/src/components/tx/SignOrExecuteForm/SignOrExecuteForm.tsx
@@ -41,6 +41,7 @@ import ConfirmationView from '../confirmation-views'
import { SignerForm } from './SignerForm'
import { useSigner } from '@/hooks/wallets/useWallet'
import { isNestedConfirmationTxInfo } from '@/utils/transaction-guards'
+import TxNoteForm from './TxNoteForm'
export type SubmitCallback = (txId: string, isExecuted?: boolean) => void
@@ -142,6 +143,7 @@ export const SignOrExecuteForm = ({
isCreation?: boolean
txDetails?: TransactionDetails
}): ReactElement => {
+ const [customOrigin, setCustomOrigin] = useState
(props.origin)
const { transactionExecution } = useAppSelector(selectSettings)
const [shouldExecute, setShouldExecute] = useState(transactionExecution)
const isNewExecutableTx = useImmediatelyExecutable() && isCreation
@@ -204,6 +206,19 @@ export const SignOrExecuteForm = ({
[onFormSubmit],
)
+ const onNoteSubmit = useCallback(
+ (note: string) => {
+ const originalOrigin = props.origin ? JSON.parse(props.origin) : { url: location.origin }
+ setCustomOrigin(
+ JSON.stringify({
+ ...originalOrigin,
+ name: JSON.stringify({ note }),
+ }),
+ )
+ },
+ [setCustomOrigin, props.origin],
+ )
+
return (
<>
@@ -230,6 +245,8 @@ export const SignOrExecuteForm = ({
{!isCounterfactualSafe && !props.isRejection && }
+ {isCreation && }
+
@@ -264,7 +281,13 @@ export const SignOrExecuteForm = ({
)}
{!isCounterfactualSafe && willExecute && !isProposing && (
-
+
)}
{!isCounterfactualSafe && willExecuteThroughRole && (
void }) => {
+ const [note, setNote] = useState('')
+
+ const onFormSubmit = (e: React.FormEvent) => {
+ e.preventDefault()
+ const formData = new FormData(e.currentTarget)
+ const value = formData.get('note') as string
+ if (value) {
+ onSubmit(value)
+ setNote(value)
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+
+export default TxNoteForm
diff --git a/src/hooks/useTransactionType.tsx b/src/hooks/useTransactionType.tsx
index fa2f74d48b..066c22a82b 100644
--- a/src/hooks/useTransactionType.tsx
+++ b/src/hooks/useTransactionType.tsx
@@ -111,7 +111,14 @@ export const getTransactionType = (tx: TransactionSummary, addressBook: AddressB
}
}
case TransactionInfoType.CUSTOM: {
- if (isMultiSendTxInfo(tx.txInfo) && !tx.safeAppInfo) {
+ if (tx.safeAppInfo) {
+ return {
+ icon: tx.safeAppInfo.logoUri,
+ text: tx.safeAppInfo.name,
+ }
+ }
+
+ if (isMultiSendTxInfo(tx.txInfo)) {
return {
icon: ,
text: 'Batch',
@@ -145,13 +152,6 @@ export const getTransactionType = (tx: TransactionSummary, addressBook: AddressB
}
}
default: {
- if (tx.safeAppInfo) {
- return {
- icon: tx.safeAppInfo.logoUri,
- text: tx.safeAppInfo.name,
- }
- }
-
return {
icon: '/images/transactions/custom.svg',
text: addressBookName || 'Contract interaction',