mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Added a 'Verify contract details' link to SetApprovalForAll confirmation screens (#15756)
This commit is contained in:
parent
a87c1750b0
commit
bf1db006fd
6
app/_locales/en/messages.json
generated
6
app/_locales/en/messages.json
generated
@ -770,6 +770,12 @@
|
|||||||
"contractInteraction": {
|
"contractInteraction": {
|
||||||
"message": "Contract interaction"
|
"message": "Contract interaction"
|
||||||
},
|
},
|
||||||
|
"contractNFT": {
|
||||||
|
"message": "NFT contract"
|
||||||
|
},
|
||||||
|
"contractRequestingAccess": {
|
||||||
|
"message": "Contract requesting access"
|
||||||
|
},
|
||||||
"contractRequestingSpendingCap": {
|
"contractRequestingSpendingCap": {
|
||||||
"message": "Contract requesting spending cap"
|
"message": "Contract requesting spending cap"
|
||||||
},
|
},
|
||||||
|
@ -25,6 +25,8 @@ import {
|
|||||||
import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard';
|
import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard';
|
||||||
import UrlIcon from '../../../ui/url-icon/url-icon';
|
import UrlIcon from '../../../ui/url-icon/url-icon';
|
||||||
import { getAddressBookEntry } from '../../../../selectors';
|
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({
|
export default function ContractDetailsModal({
|
||||||
onClose,
|
onClose,
|
||||||
@ -35,6 +37,9 @@ export default function ContractDetailsModal({
|
|||||||
rpcPrefs,
|
rpcPrefs,
|
||||||
origin,
|
origin,
|
||||||
siteImage,
|
siteImage,
|
||||||
|
tokenId,
|
||||||
|
assetName,
|
||||||
|
assetStandard,
|
||||||
}) {
|
}) {
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const [copiedTokenAddress, handleCopyTokenAddress] = useCopyToClipboard();
|
const [copiedTokenAddress, handleCopyTokenAddress] = useCopyToClipboard();
|
||||||
@ -43,6 +48,12 @@ export default function ContractDetailsModal({
|
|||||||
const addressBookEntry = useSelector((state) => ({
|
const addressBookEntry = useSelector((state) => ({
|
||||||
data: getAddressBookEntry(state, toAddress),
|
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 (
|
return (
|
||||||
<Popover className="contract-details-modal">
|
<Popover className="contract-details-modal">
|
||||||
@ -75,7 +86,7 @@ export default function ContractDetailsModal({
|
|||||||
marginTop={4}
|
marginTop={4}
|
||||||
marginBottom={2}
|
marginBottom={2}
|
||||||
>
|
>
|
||||||
{t('contractToken')}
|
{nft ? t('contractNFT') : t('contractToken')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
@ -84,11 +95,20 @@ export default function ContractDetailsModal({
|
|||||||
borderColor={COLORS.BORDER_DEFAULT}
|
borderColor={COLORS.BORDER_DEFAULT}
|
||||||
className="contract-details-modal__content__contract"
|
className="contract-details-modal__content__contract"
|
||||||
>
|
>
|
||||||
<Identicon
|
{nft ? (
|
||||||
className="contract-details-modal__content__contract__identicon"
|
<Box margin={4}>
|
||||||
address={tokenAddress}
|
<NftCollectionImage
|
||||||
diameter={24}
|
assetName={assetName}
|
||||||
/>
|
tokenAddress={tokenAddress}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Identicon
|
||||||
|
className="contract-details-modal__content__contract__identicon"
|
||||||
|
address={tokenAddress}
|
||||||
|
diameter={24}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Box data-testid="recipient">
|
<Box data-testid="recipient">
|
||||||
<Typography
|
<Typography
|
||||||
fontWeight={FONT_WEIGHT.BOLD}
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
@ -102,6 +122,8 @@ export default function ContractDetailsModal({
|
|||||||
variant={TYPOGRAPHY.H6}
|
variant={TYPOGRAPHY.H6}
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
color={COLORS.TEXT_ALTERNATIVE}
|
color={COLORS.TEXT_ALTERNATIVE}
|
||||||
|
marginTop={0}
|
||||||
|
marginBottom={4}
|
||||||
>
|
>
|
||||||
{ellipsify(tokenAddress)}
|
{ellipsify(tokenAddress)}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -166,7 +188,9 @@ export default function ContractDetailsModal({
|
|||||||
marginTop={4}
|
marginTop={4}
|
||||||
marginBottom={2}
|
marginBottom={2}
|
||||||
>
|
>
|
||||||
{t('contractRequestingSpendingCap')}
|
{nft
|
||||||
|
? t('contractRequestingAccess')
|
||||||
|
: t('contractRequestingSpendingCap')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
@ -175,22 +199,30 @@ export default function ContractDetailsModal({
|
|||||||
borderColor={COLORS.BORDER_DEFAULT}
|
borderColor={COLORS.BORDER_DEFAULT}
|
||||||
className="contract-details-modal__content__contract"
|
className="contract-details-modal__content__contract"
|
||||||
>
|
>
|
||||||
<UrlIcon
|
{nft ? (
|
||||||
className={classnames({
|
<Identicon
|
||||||
'contract-details-modal__content__contract__identicon-for-unknown-contact':
|
className="contract-details-modal__content__contract__identicon"
|
||||||
addressBookEntry?.data?.name === undefined,
|
diameter={24}
|
||||||
'contract-details-modal__content__contract__identicon':
|
address={toAddress}
|
||||||
addressBookEntry?.data?.name !== undefined,
|
/>
|
||||||
})}
|
) : (
|
||||||
fallbackClassName={classnames({
|
<UrlIcon
|
||||||
'contract-details-modal__content__contract__identicon-for-unknown-contact':
|
className={classnames({
|
||||||
addressBookEntry?.data?.name === undefined,
|
'contract-details-modal__content__contract__identicon-for-unknown-contact':
|
||||||
'contract-details-modal__content__contract__identicon':
|
addressBookEntry?.data?.name === undefined,
|
||||||
addressBookEntry?.data?.name !== undefined,
|
'contract-details-modal__content__contract__identicon':
|
||||||
})}
|
addressBookEntry?.data?.name !== undefined,
|
||||||
name={origin}
|
})}
|
||||||
url={siteImage}
|
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">
|
<Box data-testid="recipient">
|
||||||
<Typography
|
<Typography
|
||||||
fontWeight={FONT_WEIGHT.BOLD}
|
fontWeight={FONT_WEIGHT.BOLD}
|
||||||
@ -204,6 +236,8 @@ export default function ContractDetailsModal({
|
|||||||
variant={TYPOGRAPHY.H6}
|
variant={TYPOGRAPHY.H6}
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
color={COLORS.TEXT_ALTERNATIVE}
|
color={COLORS.TEXT_ALTERNATIVE}
|
||||||
|
marginTop={0}
|
||||||
|
marginBottom={4}
|
||||||
>
|
>
|
||||||
{ellipsify(toAddress)}
|
{ellipsify(toAddress)}
|
||||||
</Typography>
|
</Typography>
|
||||||
@ -310,4 +344,16 @@ ContractDetailsModal.propTypes = {
|
|||||||
* Dapp image
|
* Dapp image
|
||||||
*/
|
*/
|
||||||
siteImage: PropTypes.string,
|
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,
|
||||||
};
|
};
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
.contract-details-modal {
|
.contract-details-modal {
|
||||||
width: 360px !important;
|
width: 100% !important;
|
||||||
|
max-width: 408px;
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
border-bottom: 1px solid var(--color-border-muted);
|
border-bottom: 1px solid var(--color-border-muted);
|
||||||
|
|
||||||
&__contract {
|
&__contract {
|
||||||
&__identicon {
|
&__identicon {
|
||||||
margin: 16px 16px 38px 16px;
|
margin: 16px;
|
||||||
|
box-shadow: none;
|
||||||
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__identicon-for-unknown-contact {
|
&__identicon-for-unknown-contact {
|
||||||
|
1
ui/components/ui/nft-collection-image/index.js
Normal file
1
ui/components/ui/nft-collection-image/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './nft-collection-image';
|
7
ui/components/ui/nft-collection-image/index.scss
Normal file
7
ui/components/ui/nft-collection-image/index.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.collection-image-alt {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 2px;
|
||||||
|
background: var(--color-overlay-alternative);
|
||||||
|
}
|
@ -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,
|
||||||
|
};
|
@ -63,3 +63,5 @@
|
|||||||
@import 'deprecated-test-networks/index.scss';
|
@import 'deprecated-test-networks/index.scss';
|
||||||
@import 'contract-token-values/index.scss';
|
@import 'contract-token-values/index.scss';
|
||||||
@import 'nft-info/index.scss';
|
@import 'nft-info/index.scss';
|
||||||
|
@import 'nft-collection-image/index';
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
ERC721,
|
ERC721,
|
||||||
} from '../../../../shared/constants/transaction';
|
} from '../../../../shared/constants/transaction';
|
||||||
import { CHAIN_IDS, TEST_CHAINS } from '../../../../shared/constants/network';
|
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 {
|
export default class ConfirmApproveContent extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -82,6 +83,7 @@ export default class ConfirmApproveContent extends Component {
|
|||||||
state = {
|
state = {
|
||||||
showFullTxDetails: false,
|
showFullTxDetails: false,
|
||||||
copied: false,
|
copied: false,
|
||||||
|
setshowContractDetails: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
renderApproveContentCard({
|
renderApproveContentCard({
|
||||||
@ -588,8 +590,11 @@ export default class ConfirmApproveContent extends Component {
|
|||||||
isContract,
|
isContract,
|
||||||
assetStandard,
|
assetStandard,
|
||||||
userAddress,
|
userAddress,
|
||||||
|
tokenId,
|
||||||
|
tokenAddress,
|
||||||
|
assetName,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { showFullTxDetails } = this.state;
|
const { showFullTxDetails, setshowContractDetails } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -632,6 +637,33 @@ export default class ConfirmApproveContent extends Component {
|
|||||||
<div className="confirm-approve-content__description">
|
<div className="confirm-approve-content__description">
|
||||||
{this.renderDescription()}
|
{this.renderDescription()}
|
||||||
</div>
|
</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 className="confirm-approve-content__address-display-content">
|
||||||
<Box display={DISPLAY.FLEX}>
|
<Box display={DISPLAY.FLEX}>
|
||||||
<Identicon
|
<Identicon
|
||||||
|
@ -102,7 +102,6 @@
|
|||||||
@include H6;
|
@include H6;
|
||||||
|
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
margin-bottom: 16px;
|
|
||||||
color: var(--color-text-alternative);
|
color: var(--color-text-alternative);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-left: 24px;
|
padding-left: 24px;
|
||||||
@ -337,6 +336,13 @@
|
|||||||
color: var(--color-primary-default);
|
color: var(--color-primary-default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__verify-contract-details {
|
||||||
|
@include H6;
|
||||||
|
|
||||||
|
color: var(--color-primary-default);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&__info-row {
|
&__info-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user