1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 01:39:44 +01:00

Added a 'Verify contract details' link to SetApprovalForAll confirmation screens (#15756)

This commit is contained in:
Adnan Sahovic 2022-11-14 20:28:27 +01:00 committed by GitHub
parent a87c1750b0
commit bf1db006fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 175 additions and 27 deletions

View File

@ -770,6 +770,12 @@
"contractInteraction": {
"message": "Contract interaction"
},
"contractNFT": {
"message": "NFT contract"
},
"contractRequestingAccess": {
"message": "Contract requesting access"
},
"contractRequestingSpendingCap": {
"message": "Contract requesting spending cap"
},

View File

@ -25,6 +25,8 @@ import {
import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard';
import UrlIcon from '../../../ui/url-icon/url-icon';
import { getAddressBookEntry } from '../../../../selectors';
import { ERC1155, ERC721 } from '../../../../../shared/constants/transaction';
import NftCollectionImage from '../../../ui/nft-collection-image/nft-collection-image';
export default function ContractDetailsModal({
onClose,
@ -35,6 +37,9 @@ export default function ContractDetailsModal({
rpcPrefs,
origin,
siteImage,
tokenId,
assetName,
assetStandard,
}) {
const t = useI18nContext();
const [copiedTokenAddress, handleCopyTokenAddress] = useCopyToClipboard();
@ -43,6 +48,12 @@ export default function ContractDetailsModal({
const addressBookEntry = useSelector((state) => ({
data: getAddressBookEntry(state, toAddress),
}));
const nft =
assetStandard === ERC721 ||
assetStandard === ERC1155 ||
// if we don't have an asset standard but we do have either both an assetname and a tokenID or both a tokenName and tokenId we assume its an NFT
(assetName && tokenId) ||
(tokenName && tokenId);
return (
<Popover className="contract-details-modal">
@ -75,7 +86,7 @@ export default function ContractDetailsModal({
marginTop={4}
marginBottom={2}
>
{t('contractToken')}
{nft ? t('contractNFT') : t('contractToken')}
</Typography>
<Box
display={DISPLAY.FLEX}
@ -84,11 +95,20 @@ export default function ContractDetailsModal({
borderColor={COLORS.BORDER_DEFAULT}
className="contract-details-modal__content__contract"
>
<Identicon
className="contract-details-modal__content__contract__identicon"
address={tokenAddress}
diameter={24}
/>
{nft ? (
<Box margin={4}>
<NftCollectionImage
assetName={assetName}
tokenAddress={tokenAddress}
/>
</Box>
) : (
<Identicon
className="contract-details-modal__content__contract__identicon"
address={tokenAddress}
diameter={24}
/>
)}
<Box data-testid="recipient">
<Typography
fontWeight={FONT_WEIGHT.BOLD}
@ -102,6 +122,8 @@ export default function ContractDetailsModal({
variant={TYPOGRAPHY.H6}
display={DISPLAY.FLEX}
color={COLORS.TEXT_ALTERNATIVE}
marginTop={0}
marginBottom={4}
>
{ellipsify(tokenAddress)}
</Typography>
@ -166,7 +188,9 @@ export default function ContractDetailsModal({
marginTop={4}
marginBottom={2}
>
{t('contractRequestingSpendingCap')}
{nft
? t('contractRequestingAccess')
: t('contractRequestingSpendingCap')}
</Typography>
<Box
display={DISPLAY.FLEX}
@ -175,22 +199,30 @@ export default function ContractDetailsModal({
borderColor={COLORS.BORDER_DEFAULT}
className="contract-details-modal__content__contract"
>
<UrlIcon
className={classnames({
'contract-details-modal__content__contract__identicon-for-unknown-contact':
addressBookEntry?.data?.name === undefined,
'contract-details-modal__content__contract__identicon':
addressBookEntry?.data?.name !== undefined,
})}
fallbackClassName={classnames({
'contract-details-modal__content__contract__identicon-for-unknown-contact':
addressBookEntry?.data?.name === undefined,
'contract-details-modal__content__contract__identicon':
addressBookEntry?.data?.name !== undefined,
})}
name={origin}
url={siteImage}
/>
{nft ? (
<Identicon
className="contract-details-modal__content__contract__identicon"
diameter={24}
address={toAddress}
/>
) : (
<UrlIcon
className={classnames({
'contract-details-modal__content__contract__identicon-for-unknown-contact':
addressBookEntry?.data?.name === undefined,
'contract-details-modal__content__contract__identicon':
addressBookEntry?.data?.name !== undefined,
})}
fallbackClassName={classnames({
'contract-details-modal__content__contract__identicon-for-unknown-contact':
addressBookEntry?.data?.name === undefined,
'contract-details-modal__content__contract__identicon':
addressBookEntry?.data?.name !== undefined,
})}
name={origin}
url={siteImage}
/>
)}
<Box data-testid="recipient">
<Typography
fontWeight={FONT_WEIGHT.BOLD}
@ -204,6 +236,8 @@ export default function ContractDetailsModal({
variant={TYPOGRAPHY.H6}
display={DISPLAY.FLEX}
color={COLORS.TEXT_ALTERNATIVE}
marginTop={0}
marginBottom={4}
>
{ellipsify(toAddress)}
</Typography>
@ -310,4 +344,16 @@ ContractDetailsModal.propTypes = {
* Dapp image
*/
siteImage: PropTypes.string,
/**
* The token id of the collectible
*/
tokenId: PropTypes.string,
/**
* Token Standard
*/
assetStandard: PropTypes.string,
/**
* The name of the collection
*/
assetName: PropTypes.string,
};

View File

@ -1,12 +1,15 @@
.contract-details-modal {
width: 360px !important;
width: 100% !important;
max-width: 408px;
&__content {
border-bottom: 1px solid var(--color-border-muted);
&__contract {
&__identicon {
margin: 16px 16px 38px 16px;
margin: 16px;
box-shadow: none;
background: none;
}
&__identicon-for-unknown-contact {

View File

@ -0,0 +1 @@
export { default } from './nft-collection-image';

View File

@ -0,0 +1,7 @@
.collection-image-alt {
border-radius: 50%;
width: 24px;
height: 24px;
padding: 2px;
background: var(--color-overlay-alternative);
}

View File

@ -0,0 +1,45 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import Box from '../box';
import { COLORS, TEXT_ALIGN } from '../../../helpers/constants/design-system';
import Identicon from '../identicon';
import { getTokenList } from '../../../selectors';
import { useCollectiblesCollections } from '../../../hooks/useCollectiblesCollections';
export default function NftCollectionImage({ assetName, tokenAddress }) {
const { collections } = useCollectiblesCollections();
const tokenList = useSelector(getTokenList);
const nftTokenListImage = tokenList[tokenAddress.toLowerCase()]?.iconUrl;
const renderCollectionImageOrName = () => {
const collection = Object.values(collections).find(
({ collectionName }) => collectionName === assetName,
);
if (collection?.collectionImage || nftTokenListImage) {
return (
<Identicon
diameter={24}
image={collection?.collectionImage || nftTokenListImage}
/>
);
}
return (
<Box
color={COLORS.OVERLAY_INVERSE}
textAlign={TEXT_ALIGN.CENTER}
className="collection-image-alt"
>
{assetName?.[0]?.toUpperCase() ?? null}
</Box>
);
};
return <Box>{renderCollectionImageOrName()}</Box>;
}
NftCollectionImage.propTypes = {
assetName: PropTypes.string,
tokenAddress: PropTypes.string,
};

View File

@ -63,3 +63,5 @@
@import 'deprecated-test-networks/index.scss';
@import 'contract-token-values/index.scss';
@import 'nft-info/index.scss';
@import 'nft-collection-image/index';

View File

@ -32,6 +32,7 @@ import {
ERC721,
} from '../../../../shared/constants/transaction';
import { CHAIN_IDS, TEST_CHAINS } from '../../../../shared/constants/network';
import ContractDetailsModal from '../../../components/app/modals/contract-details-modal/contract-details-modal';
export default class ConfirmApproveContent extends Component {
static contextTypes = {
@ -82,6 +83,7 @@ export default class ConfirmApproveContent extends Component {
state = {
showFullTxDetails: false,
copied: false,
setshowContractDetails: false,
};
renderApproveContentCard({
@ -588,8 +590,11 @@ export default class ConfirmApproveContent extends Component {
isContract,
assetStandard,
userAddress,
tokenId,
tokenAddress,
assetName,
} = this.props;
const { showFullTxDetails } = this.state;
const { showFullTxDetails, setshowContractDetails } = this.state;
return (
<div
@ -632,6 +637,33 @@ export default class ConfirmApproveContent extends Component {
<div className="confirm-approve-content__description">
{this.renderDescription()}
</div>
{(assetStandard === ERC721 ||
assetStandard === ERC1155 ||
(assetName && tokenId) ||
(tokenSymbol && tokenId)) && (
<Box marginBottom={4} marginTop={2}>
<Button
type="link"
className="confirm-approve-content__verify-contract-details"
onClick={() => this.setState({ setshowContractDetails: true })}
>
{t('verifyContractDetails')}
</Button>
{setshowContractDetails && (
<ContractDetailsModal
onClose={() => this.setState({ setshowContractDetails: false })}
tokenName={tokenSymbol}
tokenAddress={tokenAddress}
toAddress={toAddress}
chainId={chainId}
rpcPrefs={rpcPrefs}
tokenId={tokenId}
assetName={assetName}
assetStandard={assetStandard}
/>
)}
</Box>
)}
<Box className="confirm-approve-content__address-display-content">
<Box display={DISPLAY.FLEX}>
<Identicon

View File

@ -102,7 +102,6 @@
@include H6;
margin-top: 16px;
margin-bottom: 16px;
color: var(--color-text-alternative);
text-align: center;
padding-left: 24px;
@ -337,6 +336,13 @@
color: var(--color-primary-default);
}
&__verify-contract-details {
@include H6;
color: var(--color-primary-default);
padding: 0;
}
&__info-row {
display: flex;
justify-content: space-between;