Skip to content

Commit

Permalink
feat: allow w3c to render
Browse files Browse the repository at this point in the history
  • Loading branch information
nghaninn committed Jan 8, 2025
1 parent d4c3f20 commit 11e1189
Show file tree
Hide file tree
Showing 9 changed files with 374 additions and 306 deletions.
492 changes: 254 additions & 238 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@
"@govtechsg/oa-encryption": "^1.3.5",
"@govtechsg/open-attestation-utils": "1.0.9",
"@reduxjs/toolkit": "^1.6.1",
"@tradetrust-tt/decentralized-renderer-react-components": "^3.14.3",
"@tradetrust-tt/document-store": "^4.1.0",
"@tradetrust-tt/tradetrust-ui-components": "^3.0.0",
"@trustvc/trustvc": "^1.0.4",
"@tradetrust-tt/decentralized-renderer-react-components": "^3.15.3",
"@tradetrust-tt/document-store": "^4.1.1",
"@tradetrust-tt/tradetrust-ui-components": "^3.2.1",
"@trustvc/trustvc": "^1.1.3",
"@types/gtag.js": "0.0.8",
"buffer": "^6.0.3",
"cross-env": "^7.0.3",
Expand Down Expand Up @@ -103,7 +103,7 @@
"@testing-library/jest-dom": "^5.13.0",
"@testing-library/react": "^11.2.7",
"@testing-library/react-hooks": "^7.0.0",
"@tradetrust-tt/tradetrust-cli": "^3.0.0-alpha.2",
"@tradetrust-tt/tradetrust-cli": "^3.0.1",
"@types/debug": "^4.1.5",
"@types/file-saver": "^2.0.3",
"@types/jest": "^26.0.23",
Expand Down Expand Up @@ -170,7 +170,7 @@
"testcafe": "^2.3.0",
"ts-jest": "^26.0.0",
"ts-node": "^10.9.2",
"typescript": "^4.3.2",
"typescript": "^4.9.5",
"vm-browserify": "^1.1.2",
"wait-on": "^5.3.0",
"webpack": "^5.91.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { v5SupportInterfaceIds } from "@trustvc/trustvc";
import { v5SupportInterfaceIds, v4SupportInterfaceIds } from "@trustvc/trustvc";
import { v5Contracts } from "@trustvc/trustvc";
import React, { createContext, FunctionComponent, useCallback, useContext, useEffect, useState } from "react";
import { BurnAddress } from "../../../constants/chain-info";
Expand All @@ -8,7 +8,6 @@ import { useSupportsInterface } from "../../hooks/useSupportsInterface";
import { useTitleEscrowContract } from "../../hooks/useTitleEscrowContract";
import { useTokenRegistryContract } from "../../hooks/useTokenRegistryContract";
import { useProviderContext } from "../provider";
const contractInterfaceId = v5SupportInterfaceIds;
type TitleEscrow = v5Contracts.TitleEscrow;
type TradeTrustToken = v5Contracts.TradeTrustToken;

Expand Down Expand Up @@ -99,8 +98,8 @@ interface TokenInformationContextProviderProps {

// TODO: HAN Move the constant value to token-registry repo
export const TitleEscrowInterface = {
V4: "0x079dff60",
V5: contractInterfaceId.TitleEscrow,
V4: v4SupportInterfaceIds.TitleEscrow,
V5: v5SupportInterfaceIds.TitleEscrow,
};

export const TokenInformationContextProvider: FunctionComponent<TokenInformationContextProviderProps> = ({
Expand Down
7 changes: 4 additions & 3 deletions src/components/CertificateViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { isTransferableAsset, getAssetId } from "@trustvc/trustvc";
import React, { FunctionComponent, useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useProviderContext } from "../common/contexts/provider";
import { useTokenInformationContext } from "../common/contexts/TokenInformationContext";
import { RootState } from "../reducers";
import { resetCertificateState, updateCertificate } from "../reducers/certificate";
import { resetDemoState } from "../reducers/demo-verify";
import { FORM_SG_URL } from "../routes";
import { TemplateProps } from "../types";
import { getLogger } from "../utils/logger";
import {
getAttachments,
getKeyId,
getTokenId,
getTokenRegistryAddress,
isTransferableAsset,
WrappedOrSignedOpenAttestationDocument,
} from "../utils/shared";
import { AssetManagementApplication } from "./AssetManagementPanel/AssetManagementApplication";
Expand All @@ -24,7 +26,6 @@ import { EndorsementChainContainer } from "./EndorsementChain";
import { ObfuscatedMessage } from "./ObfuscatedMessage";
import { TabPaneAttachments } from "./TabPaneAttachments";
import { Banner } from "./UI/Banner";
import { FORM_SG_URL } from "../routes";

const { trace } = getLogger("component: certificateviewer");

Expand Down Expand Up @@ -68,7 +69,7 @@ export const CertificateViewer: FunctionComponent<CertificateViewerProps> = ({ i
let tokenId = "";
if (isTransferableAssetVal) {
try {
tokenId = `0x${getAssetId(document)}`;
tokenId = getTokenId(document);
} catch (e) {
trace(e);
}
Expand Down
20 changes: 13 additions & 7 deletions src/components/DocumentStatus/DocumentStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import {
SignedVerifiableCredential,
VerificationFragment,
VerificationFragmentWithData,
utils,
WrappedDocument,
isWrappedV2Document,
isWrappedV3Document,
utils,
v3,
vc,
} from "@trustvc/trustvc";
import React, { FunctionComponent } from "react";
import { StatusChecks } from "./StatusChecks";
import { useSelector } from "react-redux";
import { isWrappedV2Document, v3 } from "@trustvc/trustvc";
import { RootState } from "../../reducers";
import { WrappedOrSignedOpenAttestationDocument } from "../../utils/shared";
import { StatusChecks } from "./StatusChecks";

interface VerificationFragmentData {
did: string;
Expand Down Expand Up @@ -55,6 +58,10 @@ export const getV3IdentityVerificationText = (document: WrappedDocument<v3.OpenA
return document.openAttestationMetadata.identityProof.identifier.toUpperCase();
};

export const getW3CIdentityVerificationText = (document: SignedVerifiableCredential): string => {
return (typeof document?.issuer === "string" ? document?.issuer : document?.issuer?.id)?.toUpperCase();
};

// disabled until alpha is promoted to master
// export const getV4IdentityVerificationText = (document: WrappedDocument<v4.OpenAttestationDocument>): string => {
// return document.issuer.identityProof.identifier.toUpperCase();
Expand All @@ -72,11 +79,10 @@ export const IssuedBy: FunctionComponent<IssuedByProps> = ({ title = "Issued by"
formattedDomainNames = getV2FormattedDomainNames(verificationStatus);
} else if (isWrappedV3Document(document)) {
formattedDomainNames = getV3IdentityVerificationText(document);
} else if (vc.isSignedDocument(document)) {
formattedDomainNames = getW3CIdentityVerificationText(document);
}
// disabled until alpha tradetrust-tt packages are promoted to master
// else {
// formattedDomainNames = getV4IdentityVerificationText(document);
// }

return (
<h4 id="issuedby" className="my-2 leading-none">
<span className="mr-2 break-all">{title}</span>
Expand Down
3 changes: 2 additions & 1 deletion src/components/ObfuscatedMessage/ObfuscatedMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { utils } from "@tradetrust-tt/tradetrust";
import { isSignedDocument } from "@trustvc/trustvc/w3c/vc";
import React, { FunctionComponent } from "react";
import { WrappedOrSignedOpenAttestationDocument } from "../../utils/shared";

Expand All @@ -7,7 +8,7 @@ interface ObfuscatedMessageProps {
}

export const ObfuscatedMessage: FunctionComponent<ObfuscatedMessageProps> = ({ document }) => {
if (!utils.isObfuscated(document)) return null;
if (isSignedDocument(document) || !utils.isObfuscated(document)) return null;

return (
<div className="container">
Expand Down
18 changes: 9 additions & 9 deletions src/sagas/certificate.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { decryptString } from "@govtechsg/oa-encryption";
import { errorMessages, isValid } from "@trustvc/trustvc";
import { put, select, takeEvery } from "redux-saga/effects";
import { getLogger } from "../utils/logger";
import { history } from "../history";
import {
detectingTRV4Certificate,
getCertificate,
types,
verifyingCertificateCompleted,
verifyingCertificateFailure,
getCertificate,
detectingTRV4Certificate,
} from "../reducers/certificate";
import { processQrCode } from "../services/qrProcessor";
import { verifyDocument } from "../services/verify";
import { isValid } from "@trustvc/trustvc";
import { decryptString } from "@govtechsg/oa-encryption";
import { history } from "../history";
import { errorMessages, isTransferableAsset, getAssetId } from "@trustvc/trustvc";
import { getLogger } from "../utils/logger";
import { getTokenId, getTokenRegistryAddress, isTokenRegistryV4, isTransferableAsset } from "../utils/shared";
import { ActionPayload } from "./../types";
import { isTokenRegistryV4, getTokenRegistryAddress } from "../utils/shared";

const { trace } = getLogger("saga:certificate");

Expand All @@ -31,7 +30,8 @@ export function* verifyCertificate(): any {

if (isTransferableAssetVal) {
const registryAddress = getTokenRegistryAddress(certificate);
const tokenId = `0x${getAssetId(certificate)}`;
const tokenId = getTokenId(certificate);

if (registryAddress && tokenId) {
const tokenRegistryV4 = yield isTokenRegistryV4(registryAddress, tokenId);
if (tokenRegistryV4) {
Expand Down
116 changes: 80 additions & 36 deletions src/utils/shared.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import {
getAssetId,
getDataV2,
getDocumentData,
getIssuerAddress,
isTitleEscrowVersion,
isWrappedV2Document,
isWrappedV3Document,
OpenAttestationDocument,
SignedVerifiableCredential,
v2,
v3,
OpenAttestationDocument,
WrappedDocument,
isWrappedV3Document,
getDocumentData,
isWrappedV2Document,
getIssuerAddress,
isTransferableAsset as isTransferableOAAsset,
} from "@trustvc/trustvc";
import { getSupportedChainIds } from "../common/utils/chain-utils";
import { AvailableBlockChains, BurnAddress, ChainId } from "../constants/chain-info";
import { v5Contracts } from "@trustvc/trustvc";
import { TRANSFERABLE_RECORDS_TYPE } from "@trustvc/trustvc/verify/fragments";
import { TransferableRecordsCredentialStatus } from "@trustvc/trustvc/w3c/credential-status";
import { isSignedDocument } from "@trustvc/trustvc/w3c/vc";
import { TitleEscrowInterface } from "../common/contexts/TokenInformationContext";
import { getCurrentProvider } from "../common/contexts/provider";
const { TitleEscrow__factory, TitleEscrowFactory__factory, TradeTrustToken__factory } = v5Contracts;
import { getSupportedChainIds } from "../common/utils/chain-utils";
import { AvailableBlockChains, ChainId } from "../constants/chain-info";

export type WrappedOrSignedOpenAttestationDocument = WrappedDocument<OpenAttestationDocument>;
// note that the return type for getting attachments will normalise the structure into v2.Attachment
export type OpenAttestationAttachment = v2.Attachment;

export const getOpenAttestationData = (
wrappedDocument: WrappedDocument<OpenAttestationDocument>
): OpenAttestationDocument => getDocumentData(wrappedDocument);
): OpenAttestationDocument => {
if (isSignedDocument(wrappedDocument)) {
return wrappedDocument as any;
}
return getDocumentData(wrappedDocument);
};

export const getTemplateUrl = (rawDocument: WrappedOrSignedOpenAttestationDocument): string | undefined => {
if (isWrappedV2Document(rawDocument)) {
if (isSignedDocument(rawDocument)) {
return [(rawDocument as unknown as SignedVerifiableCredential).renderMethod]?.flat()?.[0]?.id;
} else if (isWrappedV2Document(rawDocument)) {
const documentData = getDataV2(rawDocument);
return typeof documentData.$template === "object" ? documentData.$template.url : undefined;
} else if (isWrappedV3Document(rawDocument)) {
Expand Down Expand Up @@ -59,17 +71,62 @@ export const getAttachments = (rawDocument: WrappedOrSignedOpenAttestationDocume
}
};

export const getTokenRegistryAddress = (document: WrappedOrSignedOpenAttestationDocument): string | undefined => {
const issuerAddress = getIssuerAddress(document);
export const getTransferableRecordsCredentialStatus = (document: unknown): TransferableRecordsCredentialStatus => {
return [
(document as SignedVerifiableCredential)?.credentialStatus,
].flat()?.[0] as TransferableRecordsCredentialStatus;
};

export const isTransferableAsset = (document: WrappedOrSignedOpenAttestationDocument): boolean => {
// TODO: HAN: Migrate isTransferableAsset to trustvc
let isTransferableAssetVal: boolean = false;
if (isSignedDocument(document)) {
const credentialStatus = getTransferableRecordsCredentialStatus(document);
isTransferableAssetVal = credentialStatus?.type === TRANSFERABLE_RECORDS_TYPE;
} else {
isTransferableAssetVal = isTransferableOAAsset(document);
}

return isTransferableAssetVal;
};

export const getTokenRegistryAddress = (
document: WrappedOrSignedOpenAttestationDocument | SignedVerifiableCredential
): string | undefined => {
// const issuerAddress = getIssuerAddress(document);
// TODO: HAN: Migrate getIssuerAddress to trustvc
let issuerAddress: string | string[] = "";
if (isSignedDocument(document)) {
const credentialStatus = getTransferableRecordsCredentialStatus(document);
issuerAddress = credentialStatus?.tokenRegistry;
} else {
issuerAddress = getIssuerAddress(document);
}
return issuerAddress instanceof Array ? issuerAddress[0] : issuerAddress;
};

export const getChainId = (rawDocument: WrappedOrSignedOpenAttestationDocument): ChainId | undefined => {
export const getTokenId = (document: WrappedOrSignedOpenAttestationDocument | SignedVerifiableCredential): string => {
// const tokenId = `0x${utils.getAssetId(certificate)}`;
// TODO: HAN: Migrate getAssetId to trustvc
let tokenId: string | undefined = "";
if (isSignedDocument(document)) {
const credentialStatus = getTransferableRecordsCredentialStatus(document);
tokenId = credentialStatus?.tokenId;
} else {
tokenId = getAssetId(document);
}

return `0x${tokenId}`;
};

export const getChainId = (
rawDocument: WrappedOrSignedOpenAttestationDocument | SignedVerifiableCredential
): ChainId | undefined => {
const throwError = () => {
throw new Error("Invalid Document, please use a valid document.");
};

function processChainId(document: v2.OpenAttestationDocument | v3.OpenAttestationDocument): number | undefined {
function processOAChainId(document: v2.OpenAttestationDocument | v3.OpenAttestationDocument): number | undefined {
if (document.network) {
// Check for current blockchain, "ETH" or "MATIC", and chainId, if need cater for other blockchain and network, update this accordingly.
if (AvailableBlockChains.includes(document.network.chain) && document.network.chainId) {
Expand All @@ -87,17 +144,21 @@ export const getChainId = (rawDocument: WrappedOrSignedOpenAttestationDocument):
return undefined;
}

if (isWrappedV2Document(rawDocument)) {
// TODO: HAN: Migrate getChainId to trustvc
if (isSignedDocument(rawDocument)) {
const credentialStatus = getTransferableRecordsCredentialStatus(rawDocument);
return credentialStatus?.tokenNetwork?.chainId as ChainId;
} else if (isWrappedV2Document(rawDocument)) {
const documentData = getDataV2(rawDocument);
// Check for DID, ignore chainId when its DID
const identityProofType = documentData.issuers[0].identityProof?.type;
if (identityProofType === "DNS-DID" || identityProofType === "DID") return undefined;
return processChainId(documentData);
return processOAChainId(documentData);
} else if (isWrappedV3Document(rawDocument)) {
// Check for DID, ignore chainId when its DID
const identityProofType = rawDocument.openAttestationMetadata.identityProof.type;
if (identityProofType === "DNS-DID" || identityProofType === "DID") return undefined;
return processChainId(rawDocument);
return processOAChainId(rawDocument);
} else {
// for now v4 is only DID method so ignore chainID
return undefined;
Expand All @@ -111,24 +172,7 @@ export async function isTokenRegistryV4(registryAddress: string, tokenId: string
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;
const titleEscrowFactory = TitleEscrowFactory__factory.connect(titleEscrowFactoryAddress, provider);
titleEscrowAddress = await titleEscrowFactory.getEscrowAddress(tokenRegistryAddress, tokenId);
}

const titleEscrow = TitleEscrow__factory.connect(titleEscrowAddress, provider);
const isTitleEscrowV4 = await titleEscrow.supportsInterface(TitleEscrowInterface.V4);

const isTitleEscrowV4 = await isTitleEscrowVersion(TitleEscrowInterface.V4, registryAddress, tokenId, provider!);
return isTitleEscrowV4;
} catch (error) {
return false;
Expand Down
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"module": "Node16",
"moduleResolution": "node16",
"resolveJsonModule": true,
"jsx": "react",
"sourceMap": true,
"types": ["jest", "node", "webpack-env", "@testing-library/jest-dom", "@types/gtag.js", "cypress"],
"allowJs": true,
"noEmit": true,
"isolatedModules": true,
"useUnknownInCatchVariables": true
},
"include": ["src"]
Expand Down

0 comments on commit 11e1189

Please sign in to comment.