(Feature) - Send erc721 collectible with proxy contact (#1508)
* Removed containsMethodByHash condition check, now we always expect that safeTransferFrom is defined on the erc721 contract * Types * More types * Add try catch on estimateGas * Add try catch on submit transaction * More types * More types * More types * ReviewTx modal props * Fix SendCollectible modal types * Add guard for safeAddress * Move some imports * Fix DispatchReturn types * Fix import of Dispatch * Remove console log * Adds logs * Fix import Co-authored-by: Daniel Sanchez <daniel.sanchez@gnosis.pm>
This commit is contained in:
parent
95d102d337
commit
058fec3dbc
|
@ -42,7 +42,7 @@ import { Transaction, TransactionStatus, TxArgs } from 'src/logic/safe/store/mod
|
|||
import { AnyAction } from 'redux'
|
||||
import { PayableTx } from 'src/types/contracts/types.d'
|
||||
import { AppReduxState } from 'src/store'
|
||||
import { Dispatch } from './types'
|
||||
import { Dispatch, DispatchReturn } from './types'
|
||||
|
||||
export const removeTxFromStore = (
|
||||
tx: Transaction,
|
||||
|
@ -110,7 +110,7 @@ interface CreateTransactionArgs {
|
|||
safeTxGas?: number
|
||||
}
|
||||
|
||||
type CreateTransactionAction = ThunkAction<Promise<void>, AppReduxState, undefined, AnyAction>
|
||||
type CreateTransactionAction = ThunkAction<Promise<void | string>, AppReduxState, DispatchReturn, AnyAction>
|
||||
type ConfirmEventHandler = (safeTxHash: string) => void
|
||||
type ErrorEventHandler = () => void
|
||||
|
||||
|
@ -129,7 +129,7 @@ const createTransaction = (
|
|||
}: CreateTransactionArgs,
|
||||
onUserConfirm?: ConfirmEventHandler,
|
||||
onError?: ErrorEventHandler,
|
||||
): CreateTransactionAction => async (dispatch: Dispatch, getState: () => AppReduxState): Promise<void> => {
|
||||
): CreateTransactionAction => async (dispatch: Dispatch, getState: () => AppReduxState): Promise<DispatchReturn> => {
|
||||
const state = getState()
|
||||
|
||||
if (navigateToTransactionsTab) {
|
||||
|
|
|
@ -3,4 +3,6 @@ import { AnyAction } from 'redux'
|
|||
|
||||
import { AppReduxState } from 'src/store'
|
||||
|
||||
export type Dispatch = ThunkDispatch<AppReduxState, undefined, AnyAction>
|
||||
export type DispatchReturn = string | undefined
|
||||
|
||||
export type Dispatch = ThunkDispatch<AppReduxState, DispatchReturn, AnyAction>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import Card from '@material-ui/core/Card'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import Item from './components/Item'
|
||||
|
@ -8,78 +8,79 @@ import Item from './components/Item'
|
|||
import Paragraph from 'src/components/layout/Paragraph'
|
||||
import { activeNftAssetsListSelector, nftTokensSelector } from 'src/logic/collectibles/store/selectors'
|
||||
import SendModal from 'src/routes/safe/components/Balances/SendModal'
|
||||
import { safeSelector } from 'src/logic/safe/store/selectors'
|
||||
import { fontColor, lg, screenSm, screenXs } from 'src/theme/variables'
|
||||
import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles.d'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
cardInner: {
|
||||
boxSizing: 'border-box',
|
||||
maxWidth: '100%',
|
||||
padding: '52px 54px',
|
||||
},
|
||||
cardOuter: {
|
||||
boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
|
||||
},
|
||||
gridRow: {
|
||||
boxSizing: 'border-box',
|
||||
columnGap: '30px',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr',
|
||||
marginBottom: '45px',
|
||||
maxWidth: '100%',
|
||||
rowGap: '45px',
|
||||
|
||||
'&:last-child': {
|
||||
marginBottom: '0',
|
||||
const useStyles = makeStyles(
|
||||
createStyles({
|
||||
cardInner: {
|
||||
boxSizing: 'border-box',
|
||||
maxWidth: '100%',
|
||||
padding: '52px 54px',
|
||||
},
|
||||
|
||||
[`@media (min-width: ${screenXs}px)`]: {
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
cardOuter: {
|
||||
boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)',
|
||||
},
|
||||
gridRow: {
|
||||
boxSizing: 'border-box',
|
||||
columnGap: '30px',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr',
|
||||
marginBottom: '45px',
|
||||
maxWidth: '100%',
|
||||
rowGap: '45px',
|
||||
|
||||
[`@media (min-width: ${screenSm}px)`]: {
|
||||
gridTemplateColumns: '1fr 1fr 1fr 1fr',
|
||||
'&:last-child': {
|
||||
marginBottom: '0',
|
||||
},
|
||||
|
||||
[`@media (min-width: ${screenXs}px)`]: {
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
},
|
||||
|
||||
[`@media (min-width: ${screenSm}px)`]: {
|
||||
gridTemplateColumns: '1fr 1fr 1fr 1fr',
|
||||
},
|
||||
},
|
||||
},
|
||||
title: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
margin: '0 0 18px',
|
||||
},
|
||||
titleImg: {
|
||||
backgroundPosition: '50% 50%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: 'contain',
|
||||
borderRadius: '50%',
|
||||
height: '45px',
|
||||
margin: '0 10px 0 0',
|
||||
width: '45px',
|
||||
},
|
||||
titleText: {
|
||||
color: fontColor,
|
||||
fontSize: '18px',
|
||||
fontWeight: 'normal',
|
||||
lineHeight: '1.2',
|
||||
margin: '0',
|
||||
},
|
||||
titleFiller: {
|
||||
backgroundColor: '#e8e7e6',
|
||||
flexGrow: '1',
|
||||
height: '2px',
|
||||
marginLeft: '40px',
|
||||
},
|
||||
noData: {
|
||||
fontSize: lg,
|
||||
textAlign: 'center',
|
||||
},
|
||||
} as any)
|
||||
title: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
margin: '0 0 18px',
|
||||
},
|
||||
titleImg: {
|
||||
backgroundPosition: '50% 50%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: 'contain',
|
||||
borderRadius: '50%',
|
||||
height: '45px',
|
||||
margin: '0 10px 0 0',
|
||||
width: '45px',
|
||||
},
|
||||
titleText: {
|
||||
color: fontColor,
|
||||
fontSize: '18px',
|
||||
fontWeight: 'normal',
|
||||
lineHeight: '1.2',
|
||||
margin: '0',
|
||||
},
|
||||
titleFiller: {
|
||||
backgroundColor: '#e8e7e6',
|
||||
flexGrow: 1,
|
||||
height: '2px',
|
||||
marginLeft: '40px',
|
||||
},
|
||||
noData: {
|
||||
fontSize: lg,
|
||||
textAlign: 'center',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
const Collectibles = (): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const [selectedToken, setSelectedToken] = React.useState({})
|
||||
const [selectedToken, setSelectedToken] = React.useState<NFTToken | undefined>()
|
||||
const [sendNFTsModalOpen, setSendNFTsModalOpen] = React.useState(false)
|
||||
const { address, ethBalance, name } = useSelector(safeSelector) || {}
|
||||
const nftTokens = useSelector(nftTokensSelector)
|
||||
const activeAssetsList = useSelector(activeNftAssetsListSelector)
|
||||
const { trackEvent } = useAnalytics()
|
||||
|
@ -88,7 +89,7 @@ const Collectibles = (): React.ReactElement => {
|
|||
trackEvent({ category: SAFE_NAVIGATION_EVENT, action: 'Collectibles' })
|
||||
}, [trackEvent])
|
||||
|
||||
const handleItemSend = (nftToken) => {
|
||||
const handleItemSend = (nftToken: NFTToken) => {
|
||||
setSelectedToken(nftToken)
|
||||
setSendNFTsModalOpen(true)
|
||||
}
|
||||
|
@ -125,11 +126,8 @@ const Collectibles = (): React.ReactElement => {
|
|||
</div>
|
||||
<SendModal
|
||||
activeScreenType="sendCollectible"
|
||||
ethBalance={ethBalance}
|
||||
isOpen={sendNFTsModalOpen}
|
||||
onClose={() => setSendNFTsModalOpen(false)}
|
||||
safeAddress={address}
|
||||
safeName={name}
|
||||
selectedToken={selectedToken}
|
||||
/>
|
||||
</Card>
|
||||
|
|
|
@ -4,6 +4,14 @@ import cn from 'classnames'
|
|||
import React, { Suspense, useEffect, useState } from 'react'
|
||||
|
||||
import Modal from 'src/components/Modal'
|
||||
import { CollectibleTx } from './screens/ReviewCollectible'
|
||||
import { CustomTx } from './screens/ContractInteraction/ReviewCustomTx'
|
||||
import { SendFundsTx } from './screens/SendFunds'
|
||||
import { ContractInteractionTx } from './screens/ContractInteraction'
|
||||
import { CustomTxProps } from './screens/ContractInteraction/SendCustomTx'
|
||||
import { ReviewTxProp } from './screens/ReviewTx'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles'
|
||||
import { SendCollectibleTxInfo } from './screens/SendCollectible'
|
||||
|
||||
const ChooseTxType = React.lazy(() => import('./screens/ChooseTxType'))
|
||||
|
||||
|
@ -39,10 +47,24 @@ const useStyles = makeStyles({
|
|||
},
|
||||
})
|
||||
|
||||
const SendModal = ({ activeScreenType, isOpen, onClose, recipientAddress, selectedToken }: any) => {
|
||||
type Props = {
|
||||
activeScreenType: string
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
recipientAddress?: string
|
||||
selectedToken?: string | NFTToken
|
||||
}
|
||||
|
||||
const SendModal = ({
|
||||
activeScreenType,
|
||||
isOpen,
|
||||
onClose,
|
||||
recipientAddress,
|
||||
selectedToken,
|
||||
}: Props): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const [activeScreen, setActiveScreen] = useState(activeScreenType || 'chooseTxType')
|
||||
const [tx, setTx] = useState({})
|
||||
const [tx, setTx] = useState<unknown>({})
|
||||
const [isABI, setIsABI] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -53,7 +75,7 @@ const SendModal = ({ activeScreenType, isOpen, onClose, recipientAddress, select
|
|||
|
||||
const scalableModalSize = activeScreen === 'chooseTxType'
|
||||
|
||||
const handleTxCreation = (txInfo) => {
|
||||
const handleTxCreation = (txInfo: SendCollectibleTxInfo) => {
|
||||
setActiveScreen('reviewTx')
|
||||
setTx(txInfo)
|
||||
}
|
||||
|
@ -97,22 +119,22 @@ const SendModal = ({ activeScreenType, isOpen, onClose, recipientAddress, select
|
|||
)}
|
||||
{activeScreen === 'sendFunds' && (
|
||||
<SendFunds
|
||||
initialValues={tx}
|
||||
initialValues={tx as SendFundsTx}
|
||||
onClose={onClose}
|
||||
onNext={handleTxCreation}
|
||||
recipientAddress={recipientAddress}
|
||||
selectedToken={selectedToken}
|
||||
selectedToken={selectedToken as string}
|
||||
/>
|
||||
)}
|
||||
{activeScreen === 'reviewTx' && (
|
||||
<ReviewTx onClose={onClose} onPrev={() => setActiveScreen('sendFunds')} tx={tx} />
|
||||
<ReviewTx onClose={onClose} onPrev={() => setActiveScreen('sendFunds')} tx={tx as ReviewTxProp} />
|
||||
)}
|
||||
{activeScreen === 'contractInteraction' && isABI && (
|
||||
<ContractInteraction
|
||||
isABI={isABI}
|
||||
switchMethod={handleSwitchMethod}
|
||||
contractAddress={recipientAddress}
|
||||
initialValues={tx}
|
||||
initialValues={tx as ContractInteractionTx}
|
||||
onClose={onClose}
|
||||
onNext={handleContractInteractionCreation}
|
||||
/>
|
||||
|
@ -122,7 +144,7 @@ const SendModal = ({ activeScreenType, isOpen, onClose, recipientAddress, select
|
|||
)}
|
||||
{activeScreen === 'contractInteraction' && !isABI && (
|
||||
<SendCustomTx
|
||||
initialValues={tx}
|
||||
initialValues={tx as CustomTxProps}
|
||||
isABI={isABI}
|
||||
switchMethod={handleSwitchMethod}
|
||||
onClose={onClose}
|
||||
|
@ -131,7 +153,7 @@ const SendModal = ({ activeScreenType, isOpen, onClose, recipientAddress, select
|
|||
/>
|
||||
)}
|
||||
{activeScreen === 'reviewCustomTx' && (
|
||||
<ReviewCustomTx onClose={onClose} onPrev={() => setActiveScreen('contractInteraction')} tx={tx} />
|
||||
<ReviewCustomTx onClose={onClose} onPrev={() => setActiveScreen('contractInteraction')} tx={tx as CustomTx} />
|
||||
)}
|
||||
{activeScreen === 'sendCollectible' && (
|
||||
<SendCollectible
|
||||
|
@ -139,11 +161,15 @@ const SendModal = ({ activeScreenType, isOpen, onClose, recipientAddress, select
|
|||
onClose={onClose}
|
||||
onNext={handleSendCollectible}
|
||||
recipientAddress={recipientAddress}
|
||||
selectedToken={selectedToken}
|
||||
selectedToken={selectedToken as NFTToken}
|
||||
/>
|
||||
)}
|
||||
{activeScreen === 'reviewCollectible' && (
|
||||
<ReviewCollectible onClose={onClose} onPrev={() => setActiveScreen('sendCollectible')} tx={tx} />
|
||||
<ReviewCollectible
|
||||
onClose={onClose}
|
||||
onPrev={() => setActiveScreen('sendCollectible')}
|
||||
tx={tx as CollectibleTx}
|
||||
/>
|
||||
)}
|
||||
</Suspense>
|
||||
</Modal>
|
||||
|
|
|
@ -23,7 +23,7 @@ type ActiveScreen = 'sendFunds' | 'sendCollectible' | 'contractInteraction'
|
|||
|
||||
interface ChooseTxTypeProps {
|
||||
onClose: () => void
|
||||
recipientAddress: string
|
||||
recipientAddress?: string
|
||||
setActiveScreen: React.Dispatch<React.SetStateAction<ActiveScreen>>
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { fromTokenUnit, toTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
|
||||
|
||||
import { getNetworkInfo } from 'src/config'
|
||||
import { fromTokenUnit, toTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
|
||||
import AddressInfo from 'src/components/AddressInfo'
|
||||
import Block from 'src/components/layout/Block'
|
||||
import Button from 'src/components/layout/Button'
|
||||
|
@ -43,7 +43,6 @@ type Props = {
|
|||
const { nativeCoin } = getNetworkInfo()
|
||||
|
||||
const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
|
||||
const { enqueueSnackbar, closeSnackbar } = useSnackbar()
|
||||
const classes = useStyles()
|
||||
const dispatch = useDispatch()
|
||||
const { address: safeAddress } = useSelector(safeSelector) || {}
|
||||
|
@ -74,18 +73,19 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE
|
|||
const txRecipient = tx.contractAddress
|
||||
const txData = tx.data ? tx.data.trim() : ''
|
||||
const txValue = tx.value ? toTokenUnit(tx.value, nativeCoin.decimals) : '0'
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress,
|
||||
to: txRecipient,
|
||||
valueInWei: txValue,
|
||||
txData,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
} as any),
|
||||
)
|
||||
|
||||
if (safeAddress) {
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress,
|
||||
to: txRecipient as string,
|
||||
valueInWei: txValue,
|
||||
txData,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
console.error('There was an error trying to submit the transaction, the safeAddress was not found')
|
||||
}
|
||||
onClose()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { fromTokenUnit, toTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
|
||||
import { getNetworkInfo } from 'src/config'
|
||||
|
||||
import { getNetworkInfo } from 'src/config'
|
||||
import { fromTokenUnit, toTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
|
||||
import CopyBtn from 'src/components/CopyBtn'
|
||||
import EtherscanBtn from 'src/components/EtherscanBtn'
|
||||
import Identicon from 'src/components/Identicon'
|
||||
|
@ -30,10 +30,16 @@ import ArrowDown from '../../assets/arrow-down.svg'
|
|||
|
||||
import { styles } from './style'
|
||||
|
||||
export type CustomTx = {
|
||||
contractAddress?: string
|
||||
data?: string
|
||||
value?: string
|
||||
}
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
onPrev: () => void
|
||||
tx: { contractAddress?: string; data?: string; value?: string }
|
||||
tx: CustomTx
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
@ -72,15 +78,19 @@ const ReviewCustomTx = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
|
|||
const txData = tx.data ? tx.data.trim() : ''
|
||||
const txValue = tx.value ? toTokenUnit(tx.value, nativeCoin.decimals) : '0'
|
||||
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress: safeAddress as string,
|
||||
to: txRecipient as string,
|
||||
valueInWei: txValue,
|
||||
txData,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
}),
|
||||
)
|
||||
if (safeAddress) {
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress: safeAddress,
|
||||
to: txRecipient as string,
|
||||
valueInWei: txValue,
|
||||
txData,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
console.error('There was an error trying to submit the transaction, the safeAddress was not found')
|
||||
}
|
||||
|
||||
onClose()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import InputAdornment from '@material-ui/core/InputAdornment'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Switch from '@material-ui/core/Switch'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import React, { useState } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import QRIcon from 'src/assets/icons/qrcode.svg'
|
||||
import CopyBtn from 'src/components/CopyBtn'
|
||||
|
@ -40,13 +40,17 @@ export interface CreatedTx {
|
|||
value: string | number
|
||||
}
|
||||
|
||||
export type CustomTxProps = {
|
||||
contractAddress?: string
|
||||
}
|
||||
|
||||
type Props = {
|
||||
initialValues: { contractAddress?: string }
|
||||
initialValues: CustomTxProps
|
||||
onClose: () => void
|
||||
onNext: (tx: CreatedTx, submit: boolean) => void
|
||||
isABI: boolean
|
||||
switchMethod: () => void
|
||||
contractAddress: string
|
||||
contractAddress?: string
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
|
|
@ -31,9 +31,13 @@ export interface CreatedTx {
|
|||
value: string | number
|
||||
}
|
||||
|
||||
export type ContractInteractionTx = {
|
||||
contractAddress?: string
|
||||
}
|
||||
|
||||
export interface ContractInteractionProps {
|
||||
contractAddress: string
|
||||
initialValues: { contractAddress?: string }
|
||||
contractAddress?: string
|
||||
initialValues: ContractInteractionTx
|
||||
isABI: boolean
|
||||
onClose: () => void
|
||||
switchMethod: () => void
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import { withSnackbar } from 'notistack'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
|
||||
import { getNetworkInfo } from 'src/config'
|
||||
import CopyBtn from 'src/components/CopyBtn'
|
||||
|
@ -21,11 +21,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
|||
import { safeSelector } from 'src/logic/safe/store/selectors'
|
||||
import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
||||
import { estimateTxGasCosts } from 'src/logic/safe/transactions/gas'
|
||||
import {
|
||||
containsMethodByHash,
|
||||
getERC721TokenContract,
|
||||
getHumanFriendlyToken,
|
||||
} from 'src/logic/tokens/store/actions/fetchTokens'
|
||||
import { getERC721TokenContract } from 'src/logic/tokens/store/actions/fetchTokens'
|
||||
import { formatAmount } from 'src/logic/tokens/utils/formatAmount'
|
||||
import { SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH } from 'src/logic/tokens/utils/tokenHelpers'
|
||||
import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo'
|
||||
|
@ -39,9 +35,22 @@ import { styles } from './style'
|
|||
|
||||
const { nativeCoin } = getNetworkInfo()
|
||||
|
||||
const useStyles = makeStyles(styles as any)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const ReviewCollectible = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
|
||||
export type CollectibleTx = {
|
||||
recipientAddress: string
|
||||
assetAddress: string
|
||||
assetName: string
|
||||
nftTokenId: string
|
||||
}
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
onPrev: () => void
|
||||
tx: CollectibleTx
|
||||
}
|
||||
|
||||
const ReviewCollectible = ({ onClose, onPrev, tx }: Props): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const shortener = textShortener()
|
||||
const dispatch = useDispatch()
|
||||
|
@ -57,22 +66,24 @@ const ReviewCollectible = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx
|
|||
let isCurrent = true
|
||||
|
||||
const estimateGas = async () => {
|
||||
const supportsSafeTransfer = await containsMethodByHash(tx.assetAddress, SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH)
|
||||
const methodToCall = supportsSafeTransfer ? `0x${SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH}` : 'transfer'
|
||||
const transferParams = [tx.recipientAddress, tx.nftTokenId]
|
||||
const params = methodToCall === 'transfer' ? transferParams : [safeAddress, ...transferParams]
|
||||
try {
|
||||
const methodToCall = `0x${SAFE_TRANSFER_FROM_WITHOUT_DATA_HASH}`
|
||||
const transferParams = [tx.recipientAddress, tx.nftTokenId]
|
||||
const params = [safeAddress, ...transferParams]
|
||||
const ERC721Token = await getERC721TokenContract()
|
||||
const tokenInstance = await ERC721Token.at(tx.assetAddress)
|
||||
const txData = tokenInstance.contract.methods[methodToCall](...params).encodeABI()
|
||||
|
||||
const ERC721Token = methodToCall === 'transfer' ? await getHumanFriendlyToken() : await getERC721TokenContract()
|
||||
const tokenInstance = await ERC721Token.at(tx.assetAddress)
|
||||
const txData = tokenInstance.contract.methods[methodToCall](...params).encodeABI()
|
||||
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, tx.recipientAddress, txData)
|
||||
const gasCosts = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals)
|
||||
const formattedGasCosts = formatAmount(gasCosts)
|
||||
|
||||
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, tx.recipientAddress, txData)
|
||||
const gasCosts = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals)
|
||||
const formattedGasCosts = formatAmount(gasCosts)
|
||||
|
||||
if (isCurrent) {
|
||||
setGasCosts(formattedGasCosts)
|
||||
setData(txData)
|
||||
if (isCurrent) {
|
||||
setGasCosts(formattedGasCosts)
|
||||
setData(txData)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error while calculating estimated gas:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,18 +95,25 @@ const ReviewCollectible = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx
|
|||
}, [safeAddress, tx.assetAddress, tx.nftTokenId, tx.recipientAddress])
|
||||
|
||||
const submitTx = async () => {
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress,
|
||||
to: tx.assetAddress,
|
||||
valueInWei: '0',
|
||||
txData: data,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
} as any),
|
||||
)
|
||||
onClose()
|
||||
try {
|
||||
if (safeAddress) {
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress,
|
||||
to: tx.assetAddress,
|
||||
valueInWei: '0',
|
||||
txData: data,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
console.error('There was an error trying to submit the transaction, the safeAddress was not found')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating sendCollectible Tx:', error)
|
||||
} finally {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -180,4 +198,4 @@ const ReviewCollectible = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx
|
|||
)
|
||||
}
|
||||
|
||||
export default withSnackbar(ReviewCollectible)
|
||||
export default ReviewCollectible
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { lg, md, secondaryText, sm } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
heading: {
|
||||
padding: `${md} ${lg}`,
|
||||
justifyContent: 'flex-start',
|
||||
|
|
|
@ -2,7 +2,6 @@ import IconButton from '@material-ui/core/IconButton'
|
|||
import { makeStyles } from '@material-ui/core/styles'
|
||||
import Close from '@material-ui/icons/Close'
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { withSnackbar } from 'notistack'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { toTokenUnit, fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue'
|
||||
|
@ -34,11 +33,24 @@ import ArrowDown from '../assets/arrow-down.svg'
|
|||
|
||||
import { styles } from './style'
|
||||
|
||||
const useStyles = makeStyles(styles as any)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const { nativeCoin } = getNetworkInfo()
|
||||
|
||||
const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
|
||||
export type ReviewTxProp = {
|
||||
recipientAddress: string
|
||||
amount: string
|
||||
txRecipient: string
|
||||
token: string
|
||||
}
|
||||
|
||||
type ReviewTxProps = {
|
||||
onClose: () => void
|
||||
onPrev: () => void
|
||||
tx: ReviewTxProp
|
||||
}
|
||||
|
||||
const ReviewTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const dispatch = useDispatch()
|
||||
const { address: safeAddress } = useSelector(safeSelector) || {}
|
||||
|
@ -69,7 +81,7 @@ const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
|
|||
txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, txAmount).encodeABI()
|
||||
}
|
||||
|
||||
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, txRecipient, txData)
|
||||
const estimatedGasCosts = await estimateTxGasCosts(safeAddress as string, txRecipient as string, txData)
|
||||
const gasCosts = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals)
|
||||
const formattedGasCosts = formatAmount(gasCosts)
|
||||
|
||||
|
@ -92,17 +104,19 @@ const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
|
|||
// if txAmount > 0 it would send ETH from the Safe
|
||||
const txAmount = isSendingETH ? toTokenUnit(tx.amount, nativeCoin.decimals) : '0'
|
||||
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress,
|
||||
to: txRecipient,
|
||||
valueInWei: txAmount,
|
||||
txData: data,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
} as any),
|
||||
)
|
||||
if (safeAddress) {
|
||||
dispatch(
|
||||
createTransaction({
|
||||
safeAddress: safeAddress,
|
||||
to: txRecipient as string,
|
||||
valueInWei: txAmount,
|
||||
txData: data,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.STANDARD_TX,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
console.error('There was an error trying to submit the transaction, the safeAddress was not found')
|
||||
}
|
||||
onClose()
|
||||
}
|
||||
|
||||
|
@ -196,4 +210,4 @@ const ReviewTx = ({ closeSnackbar, enqueueSnackbar, onClose, onPrev, tx }) => {
|
|||
)
|
||||
}
|
||||
|
||||
export default withSnackbar(ReviewTx)
|
||||
export default ReviewTx
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { lg, md, secondaryText, sm } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
heading: {
|
||||
padding: `${md} ${lg}`,
|
||||
justifyContent: 'flex-start',
|
||||
|
|
|
@ -28,6 +28,7 @@ import { sm } from 'src/theme/variables'
|
|||
import ArrowDown from '../assets/arrow-down.svg'
|
||||
|
||||
import { styles } from './style'
|
||||
import { NFTToken } from 'src/logic/collectibles/sources/collectibles'
|
||||
|
||||
const formMutators = {
|
||||
setMax: (args, state, utils) => {
|
||||
|
@ -43,13 +44,28 @@ const formMutators = {
|
|||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
type SendCollectibleProps = {
|
||||
initialValues: any
|
||||
onClose: () => void
|
||||
onNext: (txInfo: SendCollectibleTxInfo) => void
|
||||
recipientAddress?: string
|
||||
selectedToken: NFTToken
|
||||
}
|
||||
|
||||
export type SendCollectibleTxInfo = {
|
||||
assetAddress: string
|
||||
assetName: string
|
||||
nftTokenId: string
|
||||
recipientAddress?: string
|
||||
}
|
||||
|
||||
const SendCollectible = ({
|
||||
initialValues,
|
||||
onClose,
|
||||
onNext,
|
||||
recipientAddress,
|
||||
selectedToken = {},
|
||||
}): React.ReactElement => {
|
||||
selectedToken,
|
||||
}: SendCollectibleProps): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const nftAssets = useSelector(safeActiveSelectorMap)
|
||||
const nftTokens = useSelector(nftTokensSelector)
|
||||
|
@ -67,7 +83,7 @@ const SendCollectible = ({
|
|||
}
|
||||
}, [selectedEntry, pristine])
|
||||
|
||||
const handleSubmit = (values) => {
|
||||
const handleSubmit = (values: SendCollectibleTxInfo) => {
|
||||
// If the input wasn't modified, there was no mutation of the recipientAddress
|
||||
if (!values.recipientAddress) {
|
||||
values.recipientAddress = selectedEntry?.address
|
||||
|
|
|
@ -47,18 +47,20 @@ const formMutators = {
|
|||
},
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles as any)
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
export type SendFundsTx = {
|
||||
amount?: string
|
||||
recipientAddress?: string
|
||||
token?: string
|
||||
}
|
||||
|
||||
type SendFundsProps = {
|
||||
initialValues: {
|
||||
amount?: string
|
||||
recipientAddress?: string
|
||||
token?: string
|
||||
}
|
||||
initialValues: SendFundsTx
|
||||
onClose: () => void
|
||||
onNext: (txInfo: unknown) => void
|
||||
recipientAddress: string
|
||||
selectedToken: string
|
||||
recipientAddress?: string
|
||||
selectedToken?: string
|
||||
}
|
||||
|
||||
const { nativeCoin } = getNetworkInfo()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { lg, md, secondaryText } from 'src/theme/variables'
|
||||
import { createStyles } from '@material-ui/core'
|
||||
|
||||
export const styles = () => ({
|
||||
export const styles = createStyles({
|
||||
heading: {
|
||||
padding: `${md} ${lg}`,
|
||||
justifyContent: 'flex-start',
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { withSnackbar } from 'notistack'
|
||||
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
|
@ -14,11 +13,12 @@ import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
|||
import addSafeOwner from 'src/logic/safe/store/actions/addSafeOwner'
|
||||
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||
|
||||
import { safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||
import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors'
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types'
|
||||
|
||||
const styles = () => ({
|
||||
const styles = createStyles({
|
||||
biggerModalWindow: {
|
||||
width: '775px',
|
||||
minHeight: '500px',
|
||||
|
@ -26,7 +26,15 @@ const styles = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const sendAddOwner = async (values, safeAddress, ownersOld, enqueueSnackbar, closeSnackbar, dispatch) => {
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
type OwnerValues = {
|
||||
ownerAddress: string
|
||||
ownerName: string
|
||||
threshold: string
|
||||
}
|
||||
|
||||
export const sendAddOwner = async (values: OwnerValues, safeAddress: string, dispatch: Dispatch): Promise<void> => {
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const txData = gnosisSafe.methods.addOwnerWithThreshold(values.ownerAddress, values.threshold).encodeABI()
|
||||
|
||||
|
@ -37,9 +45,7 @@ export const sendAddOwner = async (values, safeAddress, ownersOld, enqueueSnackb
|
|||
valueInWei: '0',
|
||||
txData,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
} as any),
|
||||
}),
|
||||
)
|
||||
|
||||
if (txHash) {
|
||||
|
@ -47,12 +53,17 @@ export const sendAddOwner = async (values, safeAddress, ownersOld, enqueueSnackb
|
|||
}
|
||||
}
|
||||
|
||||
const AddOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose }) => {
|
||||
type Props = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const AddOwner = ({ isOpen, onClose }: Props): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const [activeScreen, setActiveScreen] = useState('selectOwner')
|
||||
const [values, setValues] = useState<any>({})
|
||||
const dispatch = useDispatch()
|
||||
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
||||
const owners = useSelector(safeOwnersSelector)
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
|
@ -91,7 +102,7 @@ const AddOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose })
|
|||
onClose()
|
||||
|
||||
try {
|
||||
await sendAddOwner(values, safeAddress, owners, enqueueSnackbar, closeSnackbar, dispatch)
|
||||
await sendAddOwner(values, safeAddress, dispatch)
|
||||
dispatch(
|
||||
addOrUpdateAddressBookEntry(makeAddressBookEntry({ name: values.ownerName, address: values.ownerAddress })),
|
||||
)
|
||||
|
@ -121,4 +132,4 @@ const AddOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose })
|
|||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(withSnackbar(AddOwner))
|
||||
export default AddOwner
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { withSnackbar } from 'notistack'
|
||||
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
|
@ -13,13 +12,10 @@ import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions'
|
|||
import createTransaction from 'src/logic/safe/store/actions/createTransaction'
|
||||
import removeSafeOwner from 'src/logic/safe/store/actions/removeSafeOwner'
|
||||
|
||||
import {
|
||||
safeOwnersSelector,
|
||||
safeParamAddressFromStateSelector,
|
||||
safeThresholdSelector,
|
||||
} from 'src/logic/safe/store/selectors'
|
||||
import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types'
|
||||
|
||||
const styles = () => ({
|
||||
const styles = createStyles({
|
||||
biggerModalWindow: {
|
||||
width: '775px',
|
||||
minHeight: '500px',
|
||||
|
@ -27,17 +23,22 @@ const styles = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
type OwnerValues = {
|
||||
ownerAddress: string
|
||||
ownerName: string
|
||||
threshold: string
|
||||
}
|
||||
|
||||
export const sendRemoveOwner = async (
|
||||
values,
|
||||
safeAddress,
|
||||
ownerAddressToRemove,
|
||||
ownerNameToRemove,
|
||||
ownersOld,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
threshold,
|
||||
dispatch,
|
||||
) => {
|
||||
values: OwnerValues,
|
||||
safeAddress: string,
|
||||
ownerAddressToRemove: string,
|
||||
ownerNameToRemove: string,
|
||||
dispatch: Dispatch,
|
||||
threshold?: number,
|
||||
): Promise<void> => {
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const safeOwners = await gnosisSafe.methods.getOwners().call()
|
||||
const index = safeOwners.findIndex(
|
||||
|
@ -53,9 +54,7 @@ export const sendRemoveOwner = async (
|
|||
valueInWei: '0',
|
||||
txData,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
} as any),
|
||||
}),
|
||||
)
|
||||
|
||||
if (txHash && threshold === 1) {
|
||||
|
@ -63,11 +62,18 @@ export const sendRemoveOwner = async (
|
|||
}
|
||||
}
|
||||
|
||||
const RemoveOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose, ownerAddress, ownerName }) => {
|
||||
type RemoveOwnerProps = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
ownerAddress: string
|
||||
ownerName: string
|
||||
}
|
||||
|
||||
const RemoveOwner = ({ isOpen, onClose, ownerAddress, ownerName }: RemoveOwnerProps): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const [activeScreen, setActiveScreen] = useState('checkOwner')
|
||||
const [values, setValues] = useState<any>({})
|
||||
const dispatch = useDispatch()
|
||||
const owners = useSelector(safeOwnersSelector)
|
||||
const safeAddress = useSelector(safeParamAddressFromStateSelector)
|
||||
const threshold = useSelector(safeThresholdSelector)
|
||||
|
||||
|
@ -99,17 +105,7 @@ const RemoveOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose,
|
|||
|
||||
const onRemoveOwner = () => {
|
||||
onClose()
|
||||
sendRemoveOwner(
|
||||
values,
|
||||
safeAddress,
|
||||
ownerAddress,
|
||||
ownerName,
|
||||
owners,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
threshold,
|
||||
dispatch,
|
||||
)
|
||||
sendRemoveOwner(values, safeAddress, ownerAddress, ownerName, dispatch, threshold)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -142,4 +138,4 @@ const RemoveOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose,
|
|||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(withSnackbar(RemoveOwner))
|
||||
export default RemoveOwner
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { withStyles } from '@material-ui/core/styles'
|
||||
import { withSnackbar } from 'notistack'
|
||||
import { createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
|
@ -15,8 +14,10 @@ import replaceSafeOwner from 'src/logic/safe/store/actions/replaceSafeOwner'
|
|||
import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors'
|
||||
import { checksumAddress } from 'src/utils/checksumAddress'
|
||||
import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook'
|
||||
import { sameAddress } from 'src/logic/wallets/ethAddresses'
|
||||
import { Dispatch } from 'src/logic/safe/store/actions/types'
|
||||
|
||||
const styles = () => ({
|
||||
const styles = createStyles({
|
||||
biggerModalWindow: {
|
||||
width: '775px',
|
||||
minHeight: '500px',
|
||||
|
@ -24,20 +25,24 @@ const styles = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
type OwnerValues = {
|
||||
ownerAddress: string
|
||||
ownerName: string
|
||||
threshold: string
|
||||
}
|
||||
|
||||
export const sendReplaceOwner = async (
|
||||
values,
|
||||
safeAddress,
|
||||
ownerAddressToRemove,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
threshold,
|
||||
dispatch,
|
||||
) => {
|
||||
values: OwnerValues,
|
||||
safeAddress: string,
|
||||
ownerAddressToRemove: string,
|
||||
dispatch: Dispatch,
|
||||
threshold?: number,
|
||||
): Promise<void> => {
|
||||
const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress)
|
||||
const safeOwners = await gnosisSafe.methods.getOwners().call()
|
||||
const index = safeOwners.findIndex(
|
||||
(ownerAddress) => ownerAddress.toLowerCase() === ownerAddressToRemove.toLowerCase(),
|
||||
)
|
||||
const index = safeOwners.findIndex((ownerAddress) => sameAddress(ownerAddress, ownerAddressToRemove))
|
||||
const prevAddress = index === 0 ? SENTINEL_ADDRESS : safeOwners[index - 1]
|
||||
const txData = gnosisSafe.methods.swapOwner(prevAddress, ownerAddressToRemove, values.ownerAddress).encodeABI()
|
||||
|
||||
|
@ -48,9 +53,7 @@ export const sendReplaceOwner = async (
|
|||
valueInWei: '0',
|
||||
txData,
|
||||
notifiedTransaction: TX_NOTIFICATION_TYPES.SETTINGS_CHANGE_TX,
|
||||
enqueueSnackbar,
|
||||
closeSnackbar,
|
||||
} as any),
|
||||
}),
|
||||
)
|
||||
|
||||
if (txHash && threshold === 1) {
|
||||
|
@ -65,7 +68,15 @@ export const sendReplaceOwner = async (
|
|||
}
|
||||
}
|
||||
|
||||
const ReplaceOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose, ownerAddress, ownerName }) => {
|
||||
type ReplaceOwnerProps = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
ownerAddress: string
|
||||
ownerName: string
|
||||
}
|
||||
|
||||
const ReplaceOwner = ({ isOpen, onClose, ownerAddress, ownerName }: ReplaceOwnerProps): React.ReactElement => {
|
||||
const classes = useStyles()
|
||||
const [activeScreen, setActiveScreen] = useState('checkOwner')
|
||||
const [values, setValues] = useState<any>({})
|
||||
const dispatch = useDispatch()
|
||||
|
@ -94,7 +105,7 @@ const ReplaceOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose
|
|||
const onReplaceOwner = async () => {
|
||||
onClose()
|
||||
try {
|
||||
await sendReplaceOwner(values, safeAddress, ownerAddress, enqueueSnackbar, closeSnackbar, threshold, dispatch)
|
||||
await sendReplaceOwner(values, safeAddress, ownerAddress, dispatch, threshold)
|
||||
|
||||
dispatch(
|
||||
addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: values.ownerAddress, name: values.ownerName })),
|
||||
|
@ -131,4 +142,4 @@ const ReplaceOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose
|
|||
)
|
||||
}
|
||||
|
||||
export default withStyles(styles as any)(withSnackbar(ReplaceOwner))
|
||||
export default ReplaceOwner
|
||||
|
|
Loading…
Reference in New Issue