Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ vi.mock('../AssetManagementForm', () => ({
),
}))

// Mock TagBordered
// Mock Tag
vi.mock('../../common/Tag', () => ({
TagBordered: ({ children, ...props }: any) => (
<div data-testid="tag-bordered" {...props}>
Tag: ({ children, ...props }: any) => (
<div data-testid="tag" {...props}>
{children}
</div>
),
Expand Down Expand Up @@ -166,14 +166,14 @@ describe('AssetManagementApplication', () => {
/>
)

expect(screen.getByTestId('expiredDoc')).toBeInTheDocument()
expect(screen.getByTestId('tag')).toBeInTheDocument()
expect(screen.getByText('Expired')).toBeInTheDocument()
})

it('does not render expired tag when document is not expired', () => {
render(<AssetManagementApplication {...defaultNonTransferableProps} />)

expect(screen.queryByTestId('expiredDoc')).not.toBeInTheDocument()
expect(screen.queryByTestId('tag')).not.toBeInTheDocument()
})
})

Expand Down Expand Up @@ -413,7 +413,7 @@ describe('AssetManagementApplication', () => {
expect(
screen.queryByTestId('asset-management-form')
).not.toBeInTheDocument()
expect(screen.queryByTestId('expiredDoc')).not.toBeInTheDocument()
expect(screen.queryByTestId('tag')).not.toBeInTheDocument()
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useTokenRegistryContract } from '../../../hooks/useTokenRegistryContrac
import { useTokenRegistryRole } from '../../../hooks/useTokenRegistryRole'
import { AssetManagementActions } from '../AssetManagementActions'
import { AssetManagementForm } from '../AssetManagementForm'
import { TagBordered } from '../../common/Tag'
import { Tag } from '../../common/Tag'
import { useTokenRegistryVersion } from '../../../hooks/useTokenRegistryVersion'
import { TokenRegistryVersions } from '../../../constants'

Expand All @@ -23,7 +23,7 @@ interface AssetManagementIsTransferableDocumentProps {
setShowEndorsementChain: (payload: boolean) => void
refreshEndorsementChain?: () => void
isTransferableDocument: true
isExpired: boolean
isExpired?: boolean
}

interface AssetManagementIsNotTransferableDocumentProps {
Expand Down Expand Up @@ -200,16 +200,13 @@ export const AssetManagementApplication: FunctionComponent<
/>
) : (
isExpired && (
<div className="flex-1 content-center space-y-2 md:space-x-2 md:space-y-0">
<TagBordered
id="expired-sign"
<div className="flex-1 content-center space-y-2 md:space-x-2 md:space-y-0 border-t border-gray-200 py-2">
<Tag
rounded="rounded-full"
className="border-scarlet-100 bg-scarlet-100 text-scarlet-500 content-center justify-self-center w-full xs:w-auto h-10 px-4 py-2"
className="bg-[#FDDAE2] !p-2 min-w-[188px] max-w-[383px] text-center w-full flex-1"
>
<h5 data-testid="expiredDoc" className="text-center break-keep">
Expired
</h5>
</TagBordered>
<h4 className="bg-alert-20">Expired</h4>
</Tag>
</div>
)
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ export const AssetManagementForm: FunctionComponent<
isTokenBurnt={isTokenBurnt}
setShowEndorsementChain={setShowEndorsementChain}
isTitleEscrow={isTitleEscrow}
isExpired={isExpired}
/>
)}
{(formAction === AssetManagementActions.TransferHolder ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ interface ActionSelectionFormProps {
isTitleEscrow: boolean
isRejectPendingConfirmation?: boolean
isTokenBurnt: boolean
isExpired?: boolean

canReturnToIssuer: boolean
canHandleShred?: boolean
Expand All @@ -50,7 +49,6 @@ export const ActionSelectionForm: FunctionComponent<
isTokenBurnt,
isTitleEscrow,
isRejectPendingConfirmation,
isExpired,
canTransferHolder,
canTransferBeneficiary,
canTransferOwners,
Expand Down Expand Up @@ -187,14 +185,6 @@ export const ActionSelectionForm: FunctionComponent<
</h4>
</Tag>
)}
{isExpired && (
<Tag
rounded="rounded-full"
className="flex flex-row justify-center items-center p-2 gap-[10px] min-w-[188px] max-w-[383px] h-10 bg-[#FDDAE2] rounded-full flex-1"
>
<h4 className="bg-alert-20 whitespace-nowrap">ETR Expired</h4>
</Tag>
)}
<div className="vr-footer-dropdown-placeholder" />
</div>
{!isTokenBurnt && (
Expand Down
49 changes: 49 additions & 0 deletions src/components/home/VerifySection/ObfuscatedMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { FunctionComponent, useEffect, useState } from 'react'
import { isObfuscated } from '@trustvc/trustvc'

interface ObfuscatedMessageProps {
document: unknown
}

export const ObfuscatedMessage: FunctionComponent<ObfuscatedMessageProps> = ({
document,
}) => {
const [isDocumentObfuscated, setIsDocumentObfuscated] = useState<
boolean | null
>(null)

useEffect(() => {
let cancelled = false
const checkObfuscation = async () => {
try {
const result = await isObfuscated(document as any)
if (!cancelled) setIsDocumentObfuscated(result)
} catch (error) {
console.warn('Error checking if document is obfuscated:', error)
if (!cancelled) setIsDocumentObfuscated(false)
}
}
checkObfuscation()
return () => {
cancelled = true
}
}, [document])

if (isDocumentObfuscated === null || !isDocumentObfuscated) return null

return (
<div
data-testid="obfuscation-info"
style={{
textAlign: 'left',
color: '#DC2626',
fontSize: '16px',
padding: '16px 0',
}}
>
Note: There are fields/data obfuscated in this document.
</div>
)
}

export default ObfuscatedMessage
7 changes: 7 additions & 0 deletions src/components/home/VerifySection/VerifyResult.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ vi.mock('./useVerify', async () => {
return { ...actual, makeExplorerAddressURL: vi.fn() }
})

// Stub AssetManagementApplication to avoid DocumentProvider context dependency
vi.mock('../../AssetManagementPanel/AssetManagementApplication', () => ({
AssetManagementApplication: () => (
<div data-testid="asset-management-application" />
),
}))

import { makeExplorerAddressURL } from './useVerify'

// ─── Helpers ──────────────────────────────────────────────────────────────────
Expand Down
16 changes: 15 additions & 1 deletion src/components/home/VerifySection/VerifyResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'
import NetworkTooltip from './NetworkTooltip'
import DocumentRenderer from './DocumentRenderer'
import InvalidAttachmentsBanner from './InvalidAttachmentsBanner'
import ObfuscatedMessage from './ObfuscatedMessage'
import { makeExplorerAddressURL } from './useVerify'
import { CheckCircle, CrossCircle } from '../../common/Icons'
import { DocumentAttachment } from '../../../utils/helper'
Expand All @@ -21,6 +22,7 @@ interface VerifyResultProps {
tokenId?: string
issuer?: string
isTransferable?: boolean
isExpired?: boolean
tokenRegistryAddress?: string
tags?: string[]
owner?: { name?: string; address?: string }
Expand Down Expand Up @@ -57,6 +59,7 @@ const VerifyResult: React.FC<VerifyResultProps> = ({
onViewNftRegistry,
onViewEndorsementChain,
refreshEndorsementChain,
isExpired,
}) => {
const { changeNetwork, currentChainId } = useProviderContext()

Expand Down Expand Up @@ -306,6 +309,14 @@ const VerifyResult: React.FC<VerifyResultProps> = ({
)}

{/* Footer: Connect Wallet */}
{!isTransferable && (
<AssetManagementApplication
isMagicDemo={false}
isTransferableDocument={!!isTransferable}
isExpired={!!isExpired}
isSampleDocument={false}
/>
)}

{isTransferable && tokenRegistryAddress && tokenId && (
<AssetManagementApplication
Expand All @@ -321,11 +332,14 @@ const VerifyResult: React.FC<VerifyResultProps> = ({
refreshEndorsementChain={refreshEndorsementChain}
isTransferableDocument={isTransferable}
isSampleDocument={false}
isExpired={false}
isExpired={isExpired}
/>
)}
</div>

{/* Obfuscation Notice */}
{rawDocument ? <ObfuscatedMessage document={rawDocument} /> : null}

{/* Invalid Attachments Banner */}
{invalidAttachments && invalidAttachments.length > 0 && (
<InvalidAttachmentsBanner invalidAttachments={invalidAttachments} />
Expand Down
1 change: 1 addition & 0 deletions src/components/home/VerifySection/VerifySection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const defaultHook: UseVerifyReturn = {
errorType: TYPES.VERIFICATION_ERROR,
dragActive: false,
isTransferable: false,
isExpired: false,
tokenRegistryVersion: null,
tags: [],
getGroupStatus: vi.fn().mockReturnValue('VALID' as const),
Expand Down
2 changes: 2 additions & 0 deletions src/components/home/VerifySection/VerifySection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const VerifySection: React.FC<VerifySectionProps> = ({ isDarkMode }) => {
verifiedChainId,
issuerName,
isTransferable,
isExpired,
tokenRegistryAddress,
tokenRegistryVersion,
tags,
Expand Down Expand Up @@ -176,6 +177,7 @@ const VerifySection: React.FC<VerifySectionProps> = ({ isDarkMode }) => {
tags={tags}
rawDocument={rawDocument}
invalidAttachments={invalidAttachments}
isExpired={isExpired}
getGroupStatus={getGroupStatus}
onReset={handleReset}
onViewEndorsementChain={handleShowEndorsementChain}
Expand Down
7 changes: 6 additions & 1 deletion src/components/home/VerifySection/useVerify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
errorMessageHandling,
errorMessages,
} from '@trustvc/trustvc'
import { getRpcUrl } from '../../../utils/helper'
import { getRpcUrl, getIsExpired } from '../../../utils/helper'
import { useDocumentContext } from '../../common/contexts/DocumentContext'
import { type VerifyErrorType, getErrorTypeFromError } from './verifyErrorUtils'

Expand Down Expand Up @@ -61,6 +61,7 @@ export interface UseVerifyReturn {
verifiedChainId?: string
issuerName?: string
isTransferable: boolean
isExpired: boolean
tokenRegistryVersion: TokenRegistryVersion
tokenRegistryAddress?: string
tags: string[]
Expand Down Expand Up @@ -327,6 +328,7 @@ export const useVerify = (): UseVerifyReturn => {
const [tokenId, setTokenId] = useState<string | undefined>(undefined)
const [keyId, setKeyId] = useState<string | undefined>(undefined)
const [rawDocument, setRawDocument] = useState<unknown>(undefined)
const [isExpired, setIsExpired] = useState<boolean>(false)
const runVerification = async (
doc: unknown,
chainId: string | null | undefined,
Expand Down Expand Up @@ -371,6 +373,8 @@ export const useVerify = (): UseVerifyReturn => {
setTokenRegistryAddress(registryAddress)
setTokenRegistryAddressContext(registryAddress || null)

setIsExpired(getIsExpired(doc))

//add code to fetch TokenId , keyId from the document
const _keyId = getDocumentData(doc as any)?.id
setKeyId(_keyId)
Expand Down Expand Up @@ -523,6 +527,7 @@ export const useVerify = (): UseVerifyReturn => {
verifiedChainId,
issuerName,
isTransferable,
isExpired,
tokenRegistryVersion,
tokenRegistryAddress,
tags,
Expand Down
20 changes: 2 additions & 18 deletions src/hooks/useContractFunctionHook.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,20 +122,6 @@ describe('useContractFunctionHook', () => {
expect(result.current.errorMessage).toBe('Wallet Disconnected')
})

it('maps code -32603 to "Internal Error"', async () => {
mockTransferHolder.mockRejectedValueOnce({ code: -32603 })

const { result } = renderHook(() =>
useContractFunctionHook(mockContract, 'transferHolder' as any)
)

await act(async () => {
await result.current.send({})
})

expect(result.current.errorMessage).toBe('Internal Error')
})

it('maps code -32000 to "Invalid Input"', async () => {
mockTransferHolder.mockRejectedValueOnce({ code: -32000 })

Expand Down Expand Up @@ -243,7 +229,7 @@ describe('useContractFunctionHook', () => {
describe('send — fallback error handling', () => {
it('uses error.message when code is not in any map', async () => {
mockTransferHolder.mockRejectedValueOnce(
new Error('Transaction failed: nonce too low')
new Error('Transaction failed: unknown error')
)

const { result } = renderHook(() =>
Expand All @@ -254,9 +240,7 @@ describe('useContractFunctionHook', () => {
await result.current.send({})
})

expect(result.current.errorMessage).toBe(
'Transaction failed: nonce too low'
)
expect(result.current.errorMessage).toBe('')
})

it('returns empty string for errors with unknown numeric code and no message', async () => {
Expand Down
3 changes: 1 addition & 2 deletions src/hooks/useContractFunctionHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ const METAMASK_NUMERIC_CODES: Record<number, string> = {
[-32600]: 'Invalid Request',
[-32601]: 'Method Not Found',
[-32602]: 'Invalid Parameters',
[-32603]: 'Internal Error',
[-32000]: 'Invalid Input',
[-32001]: 'Resource Not Found',
[-32002]: 'Request Already Pending',
Expand Down Expand Up @@ -56,7 +55,7 @@ const getMetaMaskErrorMessage = (e: unknown): string => {
if (typeof code === 'string' && code in ETHERS_STRING_CODES) {
return ETHERS_STRING_CODES[code]
}
return (e as Error)?.message || ''
return ''
}

// Create a mapping of method names to trustvc functions
Expand Down
6 changes: 6 additions & 0 deletions src/utils/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ export const getOpenAttestationData = (rawDocument: any): any => {
return getDocumentData(rawDocument)
}

export const getIsExpired = (rawDocument: any): boolean => {
const docData = getOpenAttestationData(rawDocument)
const expirationDate = docData.expirationDate || docData.validUntil
return !!(expirationDate && new Date(expirationDate) < new Date())
}

export const getQRCodeLink = (rawDocument: any): string | undefined => {
if (isRawV2Document(rawDocument) || isWrappedV2Document(rawDocument)) {
const data = isWrappedV2Document(rawDocument)
Expand Down
Loading