diff --git a/package-lock.json b/package-lock.json
index 87c0aaf95..03b55f0b9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,7 +17,7 @@
"@reduxjs/toolkit": "^1.6.1",
"@tradetrust-tt/decentralized-renderer-react-components": "^3.14.1",
"@tradetrust-tt/document-store": "^3.2.0",
- "@tradetrust-tt/token-registry": "^4.12.1",
+ "@tradetrust-tt/token-registry": "^4.14.0",
"@tradetrust-tt/tradetrust": "^6.9.4",
"@tradetrust-tt/tradetrust-ui-components": "^2.22.2",
"@tradetrust-tt/tradetrust-utils": "^1.14.1",
@@ -9903,9 +9903,9 @@
}
},
"node_modules/@tradetrust-tt/token-registry": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@tradetrust-tt/token-registry/-/token-registry-4.12.1.tgz",
- "integrity": "sha512-GM/q+pyRSzoRAaHuIxt1W/tr45JXv1CPMwF4nYIZhofDtVJTbLIeQsEnBpzJSyCp4hSQUe1LDAI3ma6SZRMq8A==",
+ "version": "4.15.0",
+ "resolved": "https://registry.npmjs.org/@tradetrust-tt/token-registry/-/token-registry-4.15.0.tgz",
+ "integrity": "sha512-hwi/a8O1ng1Zj+gleg/9HaaggDGe8xxeVB0ghfVctrPFW93YXgPBlrGNPPox/02DmiMOSe5QDxYpRq7ZWgtwfA==",
"dependencies": {
"@typechain/ethers-v5": "10.2.1"
},
diff --git a/package.json b/package.json
index b7f476eae..e86f11ceb 100644
--- a/package.json
+++ b/package.json
@@ -54,7 +54,7 @@
"@reduxjs/toolkit": "^1.6.1",
"@tradetrust-tt/decentralized-renderer-react-components": "^3.14.1",
"@tradetrust-tt/document-store": "^3.2.0",
- "@tradetrust-tt/token-registry": "^4.12.1",
+ "@tradetrust-tt/token-registry": "^4.14.0",
"@tradetrust-tt/tradetrust": "^6.9.4",
"@tradetrust-tt/tradetrust-ui-components": "^2.22.2",
"@tradetrust-tt/tradetrust-utils": "^1.14.1",
diff --git a/src/AppContainer.tsx b/src/AppContainer.tsx
index fc6fa8156..d772eed43 100644
--- a/src/AppContainer.tsx
+++ b/src/AppContainer.tsx
@@ -1,9 +1,11 @@
-import { Overlay } from "@tradetrust-tt/tradetrust-ui-components";
+import { Button, ButtonSize, Overlay } from "@tradetrust-tt/tradetrust-ui-components";
import React, { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import { Footer } from "./components/Layout/Footer";
import { NavigationBar, rightNavItems } from "./components/Layout/NavigationBar";
import { Routes, routes } from "./routes";
+import PopupMessage from "./components/PopupMessage";
+import { URLS } from "./constants";
const AppContainer = (): React.ReactElement => {
const location = useLocation();
@@ -22,6 +24,31 @@ const AppContainer = (): React.ReactElement => {
leftItems={[]}
rightItems={rightNavItems}
/>
+
+
+
+
+
+
+ To serve our community better, we are upgrading to a newer version that has new capabilities to your
+ transferable documents.
+
+
+
+
+
+
= ({
children,
}) => {
diff --git a/src/common/utils/getDropzoneBoxUi.ts b/src/common/utils/getDropzoneBoxUi.ts
index d6acfcd22..1184dcf0a 100644
--- a/src/common/utils/getDropzoneBoxUi.ts
+++ b/src/common/utils/getDropzoneBoxUi.ts
@@ -15,6 +15,7 @@ interface GetDropzoneBoxUi {
isVerificationPending: boolean;
isVerificationError: boolean | null;
isActionError?: boolean | null;
+ isTokenRegistryV5?: boolean;
}
export const getDropzoneBoxUi = ({
@@ -24,6 +25,7 @@ export const getDropzoneBoxUi = ({
isVerificationPending,
isVerificationError,
isActionError,
+ isTokenRegistryV5,
}: GetDropzoneBoxUi): string => {
switch (true) {
case isDragReject:
@@ -35,6 +37,7 @@ export const getDropzoneBoxUi = ({
case isVerificationPending:
return DropzoneBoxUiState.VERIFICATION_PENDING;
case isVerificationError:
+ case isTokenRegistryV5:
return DropzoneBoxUiState.VERIFICATION_ERROR;
case isActionError:
return DropzoneBoxUiState.ACTION_ERROR;
diff --git a/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx b/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx
index e675edcd1..258dd3a55 100644
--- a/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementApplication/index.tsx
@@ -8,6 +8,7 @@ import { AssetManagementTags } from "../AssetManagementTags";
import { DocumentStatus } from "../../DocumentStatus";
import { constants } from "@tradetrust-tt/token-registry";
import { useTokenRegistryRole } from "../../../common/hooks/useTokenRegistryRole";
+import { AssetInformationPanel } from "../AssetInformationPanel";
interface AssetManagementApplicationProps {
isMagicDemo?: boolean;
@@ -79,7 +80,17 @@ export const AssetManagementApplication: FunctionComponent
-
+
>
)}
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/AcceptSurrenderedForm/AcceptSurrenderedForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/AcceptSurrenderedForm/AcceptSurrenderedForm.tsx
index 59750d509..e29c977a5 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/AcceptSurrenderedForm/AcceptSurrenderedForm.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/AcceptSurrenderedForm/AcceptSurrenderedForm.tsx
@@ -7,7 +7,7 @@ import {
} from "@tradetrust-tt/tradetrust-ui-components";
import React, { FunctionComponent, useContext, useEffect } from "react";
import { FormState } from "../../../../../constants/FormState";
-import { TagBorderedLg } from "../../../../UI/Tag";
+import { TagBorderedSm } from "../../../../UI/Tag";
import { AssetInformationPanel } from "../../../AssetInformationPanel";
import { AssetManagementActions } from "../../../AssetManagementActions";
import { AssetManagementTitle } from "../../AssetManagementTitle";
@@ -58,9 +58,9 @@ export const AcceptSurrenderedForm: FunctionComponent
-
- Surrendered To Issuer
-
+
+ Surrendered To Issuer
+
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx
index aa6cf0e88..4c0b9a08c 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/ActionSelectionForm/ActionSelectionForm.tsx
@@ -5,8 +5,7 @@ import {
showDocumentTransferMessage,
} from "@tradetrust-tt/tradetrust-ui-components";
import React, { FunctionComponent, useContext, useRef, useState } from "react";
-import { TagBorderedLg } from "../../../../UI/Tag";
-import { AssetInformationPanel } from "../../../AssetInformationPanel";
+import { TagBorderedSm } from "../../../../UI/Tag";
import { AssetManagementActions } from "../../../AssetManagementActions";
import { AssetManagementDropdown } from "../../AssetManagementDropdown";
import { EditableAssetTitle } from "./../EditableAssetTitle";
@@ -34,7 +33,6 @@ interface ActionSelectionFormProps {
export const ActionSelectionForm: FunctionComponent = ({
onSetFormAction,
- tokenRegistryAddress,
beneficiary,
holder,
account,
@@ -48,7 +46,6 @@ export const ActionSelectionForm: FunctionComponent =
isTokenBurnt,
canNominateBeneficiary,
canEndorseTransfer,
- setShowEndorsementChain,
isTitleEscrow,
}) => {
const [tooltipMessage, setTooltipMessage] = useState("Copy");
@@ -105,29 +102,28 @@ export const ActionSelectionForm: FunctionComponent =
return (
<>
-
+ {(isSurrendered || isTokenBurnt) && (
+
+ )}
{isSurrendered && (
-
-
+
+
Surrendered To Issuer
-
-
+
+
)}
{isTokenBurnt && (
-
- Surrendered
-
+
+ Surrendered
+
)}
@@ -139,6 +135,7 @@ export const ActionSelectionForm: FunctionComponent
=
+
>
)}
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EditableAssetTitle/EditableAssetTitle.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EditableAssetTitle/EditableAssetTitle.tsx
index 865ba2b1c..a621842bb 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EditableAssetTitle/EditableAssetTitle.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EditableAssetTitle/EditableAssetTitle.tsx
@@ -43,7 +43,7 @@ export const EditableAssetTitle: FunctionComponent = ({
return (
-
+
tokenRegistryAddress={tokenRegistryAddress}
/>
+
+
{beneficiaryEndorseState === FormState.ERROR && (
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EndorseTransferForm/EndorseTransferForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EndorseTransferForm/EndorseTransferForm.tsx
index f76f1139e..5337fa2ae 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EndorseTransferForm/EndorseTransferForm.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/EndorseTransferForm/EndorseTransferForm.tsx
@@ -68,6 +68,8 @@ export const EndorseTransferForm: FunctionComponent =
tokenRegistryAddress={tokenRegistryAddress}
/>
+
+
=
isError={transferOwnersState === FormState.ERROR}
/>
+
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/NominateBeneficiary/NominateBeneficiary.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/NominateBeneficiary/NominateBeneficiary.tsx
index 3a4d1d7dc..1bdfb87e9 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/NominateBeneficiary/NominateBeneficiary.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/NominateBeneficiary/NominateBeneficiary.tsx
@@ -68,6 +68,8 @@ export const NominateBeneficiaryForm: FunctionComponent
+
+
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/RejectSurrenderedForm/RejectSurrenderedForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/RejectSurrenderedForm/RejectSurrenderedForm.tsx
index 004aa2ca6..1728d4ccf 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/RejectSurrenderedForm/RejectSurrenderedForm.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/RejectSurrenderedForm/RejectSurrenderedForm.tsx
@@ -7,7 +7,7 @@ import {
} from "@tradetrust-tt/tradetrust-ui-components";
import React, { FunctionComponent, useContext, useEffect } from "react";
import { FormState } from "../../../../../constants/FormState";
-import { TagBorderedLg } from "../../../../UI/Tag";
+import { TagBorderedSm } from "../../../../UI/Tag";
import { AssetInformationPanel } from "../../../AssetInformationPanel";
import { AssetManagementActions } from "../../../AssetManagementActions";
import { AssetManagementTitle } from "../../AssetManagementTitle";
@@ -73,9 +73,9 @@ export const RejectSurrenderedForm: FunctionComponent
-
- Surrendered To Issuer
-
+
+ Surrendered To Issuer
+
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/SurrenderForm/SurrenderForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/SurrenderForm/SurrenderForm.tsx
index 90b4d0e5e..5ea99ad3b 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/SurrenderForm/SurrenderForm.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/SurrenderForm/SurrenderForm.tsx
@@ -59,12 +59,15 @@ export const SurrenderForm: FunctionComponent
= ({
tokenRegistryAddress={tokenRegistryAddress}
/>
+
+
diff --git a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/TransferHolderForm/TransferHolderForm.tsx b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/TransferHolderForm/TransferHolderForm.tsx
index f7932085d..e4cae939a 100644
--- a/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/TransferHolderForm/TransferHolderForm.tsx
+++ b/src/components/AssetManagementPanel/AssetManagementForm/FormVariants/TransferHolderForm/TransferHolderForm.tsx
@@ -72,6 +72,8 @@ export const TransferHolderForm: FunctionComponent = ({
tokenRegistryAddress={tokenRegistryAddress}
/>
+
+
@@ -85,6 +87,7 @@ export const TransferHolderForm: FunctionComponent
= ({
isError={holderTransferringState === FormState.ERROR}
/>
+
diff --git a/src/components/CertificateDropZone/CertificateDropZone.tsx b/src/components/CertificateDropZone/CertificateDropZone.tsx
index dd3c8313a..6823dcb00 100644
--- a/src/components/CertificateDropZone/CertificateDropZone.tsx
+++ b/src/components/CertificateDropZone/CertificateDropZone.tsx
@@ -1,4 +1,4 @@
-import React, { FunctionComponent, useCallback, useMemo } from "react";
+import React, { FunctionComponent, useCallback, useEffect, useMemo, useState } from "react";
import { useDropzone } from "react-dropzone";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../../reducers";
@@ -16,6 +16,7 @@ import { useProviderContext } from "../../common/contexts/provider";
import { getChainId } from "../../utils/shared";
import { CONSTANTS } from "@tradetrust-tt/tradetrust-utils";
import { useNetworkSelect } from "./../../common/hooks/useNetworkSelect";
+import { ViewTokenRegistryMismatch } from "../DocumentDropzone/Views/ViewTokenRegistryMismatch";
const { TYPES } = CONSTANTS;
@@ -26,11 +27,16 @@ interface CertificateDropzoneProps {
export const CertificateDropZone: FunctionComponent
= (props) => {
const { toggleQrReaderVisible } = props;
const dispatch = useDispatch();
- const { verificationPending, retrieveCertificateByActionState, verificationStatus, verificationError } = useSelector(
- (state: RootState) => state.certificate
- );
+ const {
+ verificationPending,
+ retrieveCertificateByActionState,
+ verificationStatus,
+ verificationError,
+ tokenRegistryV5,
+ } = useSelector((state: RootState) => state.certificate);
const isVerificationPending = verificationPending;
+ const isTokenRegistryV5 = tokenRegistryV5;
const isVerificationError = useMemo(() => {
if (verificationError) return true;
if (verificationStatus && !isValid(verificationStatus)) return true;
@@ -56,10 +62,17 @@ export const CertificateDropZone: FunctionComponent =
try {
const json = JSON.parse(reader.result as string);
const chainId = getChainId(json);
- if (chainId && currentChainId !== chainId) {
+ if (!chainId) {
+ dispatch(updateCertificate(json));
+ return;
+ }
+ if (currentChainId === chainId) {
+ dispatch(updateCertificate(json));
+ } else {
await switchNetwork(chainId);
+ setTargetChainId(chainId);
+ setPendingCertificateData(json);
}
- dispatch(updateCertificate(json));
} catch (e) {
if (e instanceof Error) {
dispatch(verifyingCertificateCompleted([e.message]));
@@ -74,6 +87,17 @@ export const CertificateDropZone: FunctionComponent =
[currentChainId, dispatch, switchNetwork]
);
+ const [targetChainId, setTargetChainId] = useState(null);
+ const [pendingCertificateData, setPendingCertificateData] = useState(null);
+ // Effect to dispatch once currentChainId matches targetChainId
+ useEffect(() => {
+ if (targetChainId && currentChainId === targetChainId && pendingCertificateData) {
+ dispatch(updateCertificate(pendingCertificateData));
+ setTargetChainId(null);
+ setPendingCertificateData(null);
+ }
+ }, [currentChainId, targetChainId, pendingCertificateData, dispatch]);
+
const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone({
onDrop,
multiple: false,
@@ -88,8 +112,17 @@ export const CertificateDropZone: FunctionComponent =
isVerificationPending,
isVerificationError,
isActionError,
+ isTokenRegistryV5,
});
- }, [isDragReject, isDragActive, isDragAccept, isVerificationPending, isVerificationError, isActionError]);
+ }, [
+ isDragReject,
+ isDragActive,
+ isDragAccept,
+ isVerificationPending,
+ isVerificationError,
+ isActionError,
+ isTokenRegistryV5,
+ ]);
return (
@@ -101,6 +134,8 @@ export const CertificateDropZone: FunctionComponent
=
switch (true) {
case isVerificationPending:
return ;
+ case isTokenRegistryV5:
+ return ;
case isVerificationError:
return ;
case isActionError:
diff --git a/src/components/CertificateViewer.tsx b/src/components/CertificateViewer.tsx
index eb57a22dd..52d445cb6 100644
--- a/src/components/CertificateViewer.tsx
+++ b/src/components/CertificateViewer.tsx
@@ -150,7 +150,13 @@ export const CertificateViewer: FunctionComponent = ({ i
const renderedCertificateViewer = (
<>
- {!isTransferableDocument &&
}
+ {!isTransferableDocument && (
+
+ )}
{renderBanner(isSampleDocument, isMagicDemo)}
{isTransferableDocument && (
diff --git a/src/components/DocumentDropzone/Views/ViewTokenRegistryMismatch.tsx b/src/components/DocumentDropzone/Views/ViewTokenRegistryMismatch.tsx
new file mode 100644
index 000000000..0868f4520
--- /dev/null
+++ b/src/components/DocumentDropzone/Views/ViewTokenRegistryMismatch.tsx
@@ -0,0 +1,34 @@
+import React, { FunctionComponent } from "react";
+import { Button, ButtonSize } from "@tradetrust-tt/tradetrust-ui-components";
+import { URLS } from "../../../constants";
+import { TooltipIcon } from "../../UI/SvgIcon";
+import { Info } from "react-feather";
+export const ViewTokenRegistryMismatch: FunctionComponent = () => {
+ return (
+
+

+
Document cannot be read.
+
Please check that you have a valid .tt or .json file
+
Drop your TradeTrust Document to view its contents
+
Or
+
+
+
+
+
+
+
+ {" "}
+ Please visit {URLS.REF_V5TR} to verify new version of transferable
+ documents.
+
+
+
+ );
+};
diff --git a/src/components/DocumentStatus/DocumentStatus.tsx b/src/components/DocumentStatus/DocumentStatus.tsx
index c15b68f8e..b72a70957 100644
--- a/src/components/DocumentStatus/DocumentStatus.tsx
+++ b/src/components/DocumentStatus/DocumentStatus.tsx
@@ -33,7 +33,7 @@ const getV2FormattedDomainNames = (verificationStatus: VerificationFragment[]) =
};
const identityProofFragment = utils
.getIssuerIdentityFragments(verificationStatus)
- .find((fragment) => utils.isValidFragment(fragment)) as VerificationFragmentWithData;
+ .find((fragment) => utils.isValidFragment(fragment)) as unknown as VerificationFragmentWithData;
const dataFragment = identityProofFragment?.data;
const fragmentValidity =
@@ -72,10 +72,10 @@ export const IssuedBy: FunctionComponent
= ({ title = "Issued by"
// formattedDomainNames = getV4IdentityVerificationText(document);
// }
return (
-
+
{title}
{formattedDomainNames}
-
+
);
};
@@ -93,18 +93,16 @@ export const DocumentStatus: FunctionComponent = ({ isMagic
if (!document || !verificationStatus) return null;
return (
-
-
-
-
-
-
-
+
);
diff --git a/src/components/PopupMessage.tsx b/src/components/PopupMessage.tsx
new file mode 100644
index 000000000..65eb1fc53
--- /dev/null
+++ b/src/components/PopupMessage.tsx
@@ -0,0 +1,73 @@
+import { Button, ButtonSize, IconSuccess } from "@tradetrust-tt/tradetrust-ui-components";
+import React, { useState, useEffect } from "react";
+import { Checkbox } from "./UI/Checkbox";
+import { URLS } from "../constants";
+const PopupMessage = () => {
+ const [showPopup, setShowPopup] = useState(false);
+ const [doNotShowAgain, setDoNotShowAgain] = useState(false);
+ // Check if the user has already seen the popup or opted to not show it again
+ useEffect(() => {
+ const hasSeenPopup = localStorage.getItem("hasSeenPopup");
+ if (!hasSeenPopup) {
+ setShowPopup(true);
+ }
+ }, []);
+ const handleClosePopup = () => {
+ if (doNotShowAgain) {
+ localStorage.setItem("hasSeenPopup", "true");
+ }
+ setShowPopup(false);
+ };
+ const handleChangeCheckbox = (event: React.ChangeEvent
) => {
+ setDoNotShowAgain(event.target.checked);
+ };
+ if (!showPopup) return null;
+ return (
+
+
+ {/* Text content */}
+
+
+
+ Latest Document Updates
+
+
Here's why you should update and issue your files in the latest iteration:
+
+ - Latest iteration enabled new functions that can help you better align with the IG P&I requirement:
+
+ -
+ Reject function - Received a wrongfully transferred document, you may now reject it to where it came
+ from!
+
+ - Remark column - Any actions you take for your transferable document can now include a remark!
+
+
+
+ {/* Do not show again checkbox */}
+
+
+ Do not show this again
+
+
+ {/* Buttons - Dismiss and Learn More */}
+
+
+
+ );
+};
+export default PopupMessage;
diff --git a/src/constants/index.ts b/src/constants/index.ts
index a3df2e91e..99e3ad65b 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -1,6 +1,7 @@
export enum URLS {
INFO = "https://tradetrust.io",
REF = "https://ref.tradetrust.io",
+ REF_V5TR = "https://v5-token-registry.tradetrust.io",
CREATOR = "https://creator.tradetrust.io/",
GITHUB = "https://github.com/TradeTrust/tradetrust-website",
DOCS = "https://docs.tradetrust.io/",
diff --git a/src/reducers/certificate.js b/src/reducers/certificate.js
index 4bc9bbe8a..ed381cc5b 100644
--- a/src/reducers/certificate.js
+++ b/src/reducers/certificate.js
@@ -11,6 +11,9 @@ export const initialState = {
raw: null,
rawModified: null,
+ providerOrSigner: null,
+ tokenRegistryV5: false,
+
verificationPending: false,
verificationStatus: null,
verificationError: null,
@@ -25,6 +28,8 @@ export const types = {
UPDATE_CERTIFICATE: "UPDATE_CERTIFICATE",
+ DETECTING_TR_V5_CERTIFICATE: "DETECTING_TR_V5_CERTIFICATE",
+
VERIFYING_CERTIFICATE: "VERIFYING_CERTIFICATE",
VERIFYING_CERTIFICATE_COMPLETED: "VERIFYING_CERTIFICATE_COMPLETED",
@@ -54,6 +59,12 @@ export default function reducer(state = initialState, action) {
raw: action.payload,
rawModified: action.payload,
};
+ case types.DETECTING_TR_V5_CERTIFICATE:
+ return {
+ ...state,
+ verificationPending: false,
+ tokenRegistryV5: true,
+ };
case types.VERIFYING_CERTIFICATE:
return {
...state,
@@ -117,6 +128,11 @@ export function updateCertificate(payload) {
};
}
+export const detectingTRV5Certificate = (payload) => ({
+ type: types.DETECTING_TR_V5_CERTIFICATE,
+ payload,
+});
+
export function applyPrivacyFilter(payload) {
return {
type: types.CERTIFICATE_OBFUSCATE_UPDATE,
diff --git a/src/sagas/certificate.ts b/src/sagas/certificate.ts
index 79ff4b408..a5556fb16 100644
--- a/src/sagas/certificate.ts
+++ b/src/sagas/certificate.ts
@@ -5,6 +5,7 @@ import {
verifyingCertificateCompleted,
verifyingCertificateFailure,
getCertificate,
+ detectingTRV5Certificate,
} from "../reducers/certificate";
import { processQrCode } from "../services/qrProcessor";
import { verifyDocument } from "../services/verify";
@@ -13,6 +14,8 @@ import { decryptString } from "@govtechsg/oa-encryption";
import { history } from "../history";
import { CONSTANTS } from "@tradetrust-tt/tradetrust-utils";
import { ActionPayload } from "./../types";
+import { utils } from "@tradetrust-tt/tradetrust";
+import { getTokenRegistryAddress, isTokenRegistryV5 } from "../utils/shared";
const { trace } = getLogger("saga:certificate");
@@ -25,6 +28,19 @@ export function* verifyCertificate(): any {
});
const certificate = yield select(getCertificate);
+ const isTransferableAsset = utils.isTransferableAsset(certificate);
+ if (isTransferableAsset) {
+ const registryAddress = getTokenRegistryAddress(certificate);
+ const tokenId = `0x${utils.getAssetId(certificate)}`;
+ if (registryAddress && tokenId) {
+ const tokenRegistryV5 = yield isTokenRegistryV5(registryAddress, tokenId);
+ if (tokenRegistryV5) {
+ yield put(detectingTRV5Certificate(TYPES.INVALID));
+ return;
+ }
+ }
+ }
+
const verificationStatus = yield verifyDocument(certificate);
trace(`Verification Status: ${JSON.stringify(verificationStatus)}`);
diff --git a/src/test/helper.js b/src/test/helper.js
index cad5e3840..caa812a92 100644
--- a/src/test/helper.js
+++ b/src/test/helper.js
@@ -14,6 +14,12 @@ export const validateTextContent = async (testcafe, component, texts) =>
);
export const navigateToVerify = async () => {
+ const button = Selector("button").withText("Dismiss");
+ if (await button.exists) {
+ await t.click(button);
+ } else {
+ console.log("Button does not exist");
+ }
await t.click(VerifyPage);
};
diff --git a/src/utils/shared.ts b/src/utils/shared.ts
index 2ff8d456d..a7dd67a3a 100644
--- a/src/utils/shared.ts
+++ b/src/utils/shared.ts
@@ -1,6 +1,10 @@
import { getData, utils, v2, v3, OpenAttestationDocument, WrappedDocument } from "@tradetrust-tt/tradetrust";
import { getSupportedChainIds } from "../common/utils/chain-utils";
-import { AvailableBlockChains, ChainId } from "../constants/chain-info";
+import { AvailableBlockChains, BurnAddress, ChainId } from "../constants/chain-info";
+import { TitleEscrow__factory, TradeTrustToken__factory } from "@tradetrust-tt/token-registry/contracts";
+import { TitleEscrowInterface } from "../common/contexts/TokenInformationContext";
+import { getCurrentProvider } from "../common/contexts/provider";
+import { ethers } from "ethers";
export type WrappedOrSignedOpenAttestationDocument = WrappedDocument;
// note that the return type for getting attachments will normalise the structure into v2.Attachment
@@ -85,3 +89,34 @@ export const getChainId = (rawDocument: WrappedOrSignedOpenAttestationDocument):
return undefined;
}
};
+
+export async function isTokenRegistryV5(registryAddress: string, tokenId: string): Promise {
+ try {
+ const provider = getCurrentProvider();
+ if (!provider) {
+ return false;
+ }
+ const tokenRegistry = TradeTrustToken__factory.connect(registryAddress, provider);
+ const titleEscrowOwner = await tokenRegistry.ownerOf(tokenId);
+ const inactiveEscrow = [BurnAddress, registryAddress]
+ .map((s) => s.toLowerCase())
+ .includes(titleEscrowOwner.toLowerCase());
+ let titleEscrowAddress = titleEscrowOwner;
+ if (inactiveEscrow) {
+ const titleEscrowFactoryAddress = await tokenRegistry.titleEscrowFactory();
+ const tokenRegistryAddress = await tokenRegistry.address;
+ // Resolve V5 TitleEscrowFactory__factory contract function rename from getAddress to getEscrowAddress
+ const titleEscrowFactory = new ethers.Contract(
+ titleEscrowFactoryAddress,
+ `[{"inputs":[{"internalType":"address","name":"tokenRegistry","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getEscrowAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]`,
+ provider
+ );
+ titleEscrowAddress = await titleEscrowFactory.getEscrowAddress(tokenRegistryAddress, tokenId);
+ }
+ const titleEscrow = TitleEscrow__factory.connect(titleEscrowAddress, provider);
+ const isTitleEscrowV5 = await titleEscrow.supportsInterface(TitleEscrowInterface.V5);
+ return isTitleEscrowV5;
+ } catch (error) {
+ return false;
+ }
+}
diff --git a/tests/e2e/specs/1a-transfer-holder.spec.js b/tests/e2e/specs/1a-transfer-holder.spec.js
index 609c7c6fa..5d57307f9 100644
--- a/tests/e2e/specs/1a-transfer-holder.spec.js
+++ b/tests/e2e/specs/1a-transfer-holder.spec.js
@@ -1,6 +1,10 @@
import { ACCOUNT_2 } from "../utils";
before(() => {
+ cy.window().then((window) => {
+ window.localStorage.setItem('hasSeenPopup', 'true');
+ });
+ cy.wait(1000);
cy.importMetamaskAccount("0xc58c1ff75001afdca8cecb61b47f36964febe4188b8f7b26252286ecae5a8879");
cy.switchMetamaskAccount(1); // ensure switch to account 1 (owner)
});
diff --git a/tests/e2e/specs/2a-surrender-reject.spec.js b/tests/e2e/specs/2a-surrender-reject.spec.js
index 9e1ceaff8..9ea84f959 100644
--- a/tests/e2e/specs/2a-surrender-reject.spec.js
+++ b/tests/e2e/specs/2a-surrender-reject.spec.js
@@ -1,4 +1,7 @@
before(() => {
+ cy.window().then((window) => {
+ window.localStorage.setItem('hasSeenPopup', 'true');
+ });
cy.switchMetamaskAccount(1); // ensure switch to account 1 (owner)
});
diff --git a/tests/e2e/specs/2b-surrender-accept.spec.js b/tests/e2e/specs/2b-surrender-accept.spec.js
index acc88a0d6..99e2a2302 100644
--- a/tests/e2e/specs/2b-surrender-accept.spec.js
+++ b/tests/e2e/specs/2b-surrender-accept.spec.js
@@ -1,4 +1,7 @@
before(() => {
+ cy.window().then((window) => {
+ window.localStorage.setItem('hasSeenPopup', 'true');
+ });
cy.switchMetamaskAccount(1); // ensure switch to account 1 (owner)
});
diff --git a/tests/e2e/specs/3a-endorse-transfer-owner-holder.spec.js b/tests/e2e/specs/3a-endorse-transfer-owner-holder.spec.js
index a2826ce6e..536a29f71 100644
--- a/tests/e2e/specs/3a-endorse-transfer-owner-holder.spec.js
+++ b/tests/e2e/specs/3a-endorse-transfer-owner-holder.spec.js
@@ -1,6 +1,9 @@
import { ACCOUNT_3 } from "../utils";
before(() => {
+ cy.window().then((window) => {
+ window.localStorage.setItem('hasSeenPopup', 'true');
+ });
cy.switchMetamaskAccount(1); // ensure switch to account 1 (owner)
});
diff --git a/tests/e2e/specs/3b-endorse-nominate-owner.spec.js b/tests/e2e/specs/3b-endorse-nominate-owner.spec.js
index 53f356ccd..88bdc1c02 100644
--- a/tests/e2e/specs/3b-endorse-nominate-owner.spec.js
+++ b/tests/e2e/specs/3b-endorse-nominate-owner.spec.js
@@ -1,6 +1,9 @@
import { ACCOUNT_1, ACCOUNT_3 } from "../utils";
before(() => {
+ cy.window().then((window) => {
+ window.localStorage.setItem('hasSeenPopup', 'true');
+ });
cy.switchMetamaskAccount(1); // ensure switch to account 1 (owner)
});
diff --git a/tests/e2e/specs/3c-endorse-nominate-owner-accept.spec.js b/tests/e2e/specs/3c-endorse-nominate-owner-accept.spec.js
index 758f64d56..93450e816 100644
--- a/tests/e2e/specs/3c-endorse-nominate-owner-accept.spec.js
+++ b/tests/e2e/specs/3c-endorse-nominate-owner-accept.spec.js
@@ -1,6 +1,9 @@
import { ACCOUNT_2, ACCOUNT_3 } from "../utils";
before(() => {
+ cy.window().then((window) => {
+ window.localStorage.setItem('hasSeenPopup', 'true');
+ });
cy.switchMetamaskAccount(2); // switch to account 2 (holder)
});