Skip to content

Commit

Permalink
Merge pull request #26442 from brave/feat-wallet-zcash-text-memo
Browse files Browse the repository at this point in the history
feat(wallet): ZCash Text Memo
  • Loading branch information
Douglashdaniel authored Nov 8, 2024
2 parents eea7c32 + 568cacd commit e07362f
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 7 deletions.
6 changes: 6 additions & 0 deletions components/brave_wallet/browser/brave_wallet_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,12 @@ inline constexpr webui::LocalizedString kLocalizedStrings[] = {
IDS_BRAVE_WALLET_TRANSACTION_DETAIL_HASH},
{"braveWalletTransactionDetailNetwork",
IDS_BRAVE_WALLET_TRANSACTION_DETAIL_NETWORK},
{"braveWalletMemo", IDS_BRAVE_WALLET_MEMO},
{"braveWalletEnterAMessage", IDS_BRAVE_WALLET_ENTER_A_MESSAGE},
{"braveWalletMessageOptional", IDS_BRAVE_WALLET_MESSAGE_OPTIONAL},
{"braveWalletAddMemo", IDS_BRAVE_WALLET_ADD_MEMO},
{"braveWalletRemoveMemo", IDS_BRAVE_WALLET_REMOVE_MEMO},
{"braveWalletMemoLengthError", IDS_BRAVE_WALLET_MEMO_LENGTH_ERROR},
{"braveWalletTransactionPlaceholder",
IDS_BRAVE_WALLET_TRANSACTION_PLACEHOLDER},
{"braveWalletTransactionApproveUnlimited",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ export const transactionEndpoints = ({
const zecTxData: BraveWallet.ZecTxData = {
useShieldedPool: payload.useShieldedPool,
to: payload.to,
memo: undefined,
memo: payload.memo,
amount: BigInt(payload.value),
fee: BigInt(0),
inputs: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,12 @@ export const TransactionDetailsModal = ({ onClose, transaction }: Props) => {
accountInfosRegistry
)

const memoFromTransaction = transaction.txDataUnion.zecTxData?.memo

const memoText = String.fromCharCode(
...memoFromTransaction ?? []
)

// render
return (
<PopupModal
Expand Down Expand Up @@ -591,6 +597,26 @@ export const TransactionDetailsModal = ({ onClose, transaction }: Props) => {
</HeroContent>
</HeroContainer>

{memoText && (
<>
<SectionRow padding='16px 0px'>
<SectionLabel
textAlign='left'
textSize='14px'
>
{getLocale('braveWalletMemo')}
</SectionLabel>
<SectionInfoText
textAlign='left'
textSize='14px'
>
{memoText}
</SectionInfoText>
</SectionRow>
<VerticalDivider />
</>
)}

{transaction.txHash && (
<>
<SectionRow padding='16px 0px'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export const TransactionInfo = ({
? 'braveWalletConfirmTransactionTransactionFee'
: 'braveWalletConfirmTransactionGasFee'

const memoText = String.fromCharCode(
...transactionDetails.zcashMemo ?? []
)

// render
return (
<>
Expand Down Expand Up @@ -263,6 +267,14 @@ export const TransactionInfo = ({
</TransactionTypeText>
</>
)}

{memoText && (
<SectionColumn>
<Divider />
<TransactionTitle>{getLocale('braveWalletMemo')}</TransactionTitle>
<TransactionTypeText>{memoText}</TransactionTypeText>
</SectionColumn>
)}
</>
)
}
Expand Down
1 change: 1 addition & 0 deletions components/brave_wallet_ui/constants/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ export interface SendBtcTransactionParams extends BaseTransactionParams {

export interface SendZecTransactionParams extends BaseTransactionParams {
useShieldedPool: boolean
memo: number[] | undefined
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

import styled from 'styled-components'
import Input from '@brave/leo/react/input'

export const MemoInput = styled(Input)`
display: flex;
flex-direction: column;
width: 100%;
gap: 4px;
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

import * as React from 'react'
import Button from '@brave/leo/react/button'
import Icon from '@brave/leo/react/icon'

// Constants
import { MAX_ZCASH_MEMO_LENGTH } from '../../constants/magics'

// Utils
import { getLocale } from '../../../../../../common/locale'

// Styled Components
import { Column, Text } from '../../../../../components/shared/style'
import { MemoInput } from './add_memo.style'

interface Props {
memoText: string
onUpdateMemoText: (value: string) => void
}

export const AddMemo = (props: Props) => {
const { memoText, onUpdateMemoText } = props

// State
const [showMemoTextInput, setShowMemoTextInput] =
React.useState<boolean>(false)

// Memos
const memoTextLength = React.useMemo(() => {
return memoText.length
}, [memoText])

// Methods
const onAddOrRemoveTextMemo = React.useCallback(() => {
if (showMemoTextInput) {
onUpdateMemoText('')
setShowMemoTextInput(false)
return
}
setShowMemoTextInput(true)
}, [showMemoTextInput, onUpdateMemoText])

return (
<Column
fullWidth={true}
alignItems='flex-start'
gap='8px'
padding='16px 0px 0px 0px'
>
{showMemoTextInput && (
<MemoInput
value={memoText}
onInput={(e) => onUpdateMemoText(e.value)}
placeholder={getLocale('braveWalletEnterAMessage')}
showErrors={memoText.length > MAX_ZCASH_MEMO_LENGTH}
>
<Text
textSize='12px'
isBold={true}
textColor='primary'
>
{getLocale('braveWalletMessageOptional')}
</Text>
<span slot='extra'>
{memoTextLength}/{MAX_ZCASH_MEMO_LENGTH}
</span>
<Text
textSize='12px'
isBold={true}
textColor='error'
slot='errors'
textAlign='left'
>
{getLocale('braveWalletMemoLengthError')}
</Text>
</MemoInput>
)}
<Button
kind='plain'
size='medium'
onClick={onAddOrRemoveTextMemo}
>
<Icon
name={showMemoTextInput ? 'trash' : 'plus-add'}
slot='icon-before'
/>
{showMemoTextInput
? getLocale('braveWalletRemoveMemo')
: getLocale('braveWalletAddMemo')}
</Button>
</Column>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) 2024 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

export const MAX_ZCASH_MEMO_LENGTH = 512
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import { skipToken } from '@reduxjs/toolkit/query/react'
import { useHistory, useLocation } from 'react-router'

// Selectors
import { useSafeUISelector } from '../../../../common/hooks/use-safe-selector'
import { UISelectors } from '../../../../common/selectors'
import {
useSafeUISelector,
useSafeWalletSelector
} from '../../../../common/hooks/use-safe-selector'
import { UISelectors, WalletSelectors } from '../../../../common/selectors'

// Types
import {
Expand All @@ -22,6 +25,9 @@ import {
AmountValidationErrorType
} from '../../../../constants/types'

// Constants
import { MAX_ZCASH_MEMO_LENGTH } from '../constants/magics'

// Utils
import { getLocale } from '../../../../../common/locale'
import Amount from '../../../../utils/amount'
Expand Down Expand Up @@ -52,7 +58,8 @@ import {
useSendSolTransactionMutation,
useSendFilTransactionMutation,
useSendBtcTransactionMutation,
useSendZecTransactionMutation
useSendZecTransactionMutation,
useValidateUnifiedAddressQuery
} from '../../../../common/slices/api.slice'
import {
useAccountFromAddressQuery //
Expand Down Expand Up @@ -86,6 +93,7 @@ import {
import {
SelectAddressButton //
} from '../../composer_ui/select_address_button/select_address_button'
import { AddMemo } from '../components/add_memo/add_memo'

interface Props {
isAndroid?: boolean
Expand Down Expand Up @@ -125,9 +133,13 @@ export const SendScreen = React.memo((props: Props) => {
React.useState<string>('')
const [isWarningAcknowledged, setIsWarningAcknowledged] =
React.useState<boolean>(false)
const [memoText, setMemoText] = React.useState<string>('')

// Selectors
const isPanel = useSafeUISelector(UISelectors.isPanel)
const isZCashShieldedTransactionsEnabled = useSafeWalletSelector(
WalletSelectors.isZCashShieldedTransactionsEnabled
)

// Mutations
const [sendSPLTransfer] = useSendSPLTransferMutation()
Expand All @@ -147,6 +159,20 @@ export const SendScreen = React.memo((props: Props) => {
})
})

const {
data: zecAddressValidationResult = BraveWallet.ZCashAddressValidationResult
.Unknown
} = useValidateUnifiedAddressQuery(
networkFromParams?.coin === BraveWallet.CoinType.ZEC &&
isZCashShieldedTransactionsEnabled &&
toAddressOrUrl
? {
address: toAddressOrUrl,
testnet: networkFromParams.chainId === BraveWallet.Z_CASH_TESTNET
}
: skipToken
)

const tokenFromParams = React.useMemo(() => {
if (!networkFromParams) {
return
Expand Down Expand Up @@ -416,14 +442,17 @@ export const SendScreen = React.memo((props: Props) => {
}

case BraveWallet.CoinType.ZEC: {
const memoArray =
memoText !== '' ? new TextEncoder().encode(memoText) : undefined
await sendZecTransaction({
useShieldedPool: tokenFromParams.isShielded,
network: networkFromParams,
fromAccount,
to: toAddress,
value: new Amount(sendAmount)
.multiplyByDecimals(tokenFromParams.decimals)
.toHex()
.toHex(),
memo: memoArray ? Array.from(memoArray) : undefined
})
resetSendFields()
}
Expand All @@ -437,6 +466,7 @@ export const SendScreen = React.memo((props: Props) => {
sendingMaxAmount,
sendAmount,
resolvedDomainAddress,
memoText,
resetSendFields,
sendEvmTransaction,
sendERC20Transfer,
Expand Down Expand Up @@ -557,12 +587,23 @@ export const SendScreen = React.memo((props: Props) => {
onChange={setIsWarningAcknowledged}
/>
)}
{isZCashShieldedTransactionsEnabled &&
tokenFromParams?.coin === BraveWallet.CoinType.ZEC &&
toAddressOrUrl &&
zecAddressValidationResult ===
BraveWallet.ZCashAddressValidationResult.ValidShielded && (
<AddMemo
memoText={memoText}
onUpdateMemoText={setMemoText}
/>
)}
</Column>
<ReviewButtonRow width='100%'>
<LeoSquaredButton
onClick={submitSend}
size='large'
isDisabled={
memoText.length > MAX_ZCASH_MEMO_LENGTH ||
!toAddressOrUrl ||
insufficientFundsError ||
sendAmount === '' ||
Expand Down
8 changes: 8 additions & 0 deletions components/brave_wallet_ui/stories/locale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,14 @@ provideStrings({
braveWalletTransactionDetailHash: 'Transaction hash',
braveWalletTransactionDetailNetwork: 'Network',

// Transaction Memo
braveWalletMemo: 'Memo',
braveWalletEnterAMessage: 'You can enter a message...',
braveWalletMessageOptional: 'Message (Optional)',
braveWalletAddMemo: 'Add memo',
braveWalletRemoveMemo: 'Remove memo',
braveWalletMemoLengthError: 'Memo must be less than 512 characters long.',

// Transactions Status
braveWalletTransactionStatusUnapproved: 'Unapproved',
braveWalletTransactionStatusApproved: 'Approved',
Expand Down
7 changes: 5 additions & 2 deletions components/brave_wallet_ui/utils/tx-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export interface ParsedTransaction
// Solana Specific
isAssociatedTokenAccountCreation: boolean
hasSystemProgramAssignInstruction: boolean
zcashMemo: number[] | undefined
}

export type ParsedTransactionWithoutFiatValues = Omit<
Expand Down Expand Up @@ -1745,7 +1746,8 @@ export const parseTransactionWithoutPrices = ({
weiTransferredValue,
formattedSendCurrencyTotal,
isAssociatedTokenAccountCreation: isAssociatedTokenAccountCreationTx(tx),
hasSystemProgramAssignInstruction: hasSystemProgramAssignInstruction(tx)
hasSystemProgramAssignInstruction: hasSystemProgramAssignInstruction(tx),
zcashMemo: tx.txDataUnion.zecTxData?.memo ?? undefined
}
}

Expand Down Expand Up @@ -1809,7 +1811,8 @@ export const parseTransactionWithPrices = ({
token,
txNetwork: transactionNetwork,
transferredValueWei: weiTransferredValue
})
}),
zcashMemo: tx.txDataUnion.zecTxData?.memo ?? undefined
}
}

Expand Down
6 changes: 6 additions & 0 deletions components/resources/wallet_strings.grdp
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,12 @@
<message name="IDS_BRAVE_WALLET_RECENT_TRANSACTIONS" desc="Transactions panel title">Recent transactions</message>
<message name="IDS_BRAVE_WALLET_TRANSACTION_DETAIL_HASH" desc="Transaction Detail Panel Transaction Hash">Transaction hash</message>
<message name="IDS_BRAVE_WALLET_TRANSACTION_DETAIL_NETWORK" desc="Transaction Detail Panel Blockchain Network">Network</message>
<message name="IDS_BRAVE_WALLET_MEMO" desc="Transaction Details Memo label">Memo</message>
<message name="IDS_BRAVE_WALLET_ENTER_A_MESSAGE" desc="Transaction memo input placeholder">You can enter a message...</message>
<message name="IDS_BRAVE_WALLET_MESSAGE_OPTIONAL" desc="Transaction memo input label">Message (Optional)</message>
<message name="IDS_BRAVE_WALLET_ADD_MEMO" desc="Transaction Add memo button text">Add memo</message>
<message name="IDS_BRAVE_WALLET_REMOVE_MEMO" desc="Transaction Remove memo button text">Remove memo</message>
<message name="IDS_BRAVE_WALLET_MEMO_LENGTH_ERROR" desc="Transaction memo error">Memo must be less than 512 characters long.</message>
<message name="IDS_BRAVE_WALLET_EDIT_GAS_TITLE1" desc="Edit gas panel max fee label">Max priority fee</message>
<message name="IDS_BRAVE_WALLET_EDIT_GAS_TITLE2" desc="Edit gas panel edit gas button">Edit gas</message>
<message name="IDS_BRAVE_WALLET_EDIT_GAS_DESCRIPTION" desc="Edit gas panel description">While not a guarantee, miners will likely prioritize your transaction if you pay a higher fee.</message>
Expand Down

0 comments on commit e07362f

Please sign in to comment.