Skip to content

Commit

Permalink
Merge branch 'epic-tx-flow' into port-tenderly
Browse files Browse the repository at this point in the history
  • Loading branch information
iamacook committed Jun 26, 2023
2 parents 8491110 + d259685 commit 803425a
Show file tree
Hide file tree
Showing 83 changed files with 714 additions and 815 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@ NEXT_PUBLIC_CYPRESS_MNEMONIC=
# Safe Gelato relay service
NEXT_PUBLIC_SAFE_GELATO_RELAY_SERVICE_URL_PRODUCTION=
NEXT_PUBLIC_SAFE_GELATO_RELAY_SERVICE_URL_STAGING=

# Redefine
NEXT_PUBLIC_REDEFINE_API=
45 changes: 23 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,29 @@ Create a `.env` file with environment variables. You can use the `.env.example`

Here's the list of all the required and optional variables:

| Env variable | | Description |
| ------------------------------------------------------ | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- |
| `NEXT_PUBLIC_INFURA_TOKEN` | **required** | [Infura](https://docs.infura.io/infura/networks/ethereum/how-to/secure-a-project/project-id) RPC API token |
| `NEXT_PUBLIC_SAFE_APPS_INFURA_TOKEN` | optional | Infura token for Safe Apps, falls back to `NEXT_PUBLIC_INFURA_TOKEN` |
| `NEXT_PUBLIC_IS_PRODUCTION` | optional | Set to `true` to build a minified production app |
| `NEXT_PUBLIC_GATEWAY_URL_PRODUCTION` | optional | The base URL for the [Safe Client Gateway](https://github.com/safe-global/safe-client-gateway) |
| `NEXT_PUBLIC_GATEWAY_URL_STAGING` | optional | The base CGW URL on staging |
| `NEXT_PUBLIC_SAFE_VERSION` | optional | The latest version of the Safe contract, defaults to 1.3.0 | |
| `NEXT_PUBLIC_WC_BRIDGE` | optional | [WalletConnect v1](https://docs.walletconnect.com/1.0/bridge-server) bridge URL, falls back to the public WC bridge |
| `NEXT_PUBLIC_WC_PROJECT_ID` | optional | [WalletConnect v2](https://docs.walletconnect.com/2.0/cloud/relay) project ID |
| `NEXT_PUBLIC_TENDERLY_ORG_NAME` | optional | [Tenderly](https://tenderly.co) org name for Transaction Simulation |
| `NEXT_PUBLIC_TENDERLY_PROJECT_NAME` | optional | Tenderly project name |
| `NEXT_PUBLIC_TENDERLY_SIMULATE_ENDPOINT_URL` | optional | Tenderly simulation URL |
| `NEXT_PUBLIC_BEAMER_ID` | optional | [Beamer](https://www.getbeamer.com) is a news feed for in-app announcements |
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID` | optional | [GTM](https://tagmanager.google.com) project id |
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_DEVELOPMENT_AUTH` | optional | Dev GTM key |
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_LATEST_AUTH` | optional | Preview GTM key |
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_LIVE_AUTH` | optional | Production GTM key |
| `NEXT_PUBLIC_SENTRY_DSN` | optional | [Sentry](https://sentry.io) id for tracking runtime errors |
| `NEXT_PUBLIC_SAFE_GELATO_RELAY_SERVICE_URL_PRODUCTION` | optional | [Safe Gelato Relay Service](https://github.com/safe-global/safe-gelato-relay-service) URL to allow relaying transactions via Gelato |
| `NEXT_PUBLIC_SAFE_GELATO_RELAY_SERVICE_URL_STAGING` | optional | Relay URL on staging |
| `NEXT_PUBLIC_IS_OFFICIAL_HOST` | optional | Whether it's the official distribution of the app, or a fork; has legal implications. Set to true only if you also update the legal pages like Imprint and Terms of use |
| Env variable | | Description
| ------------------------------------------------------ | ------------ | -----------
| `NEXT_PUBLIC_INFURA_TOKEN` | **required** | [Infura](https://docs.infura.io/infura/networks/ethereum/how-to/secure-a-project/project-id) RPC API token
| `NEXT_PUBLIC_SAFE_APPS_INFURA_TOKEN` | optional | Infura token for Safe Apps, falls back to `NEXT_PUBLIC_INFURA_TOKEN`
| `NEXT_PUBLIC_IS_PRODUCTION` | optional | Set to `true` to build a minified production app
| `NEXT_PUBLIC_GATEWAY_URL_PRODUCTION` | optional | The base URL for the [Safe Client Gateway](https://github.com/safe-global/safe-client-gateway)
| `NEXT_PUBLIC_GATEWAY_URL_STAGING` | optional | The base CGW URL on staging
| `NEXT_PUBLIC_SAFE_VERSION` | optional | The latest version of the Safe contract, defaults to 1.3.0
| `NEXT_PUBLIC_WC_BRIDGE` | optional | [WalletConnect v1](https://docs.walletconnect.com/1.0/bridge-server) bridge URL, falls back to the public WC bridge
| `NEXT_PUBLIC_WC_PROJECT_ID` | optional | [WalletConnect v2](https://docs.walletconnect.com/2.0/cloud/relay) project ID
| `NEXT_PUBLIC_TENDERLY_ORG_NAME` | optional | [Tenderly](https://tenderly.co) org name for Transaction Simulation
| `NEXT_PUBLIC_TENDERLY_PROJECT_NAME` | optional | Tenderly project name
| `NEXT_PUBLIC_TENDERLY_SIMULATE_ENDPOINT_URL` | optional | Tenderly simulation URL
| `NEXT_PUBLIC_BEAMER_ID` | optional | [Beamer](https://www.getbeamer.com) is a news feed for in-app announcements
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID` | optional | [GTM](https://tagmanager.google.com) project id
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_DEVELOPMENT_AUTH` | optional | Dev GTM key
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_LATEST_AUTH` | optional | Preview GTM key
| `NEXT_PUBLIC_GOOGLE_TAG_MANAGER_LIVE_AUTH` | optional | Production GTM key
| `NEXT_PUBLIC_SENTRY_DSN` | optional | [Sentry](https://sentry.io) id for tracking runtime errors
| `NEXT_PUBLIC_SAFE_GELATO_RELAY_SERVICE_URL_PRODUCTION` | optional | [Safe Gelato Relay Service](https://github.com/safe-global/safe-gelato-relay-service) URL to allow relaying transactions via Gelato
| `NEXT_PUBLIC_SAFE_GELATO_RELAY_SERVICE_URL_STAGING` | optional | Relay URL on staging
| `NEXT_PUBLIC_IS_OFFICIAL_HOST` | optional | Whether it's the official distribution of the app, or a fork; has legal implications. Set to true only if you also update the legal pages like Imprint and Terms of use
| `NEXT_PUBLIC_REDEFINE_API` | optional | Redefine API base URL

If you don't provide some of the optional vars, the corresponding features will be disabled in the UI.

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@emotion/server": "^11.10.0",
"@emotion/styled": "^11.10.0",
"@mui/icons-material": "^5.8.4",
"@mui/material": "^5.11.10",
"@mui/material": "^5.13.5",
"@mui/x-date-pickers": "^5.0.12",
"@reduxjs/toolkit": "^1.9.5",
"@safe-global/safe-apps-sdk": "7.11.0",
Expand Down Expand Up @@ -81,7 +81,7 @@
"react-papaparse": "^4.0.2",
"react-qr-reader": "2.2.1",
"react-redux": "^8.0.5",
"semver": "^7.3.7"
"semver": "^7.5.2"
},
"devDependencies": {
"@next/bundle-analyzer": "^13.1.1",
Expand Down
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
Expand Up @@ -23,7 +23,7 @@
margin-bottom: 8px;
}

@media (max-width: 600px) {
@media (max-width: 599.95px) {
.container td:last-of-type button {
opacity: 1;
}
Expand Down
17 changes: 9 additions & 8 deletions src/components/address-book/EntryDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import DialogActions from '@mui/material/DialogActions'
import DialogContent from '@mui/material/DialogContent'
import type { ReactElement } from 'react'
import type { ReactElement, BaseSyntheticEvent } from 'react'
import { Box, Button, DialogActions, DialogContent } from '@mui/material'
import { FormProvider, useForm } from 'react-hook-form'

import AddressInput from '@/components/common/AddressInput'
Expand Down Expand Up @@ -41,16 +38,20 @@ const EntryDialog = ({

const { handleSubmit, formState } = methods

const onSubmit = (data: AddressEntry) => {
const submitCallback = handleSubmit((data: AddressEntry) => {
dispatch(upsertAddressBookEntry({ ...data, chainId: chainId || currentChainId }))

handleClose()
})

const onSubmit = (e: BaseSyntheticEvent) => {
e.stopPropagation()
submitCallback(e)
}

return (
<ModalDialog open onClose={handleClose} dialogTitle={defaultValues.name ? 'Edit entry' : 'Create entry'}>
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<form onSubmit={onSubmit}>
<DialogContent>
<Box mb={2}>
<NameInput label="Name" autoFocus name="name" required />
Expand Down
2 changes: 1 addition & 1 deletion src/components/balances/AssetsTable/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
gap: var(--space-1);
}

@media (max-width: 600px) {
@media (max-width: 599.95px) {
.container td:last-of-type,
.container th:last-of-type {
display: none;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@media (max-width: 600px) {
@media (max-width: 599.95px) {
.hiddenTokenButton {
display: none;
}
Expand Down
27 changes: 24 additions & 3 deletions src/components/common/AddressBookInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useAddressBook from '@/hooks/useAddressBook'
import AddressInput, { type AddressInputProps } from '../AddressInput'
import EthHashInfo from '../EthHashInfo'
import InfoIcon from '@/public/images/notifications/info.svg'
import EntryDialog from '@/components/address-book/EntryDialog'
import css from './styles.module.css'

const abFilterOptions = createFilterOptions({
Expand All @@ -15,11 +16,12 @@ const abFilterOptions = createFilterOptions({
/**
* Temporary component until revamped safe components are done
*/
const AddressBookInput = ({ name, canAdd, ...props }: AddressInputProps): ReactElement => {
const AddressBookInput = ({ name, canAdd, ...props }: AddressInputProps & { canAdd?: boolean }): ReactElement => {
const addressBook = useAddressBook()
const { setValue, control } = useFormContext()
const addressValue = useWatch({ name, control })
const [open, setOpen] = useState(false)
const [openAddressBook, setOpenAddressBook] = useState<boolean>(false)

const addressBookEntries = Object.entries(addressBook).map(([address, name]) => ({
label: address,
Expand All @@ -35,6 +37,12 @@ const AddressBookInput = ({ name, canAdd, ...props }: AddressInputProps): ReactE
setOpen((value) => !value)
}

const onAddressBookClick = canAdd
? () => {
setOpenAddressBook(true)
}
: undefined

return (
<>
<Autocomplete
Expand Down Expand Up @@ -66,16 +74,29 @@ const AddressBookInput = ({ name, canAdd, ...props }: AddressInputProps): ReactE
name={name}
onOpenListClick={hasVisibleOptions ? handleOpenAutocomplete : undefined}
isAutocompleteOpen={open}
canAdd={canAdd}
onAddressBookClick={onAddressBookClick}
/>
)}
/>
{canAdd ? (
<Typography variant="body2" className={css.unknownAddress}>
<SvgIcon component={InfoIcon} fontSize="small" />
<span>This is an unknown address. Consider adding it to your address book.</span>
<span>
This is an unknown address. You can{' '}
<a role="button" onClick={onAddressBookClick}>
add it to your address book
</a>
.
</span>
</Typography>
) : null}

{openAddressBook && (
<EntryDialog
handleClose={() => setOpenAddressBook(false)}
defaultValues={{ name: '', address: addressValue }}
/>
)}
</>
)
}
Expand Down
6 changes: 6 additions & 0 deletions src/components/common/AddressBookInput/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@
.unknownAddress svg {
height: auto;
}

.unknownAddress a {
color: inherit;
text-decoration: underline;
cursor: pointer;
}
13 changes: 6 additions & 7 deletions src/components/common/AddressInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@ import { cleanInputValue, parsePrefixedAddress } from '@/utils/addresses'
import useDebounce from '@/hooks/useDebounce'
import CaretDownIcon from '@/public/images/common/caret-down.svg'
import SaveAddressIcon from '@/public/images/common/save-address.svg'
import EntryDialog from '@/components/address-book/EntryDialog'
import classnames from 'classnames'
import css from './styles.module.css'

export type AddressInputProps = TextFieldProps & {
name: string
address?: string
canAdd?: boolean
onOpenListClick?: () => void
isAutocompleteOpen?: boolean
validate?: Validate<string>
deps?: string | string[]
onAddressBookClick?: () => void
}

const AddressInput = ({
Expand All @@ -39,7 +38,7 @@ const AddressInput = ({
required = true,
onOpenListClick,
isAutocompleteOpen,
canAdd,
onAddressBookClick,
deps,
...props
}: AddressInputProps): ReactElement => {
Expand All @@ -55,7 +54,6 @@ const AddressInput = ({
const watchedValue = useWatch({ name, control })
const currentShortName = currentChain?.shortName || ''
const [isValidating, setIsValidating] = useState<boolean>(false)
const [open, setOpen] = useState<boolean>(false)

// Fetch an ENS resolution for the current address
const isDomainLookupEnabled = !!currentChain && hasFeature(currentChain, FEATURES.DOMAIN_LOOKUP)
Expand Down Expand Up @@ -91,12 +89,14 @@ const AddressInput = ({
<CircularProgress size={20} />
) : !props.disabled ? (
<>
{canAdd && (
<IconButton onClick={() => setOpen(true)}>
{onAddressBookClick && (
<IconButton onClick={onAddressBookClick}>
<SvgIcon component={SaveAddressIcon} inheritViewBox fontSize="small" color="primary" />
</IconButton>
)}

<ScanQRButton onScan={setAddressValue} />

{onOpenListClick && (
<IconButton
onClick={onOpenListClick}
Expand Down Expand Up @@ -169,7 +169,6 @@ const AddressInput = ({
// Only seems to occur on the `/load` route
value={watchedValue}
/>
{open && <EntryDialog handleClose={() => setOpen(false)} defaultValues={{ name: '', address: watchedValue }} />}
</>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/ConnectWallet/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
gap: var(--space-2);
}

@media (max-width: 600px) {
@media (max-width: 599.95px) {
.buttonContainer button {
font-size: 12px;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/CookieBanner/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
user-select: none;
}

@media (max-width: 600px) {
@media (max-width: 599.95px) {
.container {
right: 0;
bottom: 0;
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/EnhancedTable/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
gap: var(--space-1);
}

@media (max-width: 900px) {
@media (max-width: 899.95px) {
.mobileColumn thead th {
display: none;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/EthHashInfo/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
display: none;
}

@media (max-width: 600px) {
@media (max-width: 599.95px) {
.mobileAddress {
display: inline-block;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/Footer/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
pointer-events: none;
}

@media (max-width: 600px) {
@media (max-width: 599.95px) {
.container li:not(:last-of-type):after {
visibility: hidden;
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/Header/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
flex-shrink: 0;
}

@media (max-width: 900px) {
@media (max-width: 899.95px) {
.logo {
display: none;
}
Expand All @@ -61,7 +61,7 @@
}
}

@media (max-width: 600px) {
@media (max-width: 599.95px) {
.element {
padding: 0 var(--space-1);
}
Expand Down
Loading

0 comments on commit 803425a

Please sign in to comment.