Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Enable spending limit txs in new flow #2160

Merged
merged 2 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions src/components/address-book/AddressBookTable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react'
import { useContext, useMemo, useState } from 'react'
import { Box } from '@mui/material'
import EnhancedTable from '@/components/common/EnhancedTable'
import type { AddressEntry } from '@/components/address-book/EntryDialog'
Expand All @@ -21,8 +21,8 @@ import PagePlaceholder from '@/components/common/PagePlaceholder'
import NoEntriesIcon from '@/public/images/address-book/no-entries.svg'
import { useCurrentChain } from '@/hooks/useChains'
import tableCss from '@/components/common/EnhancedTable/styles.module.css'
import TokenTransferModal from '@/components/tx/modals/TokenTransferModal'
import { SendAssetsField } from '@/components/tx/modals/TokenTransferModal/SendAssetsForm'
import { TxModalContext } from '@/components/tx-flow'
import TokenTransferFlow from '@/components/tx-flow/flows/TokenTransfer'
import CheckWallet from '@/components/common/CheckWallet'

const headCells = [
Expand All @@ -47,10 +47,11 @@ const defaultOpen = {

const AddressBookTable = () => {
const chain = useCurrentChain()
const { setTxFlow } = useContext(TxModalContext)

const [open, setOpen] = useState<typeof defaultOpen>(defaultOpen)
const [searchQuery, setSearchQuery] = useState('')
const [defaultValues, setDefaultValues] = useState<AddressEntry | undefined>(undefined)
const [selectedAddress, setSelectedAddress] = useState<string | undefined>()

const handleOpenModal = (type: keyof typeof open) => () => {
setOpen((prev) => ({ ...prev, [type]: true }))
Expand Down Expand Up @@ -117,7 +118,7 @@ const AddressBookTable = () => {
variant="contained"
color="primary"
size="small"
onClick={() => setSelectedAddress(address)}
onClick={() => setTxFlow(<TokenTransferFlow recipient={address} />)}
disabled={!isOk}
>
Send
Expand Down Expand Up @@ -165,14 +166,6 @@ const AddressBookTable = () => {
)}

{open[ModalType.REMOVE] && <RemoveDialog handleClose={handleClose} address={defaultValues?.address || ''} />}

{/* Send funds modal */}
{selectedAddress && (
<TokenTransferModal
onClose={() => setSelectedAddress(undefined)}
initialData={[{ [SendAssetsField.recipient]: selectedAddress }]}
/>
)}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MouseEvent } from 'react'
import { type ReactElement, useState } from 'react'
import { type ReactElement, useContext, useState } from 'react'
import IconButton from '@mui/material/IconButton'
import MoreHorizIcon from '@mui/icons-material/MoreHoriz'
import MenuItem from '@mui/material/MenuItem'
Expand All @@ -8,29 +8,31 @@ import ListItemText from '@mui/material/ListItemText'
import useAddressBook from '@/hooks/useAddressBook'
import EntryDialog from '@/components/address-book/EntryDialog'
import ContextMenu from '@/components/common/ContextMenu'
import TokenTransferModal from '@/components/tx/modals/TokenTransferModal'
import TokenTransferFlow from '@/components/tx-flow/flows/TokenTransfer'
import type { Transfer } from '@safe-global/safe-gateway-typescript-sdk'
import { TransferDirection } from '@safe-global/safe-gateway-typescript-sdk'
import { ZERO_ADDRESS } from '@safe-global/safe-core-sdk/dist/src/utils/constants'
import { isERC20Transfer, isNativeTokenTransfer } from '@/utils/transaction-guards'
import { trackEvent, TX_LIST_EVENTS } from '@/services/analytics'
import { safeFormatUnits } from '@/utils/formatters'
import CheckWallet from '@/components/common/CheckWallet'
import { TxModalContext } from '@/components/tx-flow'

// TODO: No need for an enum anymore
enum ModalType {
SEND_AGAIN = 'SEND_AGAIN',
ADD_TO_AB = 'ADD_TO_AB',
}

const ETHER = 'ether'

const defaultOpen = { [ModalType.SEND_AGAIN]: false, [ModalType.ADD_TO_AB]: false }
const defaultOpen = { [ModalType.ADD_TO_AB]: false }

const TransferActions = ({ address, txInfo }: { address: string; txInfo: Transfer }): ReactElement => {
const [anchorEl, setAnchorEl] = useState<HTMLElement | undefined>()
const [open, setOpen] = useState<typeof defaultOpen>(defaultOpen)
const addressBook = useAddressBook()
const name = addressBook?.[address]
const { setTxFlow } = useContext(TxModalContext)

const handleOpenContextMenu = (e: MouseEvent<HTMLButtonElement, globalThis.MouseEvent>) => {
setAnchorEl(e.currentTarget)
Expand Down Expand Up @@ -75,7 +77,13 @@ const TransferActions = ({ address, txInfo }: { address: string; txInfo: Transfe
{canSendAgain && (
<CheckWallet>
{(isOk) => (
<MenuItem onClick={handleOpenModal(ModalType.SEND_AGAIN, TX_LIST_EVENTS.SEND_AGAIN)} disabled={!isOk}>
<MenuItem
onClick={() => {
handleCloseContextMenu()
setTxFlow(<TokenTransferFlow recipient={recipient} tokenAddress={tokenAddress} amount={amount} />)
}}
disabled={!isOk}
>
<ListItemText>Send again</ListItemText>
</MenuItem>
)}
Expand All @@ -87,10 +95,6 @@ const TransferActions = ({ address, txInfo }: { address: string; txInfo: Transfe
</MenuItem>
</ContextMenu>

{open[ModalType.SEND_AGAIN] && (
<TokenTransferModal onClose={handleCloseModal} initialData={[{ recipient, tokenAddress, amount }]} />
)}

{open[ModalType.ADD_TO_AB] && (
<EntryDialog handleClose={handleCloseModal} defaultValues={{ name, address }} disableAddressInput />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { parseUnits, defaultAbiCoder } from 'ethers/lib/utils'

import AddressBookInput from '@/components/common/AddressBookInput'
import { validateAmount, validateDecimalLength } from '@/utils/validation'
import { AutocompleteItem } from '@/components/tx/modals/TokenTransferModal/SendAssetsForm'
import { AutocompleteItem } from '@/components/tx-flow/flows/TokenTransfer/CreateTokenTransfer'
import useChainId from '@/hooks/useChainId'
import { getResetTimeOptions } from '@/components/transactions/TxDetails/TxData/SpendingLimits'
import NumberField from '@/components/common/NumberField'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Typography, Box } from '@mui/material'

import SpendingLimitLabel from '@/components/common/SpendingLimitLabel'
import { getResetTimeOptions } from '@/components/transactions/TxDetails/TxData/SpendingLimits'
import { TokenTransferReview } from '@/components/tx/modals/TokenTransferModal/ReviewTokenTx'
import { AmountBlock } from '@/components/tx-flow/flows/TokenTransfer/SendAmountBlock'
import SignOrExecuteForm from '@/components/tx/SignOrExecuteForm'
import useBalances from '@/hooks/useBalances'
import useChainId from '@/hooks/useChainId'
Expand Down Expand Up @@ -59,16 +59,16 @@ export const ReviewSpendingLimit = ({ params }: { params: NewSpendingLimitFlowPr
return (
<SignOrExecuteForm onSubmit={onFormSubmit}>
{token && (
<TokenTransferReview amount={params.amount} tokenInfo={token.tokenInfo}>
<AmountBlock amount={params.amount} tokenInfo={token.tokenInfo}>
{!!existingSpendingLimit && (
<>
<Typography color="error" sx={{ textDecoration: 'line-through' }} component="span" fontSize={20}>
{formatVisualAmount(BigNumber.from(existingSpendingLimit.amount), decimals)} {symbol}
<Typography color="error" sx={{ textDecoration: 'line-through' }} component="span">
{formatVisualAmount(BigNumber.from(existingSpendingLimit.amount), decimals)}
</Typography>
{''}
{''}
</>
)}
</TokenTransferReview>
</AmountBlock>
)}
<Typography color={({ palette }) => palette.text.secondary} pb={1}>
Beneficiary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { SpendingLimitState } from '@/store/spendingLimitsSlice'
import { relativeTime } from '@/utils/date'
import { trackEvent, SETTINGS_EVENTS } from '@/services/analytics'
import useBalances from '@/hooks/useBalances'
import { TokenTransferReview } from '@/components/tx/modals/TokenTransferModal/ReviewTokenTx'
import { AmountBlock } from '@/components/tx-flow/flows/TokenTransfer/SendAmountBlock'
import { safeFormatUnits } from '@/utils/formatters'
import SpendingLimitLabel from '@/components/common/SpendingLimitLabel'
import { createTx } from '@/services/tx/tx-sender'
Expand Down Expand Up @@ -49,10 +49,7 @@ export const RemoveSpendingLimit = ({ params }: { params: SpendingLimitState })
return (
<SignOrExecuteForm onSubmit={onFormSubmit}>
{token && (
<TokenTransferReview
amount={safeFormatUnits(params.amount, token.tokenInfo.decimals)}
tokenInfo={token.tokenInfo}
/>
<AmountBlock amount={safeFormatUnits(params.amount, token.tokenInfo.decimals)} tokenInfo={token.tokenInfo} />
)}
<Typography sx={({ palette }) => ({ color: palette.primary.light })}>Beneficiary</Typography>
<EthHashInfo address={params.beneficiary} showCopyButton hasExplorer shortAddress={false} />
Expand Down
31 changes: 17 additions & 14 deletions src/components/tx-flow/flows/TokenTransfer/CreateTokenTransfer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { type ReactElement, useMemo, useState, useCallback } from 'react'
import { type TokenInfo } from '@safe-global/safe-gateway-typescript-sdk'
import { useVisibleBalances } from '@/hooks/useVisibleBalances'
import useAddressBook from '@/hooks/useAddressBook'
import useChainId from '@/hooks/useChainId'
import { getSafeTokenAddress } from '@/components/common/SafeTokenWidget'
import useIsSafeTokenPaused from '@/components/tx/modals/TokenTransferModal/useIsSafeTokenPaused'
import useIsSafeTokenPaused from '@/hooks/useIsSafeTokenPaused'
import useIsOnlySpendingLimitBeneficiary from '@/hooks/useIsOnlySpendingLimitBeneficiary'
import { useAppSelector } from '@/store'
import { selectSpendingLimits } from '@/store/spendingLimitsSlice'
Expand Down Expand Up @@ -39,6 +40,20 @@ import { formatVisualAmount, safeFormatUnits } from '@/utils/formatters'
import commonCss from '@/components/tx-flow/common/styles.module.css'
import css from './styles.module.css'

export const AutocompleteItem = (item: { tokenInfo: TokenInfo; balance: string }): ReactElement => (
<Grid container alignItems="center" gap={1}>
<TokenIcon logoUri={item.tokenInfo.logoUri} tokenSymbol={item.tokenInfo.symbol} />

<Grid item xs>
<Typography variant="body2">{item.tokenInfo.name}</Typography>

<Typography variant="caption" component="p">
{formatVisualAmount(item.balance, item.tokenInfo.decimals)} {item.tokenInfo.symbol}
</Typography>
</Grid>
</Grid>
)

const CreateTokenTransfer = ({
params,
onSubmit,
Expand Down Expand Up @@ -201,19 +216,7 @@ const CreateTokenTransfer = ({
>
{balancesItems.map((item) => (
<MenuItem key={item.tokenInfo.address} value={item.tokenInfo.address}>
<Grid container alignItems="center" gap={1}>
<TokenIcon logoUri={item.tokenInfo.logoUri} tokenSymbol={item.tokenInfo.symbol} />

<Grid item xs>
<Typography variant="body2" lineHeight="18px">
{item.tokenInfo.name}
</Typography>

<Typography variant="caption" component="p">
{formatVisualAmount(item.balance, item.tokenInfo.decimals)} {item.tokenInfo.symbol}
</Typography>
</Grid>
</Grid>
<AutocompleteItem {...item} />
</MenuItem>
))}
</TextField>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { ReactElement, SyntheticEvent } from 'react'
import { useMemo, useState } from 'react'
import type { BigNumberish, BytesLike } from 'ethers'
import { Button, DialogContent, Typography } from '@mui/material'
import SendFromBlock from '@/components/tx/SendFromBlock'
import SendToBlock from '@/components/tx/SendToBlock'
import type { TokenTransferModalProps } from '.'
import { TokenTransferReview } from '@/components/tx/modals/TokenTransferModal/ReviewTokenTx'
import { Button, CardActions, Typography } from '@mui/material'
import SendToBlock from '@/components/tx-flow/flows/TokenTransfer/SendToBlock'
import { type TokenTransferParams } from '@/components/tx-flow/flows/TokenTransfer/index'
import SendAmountBlock from '@/components/tx-flow/flows/TokenTransfer/SendAmountBlock'
import useBalances from '@/hooks/useBalances'
import useSpendingLimit from '@/hooks/useSpendingLimit'
import useSpendingLimitGas from '@/hooks/useSpendingLimitGas'
Expand All @@ -22,6 +21,7 @@ import { MODALS_EVENTS, trackEvent } from '@/services/analytics'
import useOnboard from '@/hooks/wallets/useOnboard'
import { WrongChainWarning } from '@/components/tx/WrongChainWarning'
import { asError } from '@/services/exceptions/utils'
import TxCard from '@/components/tx-flow/common/TxCard'

export type SpendingLimitTxParams = {
safeAddress: string
Expand All @@ -34,7 +34,13 @@ export type SpendingLimitTxParams = {
signature: BytesLike
}

const ReviewSpendingLimitTx = ({ params, onSubmit }: TokenTransferModalProps): ReactElement => {
const ReviewSpendingLimitTx = ({
params,
onSubmit,
}: {
params: TokenTransferParams
onSubmit: () => void
}): ReactElement => {
const [isSubmittable, setIsSubmittable] = useState<boolean>(true)
const [submitError, setSubmitError] = useState<Error | undefined>()
const currentChain = useCurrentChain()
Expand Down Expand Up @@ -67,10 +73,7 @@ const ReviewSpendingLimitTx = ({ params, onSubmit }: TokenTransferModalProps): R

const { gasLimit, gasLimitLoading } = useSpendingLimitGas(txParams)

const [advancedParams, setManualParams] = useAdvancedParams({
gasLimit,
nonce: params.txNonce,
})
const [advancedParams, setManualParams] = useAdvancedParams(gasLimit)

const handleSubmit = async (e: SyntheticEvent) => {
e.preventDefault()
Expand Down Expand Up @@ -99,15 +102,14 @@ const ReviewSpendingLimitTx = ({ params, onSubmit }: TokenTransferModalProps): R

return (
<form onSubmit={handleSubmit}>
<DialogContent>
<Typography variant="body2" mb={4}>
<TxCard>
<Typography variant="body2">
Spending limit transactions only appear in the interface once they are successfully processed and indexed.
Pending transactions can only be viewed in your signer wallet application or under your wallet address on a
Blockchain Explorer.
</Typography>
{token && <TokenTransferReview amount={params.amount} tokenInfo={token.tokenInfo} />}

<SendFromBlock />
{token && <SendAmountBlock amount={params.amount} tokenInfo={token.tokenInfo} />}

<SendToBlock address={params.recipient} />

Expand All @@ -119,14 +121,16 @@ const ReviewSpendingLimitTx = ({ params, onSubmit }: TokenTransferModalProps): R
<ErrorMessage error={submitError}>Error submitting the transaction. Please try again.</ErrorMessage>
)}

<Typography variant="body2" color="primary.light" textAlign="center" mt={3}>
<Typography variant="body2" color="primary.light" textAlign="center">
You&apos;re about to create a transaction and will need to confirm it with your currently connected wallet.
</Typography>

<Button variant="contained" type="submit" disabled={submitDisabled}>
Submit
</Button>
</DialogContent>
<CardActions>
<Button variant="contained" type="submit" disabled={submitDisabled}>
Submit
</Button>
</CardActions>
</TxCard>
</form>
)
}
Expand Down
25 changes: 25 additions & 0 deletions src/components/tx-flow/flows/TokenTransfer/ReviewTokenTx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type ReactElement } from 'react'
import { type TokenTransferParams, TokenTransferType } from '@/components/tx-flow/flows/TokenTransfer/index'
import ReviewTokenTransfer from '@/components/tx-flow/flows/TokenTransfer/ReviewTokenTransfer'
import ReviewSpendingLimitTx from '@/components/tx-flow/flows/TokenTransfer/ReviewSpendingLimitTx'

// TODO: Split this into separate flows
const ReviewTokenTx = ({
params,
onSubmit,
txNonce,
}: {
params: TokenTransferParams
onSubmit: () => void
txNonce?: number
}): ReactElement => {
const isSpendingLimitTx = params.type === TokenTransferType.spendingLimit

return isSpendingLimitTx ? (
<ReviewSpendingLimitTx params={params} onSubmit={onSubmit} />
) : (
<ReviewTokenTransfer params={params} onSubmit={onSubmit} txNonce={txNonce} />
)
}

export default ReviewTokenTx
Loading