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

Feature/token allowlist #1115

Merged
merged 5 commits into from
Feb 14, 2024
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
1 change: 1 addition & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@shared/api": "1.0.0",
"@shared/constants": "1.0.0",
"@shared/helpers": "1.0.0",
"@stellar-asset-lists/sdk": "^1.0.0",
"@stellar/design-system": "^1.1.2",
"@stellar/wallet-sdk": "v0.11.0-beta.1",
"@testing-library/react": "^10.4.8",
Expand Down
2 changes: 2 additions & 0 deletions extension/src/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ declare module "qrcode.react";
declare module "react-identicons";

declare module "stellar-identicon-js";

declare module "@stellar-asset-lists/sdk";
10 changes: 10 additions & 0 deletions extension/src/popup/assets/icon-add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions extension/src/popup/assets/icon-remove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions extension/src/popup/assets/icon-unverified-warning.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions extension/src/popup/assets/icon-unverified.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
136 changes: 136 additions & 0 deletions extension/src/popup/components/WarningMessages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Operation,
Horizon,
TransactionBuilder,
Networks,
xdr,
} from "stellar-sdk";

Expand All @@ -31,11 +32,13 @@ import {
ManageAssetRow,
NewAssetFlags,
} from "popup/components/manageAssets/ManageAssetRows";
import { SorobanTokenIcon } from "popup/components/account/AccountAssets";
import { View } from "popup/basics/layout/View";
import { useNetworkFees } from "popup/helpers/useNetworkFees";
import {
publicKeySelector,
hardwareWalletTypeSelector,
addTokenId,
} from "popup/ducks/accountServices";
import { ROUTES } from "popup/constants/routes";
import { navigateTo } from "popup/helpers/navigate";
Expand All @@ -46,6 +49,7 @@ import { emitMetric } from "helpers/metrics";
import IconShieldCross from "popup/assets/icon-shield-cross.svg";
import IconInvalid from "popup/assets/icon-invalid.svg";
import IconWarning from "popup/assets/icon-warning.svg";
import IconUnverifiedWarning from "popup/assets/icon-unverified-warning.svg";

import "./styles.scss";
import { INDEXER_URL } from "@shared/constants/mercury";
Expand Down Expand Up @@ -667,6 +671,138 @@ export const NewAssetWarning = ({
);
};

export const UnverifiedTokenWarning = ({
domain,
code,
issuer,
onClose,
}: {
domain: string;
code: string;
issuer: string;
onClose: () => void;
}) => {
const { t } = useTranslation();
const dispatch: AppDispatch = useDispatch();
const warningRef = useRef<HTMLDivElement>(null);
const networkDetails = useSelector(settingsNetworkDetailsSelector);
const publicKey = useSelector(publicKeySelector);
const { submitStatus } = useSelector(transactionSubmissionSelector);
const [isSubmitting, setIsSubmitting] = useState(false);

const closeOverlay = () => {
if (warningRef.current) {
warningRef.current.style.marginBottom = `-${POPUP_HEIGHT}px`;
}
const timeout = setTimeout(() => {
onClose();
clearTimeout(timeout);
}, 300);
};

// animate entry
useEffect(() => {
if (warningRef.current) {
const timeout = setTimeout(() => {
// Adding extra check to fix flaky tests
if (warningRef.current) {
warningRef.current.style.marginBottom = "0";
}
clearTimeout(timeout);
}, 10);
}
}, [warningRef]);

const handleSubmit = async () => {
setIsSubmitting(true);
await dispatch(
addTokenId({
publicKey,
tokenId: issuer,
network: networkDetails.network as Networks,
}),
);
navigateTo(ROUTES.account);

setIsSubmitting(false);
};

return (
<div className="UnverifiedTokenWarning">
<View.Content>
<div className="UnverifiedTokenWarning__wrapper" ref={warningRef}>
<div className="UnverifiedTokenWarning__heading">
<div className="UnverifiedTokenWarning__icon">
<SorobanTokenIcon code={code} noMargin />
</div>
<div className="UnverifiedTokenWarning__code">{code}</div>
<div className="UnverifiedTokenWarning__domain">{domain}</div>
<div className="UnverifiedTokenWarning__description">
<div className="UnverifiedTokenWarning__description__icon">
<Icon.VerifiedUser />
</div>
<div className="UnverifiedTokenWarning__description__text">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we show this even when adding a Soroban token? My thought here is that they don't have trustlines so maybe this inly belongs on classic assets or SACs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, we would show this if a user is adding a contract ID that doesn't appear on this list:
https://api.stellar.expert/explorer/testnet/asset-list/top50

So we just flag that a user is adding a contract ID that has not been vetted

Lmk if that answers your question!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I was mainly curious if the badge could be confused with the concept of having a trustline to a classic asset. As in this case we're "trusting" the asset by means of it being on the list which is different than having a trust line on the classic asset side.

{t("Add Asset Trustline")}
</div>
</div>
</div>

<Notification
title={t(
"Before you add this asset, please double-check its information and characteristics. This can help you identify fraudulent assets.",
)}
variant="warning"
></Notification>
<div className="UnverifiedTokenWarning__flags">
<div className="UnverifiedTokenWarning__flags__info">
{t("Asset Info")}
</div>
<div className="UnverifiedTokenWarning__flag">
<div className="UnverifiedTokenWarning__flag__icon">
<img src={IconUnverifiedWarning} alt="unverified token" />
</div>
<div className="UnverifiedTokenWarning__flag__content">
<div className="UnverifiedTokenWarning__flag__header UnverifiedTokenWarning__flags__icon--unverified">
{t("Unverified asset")}
</div>
<div className="UnverifiedTokenWarning__flag__description">
{t("Proceed with caution")}
</div>
</div>
</div>
</div>

<div className="UnverifiedTokenWarning__bottom-content">
<div className="ScamAssetWarning__btns">
<Button
size="md"
isFullWidth
variant="secondary"
type="button"
onClick={closeOverlay}
>
{t("Cancel")}
</Button>
<Button
size="md"
isFullWidth
onClick={handleSubmit}
type="button"
variant="primary"
isLoading={
isSubmitting || submitStatus === ActionStatus.PENDING
}
>
{t("Add asset")}
</Button>
</div>{" "}
</div>
</div>
</View.Content>
</div>
);
};

export const TransferWarning = ({
authEntry,
}: {
Expand Down
95 changes: 95 additions & 0 deletions extension/src/popup/components/WarningMessages/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,101 @@
}
}

.UnverifiedTokenWarning {
z-index: var(--z-index--scam-warning);
display: flex;
position: fixed;
height: fit-content;
width: 100%;
bottom: -1.5rem;
left: 0;

&__wrapper {
align-items: center;
z-index: calc(var(--z-index--scam-warning) + 1);
height: 100%;
margin-bottom: -600px;
display: flex;
flex-direction: column;
background: var(--color-gray-10);
padding: 2rem 1.5rem 1.5rem 1.5rem;
transition: margin var(--dropdown-animation);
border-top-right-radius: 1rem;
border-top-left-radius: 1rem;
}

&__icon {
align-items: center;
display: flex;
justify-content: center;
margin-bottom: 0.5rem;
}

&__code {
display: flex;
justify-content: center;
}

&__description {
align-items: center;
background: var(--color-gray-40);
border-radius: 0.375rem;
display: flex;
gap: 0.25rem;
margin: 1rem 0 1.5rem 0;
padding: 0.125rem 0.375rem;

&__icon {
svg {
color: var(--color-gray-70);
height: 0.75rem;
width: 0.75rem;
}
}

&__text {
color: var(--color-gray-70);
font-size: 0.75rem;
line-height: 1.125rem;
}
}

&__flags {
border-radius: 0.5rem;
margin-top: 0.5rem;
background: var(--color-gray-20);
padding: 0.75rem 1rem;
width: 100%;

&__info {
font-size: 0.875rem;
color: var(--color-gray-70);
margin-bottom: 0.5rem;
}
}

&__flag {
display: flex;
margin-bottom: 1.5rem;
column-gap: 0.5rem;
&__header {
color: var(--color-yellow-60);
font-size: 0.875rem;
margin-bottom: 0.25rem;
}
&__description {
color: var(--color-gray-70);
font-size: 0.75rem;
line-height: 1.25rem;
}
}

&__bottom-content {
margin-top: 1rem;
width: 100%;
}
}

.TokenTransferWarning {
overflow-wrap: break-word;

Expand Down
22 changes: 17 additions & 5 deletions extension/src/popup/components/account/AccountAssets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ import { formatAmount } from "popup/helpers/formatters";

const getIsXlm = (code: string) => code === "XLM";

export const SorobanTokenIcon = ({
code,
noMargin,
}: {
code: string;
noMargin?: boolean;
}) => (
<div
className={`AccountAssets__asset--logo AccountAssets__asset--soroban-token ${
noMargin ? "AccountAssets__asset--no-margin" : ""
}`}
>
{code.substring(0, 2)}
</div>
);

export const AssetIcon = ({
assetIcons,
code,
Expand Down Expand Up @@ -71,11 +87,7 @@ export const AssetIcon = ({

// Placeholder for Soroban tokens
if (_isSorobanToken) {
return (
<div className="AccountAssets__asset--logo AccountAssets__asset--soroban-token">
S
</div>
);
return <SorobanTokenIcon code={code} />;
}

// If we're waiting on the icon lookup (Method 1), just return the loader until this re-renders with `assetIcons`. We can't do anything until we have it.
Expand Down
11 changes: 8 additions & 3 deletions extension/src/popup/components/account/AccountAssets/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ $loader-light-color: #444961;

&--lp-share,
&--soroban-token {
border: 1px solid var(--color-gray-20);
height: 32px;
width: 32px;
background: var(--color-gray-10);
border-radius: 2rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
font-size: 0.875rem;
font-weight: var(--font-weight-semi-bold);
line-height: 1.5rem;
color: var(--color-gray-90);
color: var(--color-gray-70);
}

&--error {
Expand Down Expand Up @@ -67,6 +68,10 @@ $loader-light-color: #444961;
height: 0;
}
}

&--no-margin {
margin: 0;
}
}

@keyframes loadingAnimation {
Expand Down
Loading
Loading