Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
78 changes: 78 additions & 0 deletions src/__tests__/core/verify.pol.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { describe, it, expect } from 'vitest';
import { verifyDocument } from '../../core/verify';
import { W3C_TRANSFERABLE_RECORD_POL } from '../fixtures/fixtures';
import { CHAIN_ID, SUPPORTED_CHAINS } from '../../utils/supportedChains';

// Public Polygon mainnet RPC — no API key required for read-only calls.
const POL_RPC_URL = process.env.POL_RPC || 'https://polygon-rpc.com';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

describe('Polygon (POL) network support', () => {
describe('CHAIN_ID and SUPPORTED_CHAINS', () => {
it('CHAIN_ID.pol should equal chain ID 137', () => {
expect(CHAIN_ID.pol).toBe('137');
});

it('CHAIN_ID.matic (backward-compat alias) should also equal 137', () => {
expect(CHAIN_ID.matic).toBe('137');
});

it('SUPPORTED_CHAINS[CHAIN_ID.pol] should have currency POL', () => {
expect(SUPPORTED_CHAINS[CHAIN_ID.pol].currency).toBe('POL');
});

it('SUPPORTED_CHAINS[CHAIN_ID.pol] and SUPPORTED_CHAINS[CHAIN_ID.matic] should be the same object', () => {
expect(SUPPORTED_CHAINS[CHAIN_ID.pol]).toBe(SUPPORTED_CHAINS[CHAIN_ID.matic]);
});
});

describe('W3C_TRANSFERABLE_RECORD_POL fixture structure', () => {
it('should have chain POL and chainId 137 in credentialStatus', () => {
expect(W3C_TRANSFERABLE_RECORD_POL.credentialStatus.tokenNetwork.chain).toBe('POL');
expect(W3C_TRANSFERABLE_RECORD_POL.credentialStatus.tokenNetwork.chainId).toBe(137);
});

it('should have a DataIntegrityProof with ecdsa-sd-2023 cryptosuite', () => {
expect(W3C_TRANSFERABLE_RECORD_POL.proof.type).toBe('DataIntegrityProof');
expect(W3C_TRANSFERABLE_RECORD_POL.proof.cryptosuite).toBe('ecdsa-sd-2023');
});

it('issuer should be did:web:didhost.vercel.app', () => {
expect(W3C_TRANSFERABLE_RECORD_POL.issuer).toBe('did:web:didhost.vercel.app');
});
});

describe('W3C_TRANSFERABLE_RECORD_POL — POL network routing', () => {
it(
'verifyDocument should return fragments for a POL credential (all verifiers run)',
{ timeout: 30000 },
async () => {
const fragments = await verifyDocument(W3C_TRANSFERABLE_RECORD_POL as any);

// All three verifier types should produce fragments — proves the document
// is recognised as a W3C VC with POL credentialStatus.
const names = fragments.map((f) => f.name);
expect(names).toContain('EcdsaW3CSignatureIntegrity');
expect(names).toContain('W3CCredentialStatus');
expect(names).toContain('W3CIssuerIdentity');
},
);

it(
'should reach Polygon mainnet (chain 137) for DOCUMENT_STATUS check',
{ timeout: 300000 },
async () => {
const fragments = await verifyDocument(W3C_TRANSFERABLE_RECORD_POL as any, {
rpcProviderUrl: POL_RPC_URL,
});

// W3CCredentialStatus fragment must be present — its presence proves the verifier
// attempted to check the token on Polygon mainnet (chain 137).
// INVALID = token not minted on-chain (expected for this test fixture).
// ERROR = RPC connection failed, meaning POL routing is broken.
const statusFragment = fragments.find((f) => f.name === 'W3CCredentialStatus');
expect(statusFragment).toBeDefined();
expect(statusFragment?.status).not.toBe('ERROR');
},
);
});
});
42 changes: 42 additions & 0 deletions src/__tests__/fixtures/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,48 @@ export const W3C_TRANSFERABLE_RECORD = freezeObject({
},
} as SignedVerifiableCredential);

// W3C Transferable Record fixture for Polygon mainnet (POL, chain ID 137).
// Signed with did:web:didhost.vercel.app using ecdsa-sd-2023.
// DOCUMENT_INTEGRITY passes offline (signature is valid).
// DOCUMENT_STATUS requires the token to be minted on Polygon mainnet at the tokenRegistry address.
export const W3C_TRANSFERABLE_RECORD_POL = freezeObject({
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v1.jsonld',
'https://w3id.org/security/bbs/v1',
'https://trustvc.io/context/transferable-records-context.json',
'https://w3id.org/security/data-integrity/v2',
],
type: ['VerifiableCredential'],
issuer: 'did:web:didhost.vercel.app',
issuanceDate: '2024-04-01T12:19:52Z',
expirationDate: '2029-12-03T12:19:52Z',
credentialStatus: {
type: 'TransferableRecords',
tokenNetwork: {
chain: 'POL',
chainId: 137,
},
tokenRegistry: '0xF94f95014304dC45B097439765A4D321bbE165c7',
tokenId: '522da91d80e973d3480107f394b33c48244c5ef1d455fe4a516efefbc37d0310',
},
credentialSubject: {
name: 'TrustVC',
birthDate: '2024-04-01T12:19:52Z',
type: ['PermanentResident', 'Person'],
},
id: 'urn:uuid:019e95f1-315b-7dda-bbfd-06283a6c54b1',
proof: {
type: 'DataIntegrityProof',
created: '2026-06-05T04:01:12Z',
verificationMethod: 'did:web:didhost.vercel.app#keys-1',
cryptosuite: 'ecdsa-sd-2023',
proofPurpose: 'assertionMethod',
proofValue:
'u2V0AhVhAcuRTpIgZ2E_2UOYXIljOZAaR9vgzyVMUFfYruJdE1sOWDGi_Vd379i5OUyMClfEesEo7U__9MRg3Er60lJeYN1gjgCQDBb61OwH2PchruRs5qrT5iv7CSjrQnJxbHZ-urSdV4a1YIEyqtohBGl2vFBrELmxss_WTmmGR-GMitByV-x3g8KzyjVhA3KUOWiH4RoGZHvxJnsu1fASwNyW9-vq2mltNJ9mgulfakW6vvKWyhre6rmivF5JD5o3oRl0fhaxzr0DHQQy21lhAkcGRcpuu8Sguh6nOO4wtI5Zst47zRBGh2r57WwAK55a2MDHiRYDs2BPoTtaR4d6tuO0FvIQDN9MK_tVlUCRY0lhAf6X8_Zvfmiu2mgDOyzSYSVhwE76E3h8nGDLQpOo6ihYH-1oJx71BkqTVjsSkYKtU3EfUGLqHSz_JMEQLc6dZMFhAgvlx5Fr60cSA1BIXnQ1RpZiHjDW4KNO1DCqgKUTPWy4qzPOO3H-1rWiuTh6fIPlV6YZoQZFcu55AUm0TVvc8IlhAywIiSmIi7mc43rM8jwPhLbO2FSMfKtTF1GIRM-58G16mwo8Dc2xpBOnELyL0kdEIMRMgBTFXBRpjULcI4xDNfFhAAxU-O91dPTxAgUT8FD-t2kwftvruUuXBqiriplVqHnLIaH0FeWqoXKHfhBbkZHMohFRu4PARA2LbsQpK8wXB5lhABkBefxVyG2CV_JCak8IZx_RveHJLCmfrJ5OF1j6h6766C3fQdtvKqAD_4wu8jM43ko9xAUOyxbze_52pC3ijbFhAlQs7t28G2g8i3aKeelFT_vZPkYOmJQQZy4t-aPQOLCPwgZ2rNIh-vsnDTIllPSpRTaxUdDJG-xf-gpPeekqiQ1hAbAsQrsy2yc7ZLIV4GPppijaQTDA5DjMyGaqgPh2SVeB2j-ah2wSyAlx_OzvN-uaeAxT1UAdEn11eWAWGL8Ew6VhAevByicUW72hxpt6N0aP8AO-AL5BKAm_Za_aUm0GA6FDbxS7LkB-tNs3v3gxyrw8H_Zml7Plm38DG4Jl99MOgLVhAamt5Z-HKYytwPqhy3ZEebq0BjtPJPM2eXxQUvJtpf9wQg-nI8QVpEP0gFRKybBNK-Mr0MSskP33uPwCfJUexK1hAqtAk5vm5P3ODX-wUOp9bqB5orcXbfjsSXb1tzfcTEK_QCUgeJd3tMVyf5y1VKkbVUcL2tVPo4gCorJWYDLS_0FhAz4-swM-Yw4aO6bhAfw389KvCA_Q755qEzpASrirhpxc1oLdSZnxUlP8hDQQaTI9is3LPPfduoaLTpLdDrKcWA4JnL2lzc3Vlcm0vaXNzdWFuY2VEYXRl',
},
} as SignedVerifiableCredential);

// Unsigned W3C v2.0 credential template — cryptosuite-agnostic. Used as input to signW3C
// for both did:web and did:key tests across ECDSA-SD-2023 and BBS-2023. Has no `proof`
// and no top-level `id` (generated at sign time). Tests override `issuer` when using
Expand Down
3 changes: 2 additions & 1 deletion src/utils/network/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const networks = [
'local',
'mainnet',
'pol',
'matic',
'maticmum',
'amoy',
Expand All @@ -17,4 +18,4 @@ export type networkName = (typeof networks)[number];

export type networkType = 'production' | 'test' | 'development';

export type networkCurrency = 'ETH' | 'MATIC' | 'XDC' | 'FREE' | 'ASTRON';
export type networkCurrency = 'ETH' | 'MATIC' | 'POL' | 'XDC' | 'FREE' | 'ASTRON';
18 changes: 10 additions & 8 deletions src/utils/supportedChains/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { networkCurrency, networkName, networkType } from './../network';
export enum CHAIN_ID {
local = '1337',
mainnet = '1',
matic = '137',
pol = '137',
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
matic = '137', // backward-compat alias for chain 137 (Polygon PoS)
amoy = '80002',
sepolia = '11155111',
xdc = '50',
Expand Down Expand Up @@ -58,19 +60,19 @@ export const SUPPORTED_CHAINS: supportedChains = {
explorerUrl: 'https://etherscan.io',
rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`,
},
[CHAIN_ID.matic]: {
id: CHAIN_ID.matic,
label: 'Polygon',
name: 'matic',
[CHAIN_ID.pol]: {
id: CHAIN_ID.pol,
label: 'Polygon (POL)',
name: 'pol',
type: 'production',
currency: 'MATIC',
currency: 'POL',
iconImage: iconPolygon,
explorerUrl: 'https://polygonscan.com',
rpcUrl: `https://polygon-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`,
gasStation: gasStation('https://gasstation.polygon.technology/v2'),
nativeCurrency: {
name: 'MATIC',
symbol: 'MATIC',
name: 'POL',
symbol: 'POL',
decimals: 18,
},
},
Expand Down
23 changes: 19 additions & 4 deletions src/utils/supportedChains/supportedChains.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,31 @@ describe('supportedChains', () => {
expect(explorerUrl).toBe('https://etherscan.io');
});

it('should matic chain info correctly', () => {
it('should return pol chain info for CHAIN_ID.pol (Polygon PoS mainnet)', () => {
const { id, name, type, currency, explorerUrl } = SUPPORTED_CHAINS[CHAIN_ID.pol];

expect(id).toBe(CHAIN_ID.pol);
expect(name).toBe('pol');
expect(type).toBe('production');
expect(currency).toBe('POL');
expect(explorerUrl).toBe('https://polygonscan.com');
});

it('should return pol chain info when accessing via CHAIN_ID.matic (backward-compat alias for chain 137)', () => {
const { id, name, type, currency, explorerUrl } = SUPPORTED_CHAINS[CHAIN_ID.matic];

expect(id).toBe(CHAIN_ID.matic);
expect(name).toBe('matic');
expect(id).toBe(CHAIN_ID.pol);
expect(name).toBe('pol');
expect(type).toBe('production');
expect(currency).toBe('MATIC');
expect(currency).toBe('POL');
expect(explorerUrl).toBe('https://polygonscan.com');
});

it('CHAIN_ID.pol and CHAIN_ID.matic should be the same chain ID value', () => {
expect(CHAIN_ID.pol).toBe(CHAIN_ID.matic);
expect(SUPPORTED_CHAINS[CHAIN_ID.pol]).toBe(SUPPORTED_CHAINS[CHAIN_ID.matic]);
});

it('should get polygon amoy chain info correctly', () => {
const { id, name, type, currency, explorerUrl, rpcUrl } = SUPPORTED_CHAINS[CHAIN_ID.amoy];

Expand Down
Loading