diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6a7537..bc224b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ jobs: uses: ./.github/workflows/tests.yml secrets: ANKR_API_KEY: ${{ secrets.ANKR_API_KEY }} + POLYGONSCAN_API_KEY: ${{ secrets.POLYGONSCAN_API_KEY }} linters: name: Linters diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 86e8c0a..13416db 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,6 +3,8 @@ on: secrets: ANKR_API_KEY: required: true + POLYGONSCAN_API_KEY: + required: true env: NODE_ENV: ci @@ -34,6 +36,7 @@ jobs: - run: npm run test env: ANKR_API_KEY: ${{ secrets.ANKR_API_KEY }} + POLYGONSCAN_API_KEY: ${{ secrets.POLYGONSCAN_API_KEY }} test-build: name: Test Build diff --git a/.gitignore b/.gitignore index e562349..db683df 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ testem.log .DS_Store Thumbs.db .env +#local yalc configuration for help build and test local environment +.yalc/ +yalc.lock \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f4f4d94..d22b4e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@tradetrust-tt/token-registry-v4": "npm:@tradetrust-tt/token-registry@^4.16.0", "@tradetrust-tt/token-registry-v5": "npm:@tradetrust-tt/token-registry@^5.5.0", "@tradetrust-tt/tradetrust": "^6.10.3", - "@tradetrust-tt/tt-verify": "^9.7.4", + "@tradetrust-tt/tt-verify": "^9.7.5", "@trustvc/document-store": "^1.0.3", "@trustvc/w3c": "^2.2.0", "@trustvc/w3c-context": "^2.2.0", @@ -6780,9 +6780,9 @@ "license": "ISC" }, "node_modules/@tradetrust-tt/tt-verify": { - "version": "9.7.4", - "resolved": "https://registry.npmjs.org/@tradetrust-tt/tt-verify/-/tt-verify-9.7.4.tgz", - "integrity": "sha512-d/mfIKiuxRaFZUPSH/Cz78mj+qEPAoSydgzlPf6o3WuFiHaxLIg74SxgnUoxsoir806e5RkxYYFkZehBCCOBHg==", + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@tradetrust-tt/tt-verify/-/tt-verify-9.7.5.tgz", + "integrity": "sha512-LoVrJLiWB9ZnMHoe7IGBAc4edK2upKYsfCRgo9T7VNLo3XiBM4cIr25RQ5e1AQ58m+3gKWRnpP9ZKj0qqN+xFg==", "license": "Apache-2.0", "dependencies": { "@tradetrust-tt/dnsprove": "^2.21.0", @@ -23657,7 +23657,6 @@ "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { diff --git a/package.json b/package.json index c409552..b001ef1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@trustvc/trustvc", - "version": "2.13.0", + "version": "2.14.0", "description": "TrustVC library", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -120,7 +120,7 @@ "@tradetrust-tt/token-registry-v4": "npm:@tradetrust-tt/token-registry@^4.16.0", "@tradetrust-tt/token-registry-v5": "npm:@tradetrust-tt/token-registry@^5.5.0", "@tradetrust-tt/tradetrust": "^6.10.3", - "@tradetrust-tt/tt-verify": "^9.7.4", + "@tradetrust-tt/tt-verify": "^9.7.5", "@trustvc/document-store": "^1.0.3", "@trustvc/w3c": "^2.2.0", "@trustvc/w3c-context": "^2.2.0", diff --git a/src/__tests__/core/verify.amoy.test.ts b/src/__tests__/core/verify.amoy.test.ts new file mode 100644 index 0000000..4152650 --- /dev/null +++ b/src/__tests__/core/verify.amoy.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect } from 'vitest'; +import { CHAIN_ID, SUPPORTED_CHAINS } from '../../utils/supportedChains'; + +import amoyOaTokenRegistryMinted from '../fixtures/amoy-oa-token-registry-minted.json'; +import amoyW3cTransferableRecordMinted from '../fixtures/amoy-w3c-transferable-record-minted.json'; +import { + isMintedFixtureReady, + oaTokenRegistryMintedTests, + w3cTransferableRecordMintedTests, +} from './verify.polygon-network.helpers'; + +const AMOY_RPC_URL = process.env.AMOY_RPC || 'https://rpc-amoy.polygon.technology/'; + +describe('Polygon Amoy (testnet) network support', () => { + describe('CHAIN_ID and SUPPORTED_CHAINS', () => { + it('CHAIN_ID.amoy should equal chain ID 80002', () => { + expect(CHAIN_ID.amoy).toBe('80002'); + }); + + it('SUPPORTED_CHAINS[CHAIN_ID.amoy] should have currency POL', () => { + expect(SUPPORTED_CHAINS[CHAIN_ID.amoy].currency).toBe('POL'); + }); + + it('SUPPORTED_CHAINS[CHAIN_ID.amoy] should have name amoy', () => { + expect(SUPPORTED_CHAINS[CHAIN_ID.amoy].name).toBe('amoy'); + }); + }); + + describe.skipIf(!isMintedFixtureReady(amoyOaTokenRegistryMinted))( + 'amoy-oa-token-registry-minted', + () => { + oaTokenRegistryMintedTests({ + fixture: amoyOaTokenRegistryMinted, + rpcUrl: AMOY_RPC_URL, + expectedNetworkChainId: '80002', + includeSignatureTamperTest: true, + }); + }, + ); + + describe.skipIf(!isMintedFixtureReady(amoyW3cTransferableRecordMinted))( + 'amoy-w3c-transferable-record-minted', + () => { + w3cTransferableRecordMintedTests({ + fixture: amoyW3cTransferableRecordMinted, + rpcUrl: AMOY_RPC_URL, + chainId: 80002, + }); + }, + ); +}); diff --git a/src/__tests__/core/verify.pol.test.ts b/src/__tests__/core/verify.pol.test.ts new file mode 100644 index 0000000..acb3916 --- /dev/null +++ b/src/__tests__/core/verify.pol.test.ts @@ -0,0 +1,171 @@ +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'; + +import polW3cTransferableRecordMinted from '../fixtures/pol-w3c-transferable-record-minted.json'; +import polOaTokenRegistryMinted from '../fixtures/pol-oa-token-registry-minted.json'; +import polW3cVerifiableDocument from '../fixtures/pol-w3c-verifiable-document.json'; +import { + expectTransferableRecordError, + isMintedFixtureReady, + oaTokenRegistryMintedTests, + w3cTransferableRecordMintedTests, +} from './verify.polygon-network.helpers'; + +const POL_RPC_URL = process.env.POL_RPC || 'https://polygon-bor-rpc.publicnode.com'; + +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', + { timeout: 30000 }, + async () => { + const fragments = await verifyDocument(W3C_TRANSFERABLE_RECORD_POL as any); + 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, + }); + const statusFragment = fragments.find((f) => f.name === 'W3CCredentialStatus'); + expect(statusFragment).toBeDefined(); + expect(statusFragment?.status).not.toBe('ERROR'); + }, + ); + + it.each([ + { + label: 'tokenRegistry is missing', + credentialStatus: { + ...W3C_TRANSFERABLE_RECORD_POL.credentialStatus, + tokenRegistry: '', + }, + }, + { + label: 'tokenNetwork.chainId is missing', + credentialStatus: { + ...W3C_TRANSFERABLE_RECORD_POL.credentialStatus, + tokenNetwork: { chain: 'POL', chainId: '' }, + }, + }, + ])('should return ERROR when $label', async ({ credentialStatus }) => { + const tampered: any = { + ...W3C_TRANSFERABLE_RECORD_POL, + credentialStatus, + }; + expectTransferableRecordError(await verifyDocument(tampered)); + }); + + it('should return INVALID for DOCUMENT_INTEGRITY when proof is tampered', async () => { + const tampered: any = { + ...W3C_TRANSFERABLE_RECORD_POL, + proof: { ...W3C_TRANSFERABLE_RECORD_POL.proof, proofValue: 'u2V0AhVhAINVALIDPROOF' }, + }; + const fragments = await verifyDocument(tampered); + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'EcdsaW3CSignatureIntegrity', status: 'INVALID' }), + ]), + ); + }); + }); + + describe.skipIf(!isMintedFixtureReady(polW3cTransferableRecordMinted))( + 'pol-w3c-transferable-record-minted', + () => { + w3cTransferableRecordMintedTests({ + fixture: polW3cTransferableRecordMinted, + rpcUrl: POL_RPC_URL, + chainId: 137, + }); + }, + ); + + describe.skipIf(!isMintedFixtureReady(polOaTokenRegistryMinted))( + 'pol-oa-token-registry-minted', + () => { + oaTokenRegistryMintedTests({ + fixture: polOaTokenRegistryMinted, + rpcUrl: POL_RPC_URL, + }); + }, + ); + + describe.skipIf(!isMintedFixtureReady(polW3cVerifiableDocument))( + 'pol-w3c-verifiable-document — structural (offline)', + () => { + it('all verifier types should produce fragments', async () => { + const fragments = await verifyDocument(polW3cVerifiableDocument as any); + expect(fragments.map((f) => f.name)).toContain('W3CIssuerIdentity'); + }); + + it('W3CCredentialStatus should be SKIPPED and W3CEmptyCredentialStatus VALID when no credentialStatus', async () => { + const doc: any = { ...polW3cVerifiableDocument }; + delete doc.credentialStatus; + const fragments = await verifyDocument(doc); + const statusFragments = fragments.filter((f) => f.type === 'DOCUMENT_STATUS'); + expect(statusFragments.find((f) => f.name === 'W3CCredentialStatus')?.status).toBe( + 'SKIPPED', + ); + expect(statusFragments.find((f) => f.name === 'W3CEmptyCredentialStatus')?.status).toBe( + 'VALID', + ); + expect(statusFragments.every((f) => f.status !== 'INVALID')).toBe(true); + }); + + it('should return INVALID for DOCUMENT_INTEGRITY when proof is tampered', async () => { + const doc = polW3cVerifiableDocument as any; + if (!doc.proof) return; + const tampered: any = { ...doc, proof: { ...doc.proof, proofValue: 'uINVALID' } }; + const fragments = await verifyDocument(tampered); + const integrityFragment = fragments.find( + (f) => f.type === 'DOCUMENT_INTEGRITY' && f.status !== 'SKIPPED', + ); + expect(integrityFragment?.status).toBe('INVALID'); + }); + }, + ); +}); diff --git a/src/__tests__/core/verify.polygon-network.helpers.ts b/src/__tests__/core/verify.polygon-network.helpers.ts new file mode 100644 index 0000000..2c52aa4 --- /dev/null +++ b/src/__tests__/core/verify.polygon-network.helpers.ts @@ -0,0 +1,206 @@ +import { it, expect } from 'vitest'; +import { verifyDocument } from '../../core/verify'; + +const ZERO_HASH = '0000000000000000000000000000000000000000000000000000000000000000'; +const ZERO_TOKEN_ID = ZERO_HASH; + +export function isMintedFixtureReady(fixture: object): boolean { + return Object.keys(fixture).length > 0; +} + +type W3cTransferableRecordMintedOptions = { + fixture: unknown; + rpcUrl: string; + chainId: number; +}; + +export function w3cTransferableRecordMintedTests({ + fixture, + rpcUrl, + chainId, +}: W3cTransferableRecordMintedOptions): void { + it(`should have chain POL and chainId ${chainId}`, () => { + const doc = fixture as { + credentialStatus: { tokenNetwork: { chain: string; chainId: number } }; + }; + expect(doc.credentialStatus.tokenNetwork.chain).toBe('POL'); + expect(doc.credentialStatus.tokenNetwork.chainId).toBe(chainId); + }); + + it('should return SKIPPED for W3CCredentialStatus (not a status-list credential)', async () => { + const fragments = await verifyDocument(fixture as Parameters[0]); + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'W3CCredentialStatus', status: 'SKIPPED' }), + ]), + ); + }); + + it( + 'should return VALID for all fragments (signature + minted token + issuer)', + { timeout: 300000 }, + async () => { + const fragments = await verifyDocument(fixture as Parameters[0], { + rpcProviderUrl: rpcUrl, + }); + const integrity = fragments.find( + (f) => f.type === 'DOCUMENT_INTEGRITY' && f.status === 'VALID', + ); + const status = fragments.find((f) => f.name === 'TransferableRecords'); + const identity = fragments.find((f) => f.name === 'W3CIssuerIdentity'); + expect(integrity).toBeDefined(); + expect(status?.status).toBe('VALID'); + expect(identity?.status).toBe('VALID'); + }, + ); + + it( + 'should return INVALID for TransferableRecords when tokenId is tampered', + { timeout: 300000 }, + async () => { + const doc = fixture as { + credentialStatus: Record; + }; + const tampered = { + ...(fixture as Record), + credentialStatus: { + ...doc.credentialStatus, + tokenId: ZERO_TOKEN_ID, + }, + }; + const fragments = await verifyDocument( + tampered as unknown as Parameters[0], + { rpcProviderUrl: rpcUrl }, + ); + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'TransferableRecords', status: 'INVALID' }), + ]), + ); + }, + ); +} + +type OaTokenRegistryMintedOptions = { + fixture: unknown; + rpcUrl: string; + /** When set, asserts OA `network.chainId` decodes to this value (e.g. Amoy `80002`). */ + expectedNetworkChainId?: string; + /** When true, includes DOCUMENT_STATUS invalidation when signature hashes are tampered. */ + includeSignatureTamperTest?: boolean; +}; + +export function oaTokenRegistryMintedTests({ + fixture, + rpcUrl, + expectedNetworkChainId, + includeSignatureTamperTest = false, +}: OaTokenRegistryMintedOptions): void { + if (expectedNetworkChainId) { + it(`network.chainId should decode to ${expectedNetworkChainId}`, () => { + const doc = fixture as { data?: { network?: { chainId?: string } } }; + const chainId = (doc.data?.network?.chainId ?? '').split(':').pop(); + expect(chainId).toBe(expectedNetworkChainId); + }); + } + + it('should return VALID for OpenAttestationHash (pure hash check)', async () => { + const fragments = await verifyDocument(fixture as Parameters[0], { + rpcProviderUrl: rpcUrl, + }); + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'OpenAttestationHash', status: 'VALID' }), + ]), + ); + }); + + it('should return INVALID for OpenAttestationHash when document data is tampered', async () => { + const doc = fixture as { data: Record }; + const tampered = { + ...(fixture as Record), + data: { ...doc.data, TAMPERED: true }, + }; + const fragments = await verifyDocument( + tampered as unknown as Parameters[0], + { rpcProviderUrl: rpcUrl }, + ); + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'OpenAttestationHash', status: 'INVALID' }), + ]), + ); + }); + + it('OpenAttestationEthereumTokenRegistryStatus verifier should be selected (not skipped)', async () => { + const fragments = await verifyDocument(fixture as Parameters[0], { + rpcProviderUrl: rpcUrl, + }); + const statusFragment = fragments.find( + (f) => f.name === 'OpenAttestationEthereumTokenRegistryStatus', + ); + expect(statusFragment?.status).not.toBe('SKIPPED'); + }); + + it( + 'should return VALID for DOCUMENT_INTEGRITY and DOCUMENT_STATUS', + { timeout: 300000 }, + async () => { + const fragments = await verifyDocument(fixture as Parameters[0], { + rpcProviderUrl: rpcUrl, + }); + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'OpenAttestationHash', status: 'VALID' }), + expect.objectContaining({ + name: 'OpenAttestationEthereumTokenRegistryStatus', + status: 'VALID', + }), + ]), + ); + }, + ); + + if (includeSignatureTamperTest) { + it( + 'should return INVALID for DOCUMENT_STATUS when tokenId is tampered', + { timeout: 300000 }, + async () => { + const doc = fixture as { signature: Record }; + const tampered = { + ...(fixture as Record), + signature: { + ...doc.signature, + targetHash: ZERO_HASH, + merkleRoot: ZERO_HASH, + }, + }; + const fragments = await verifyDocument(tampered as Parameters[0], { + rpcProviderUrl: rpcUrl, + }); + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'OpenAttestationEthereumTokenRegistryStatus', + status: 'INVALID', + }), + ]), + ); + }, + ); + } +} + +export function expectTransferableRecordError( + fragments: Awaited>, +): void { + expect(fragments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: 'TransferableRecords', + status: 'ERROR', + reason: expect.objectContaining({ codeString: 'UNRECOGNIZED_DOCUMENT' }), + }), + ]), + ); +} diff --git a/src/__tests__/fixtures/amoy-oa-token-registry-minted.json b/src/__tests__/fixtures/amoy-oa-token-registry-minted.json new file mode 100644 index 0000000..ea26b38 --- /dev/null +++ b/src/__tests__/fixtures/amoy-oa-token-registry-minted.json @@ -0,0 +1,34 @@ +{ + "version": "https://schema.openattestation.com/2.0/schema.json", + "data": { + "version": "e701574c-bce2-4e96-8c5c-cb9d02c4ce28:string:https://schema.openattestation.com/2.0/schema.json", + "$template": { + "name": "c1fe588b-da05-45af-afee-3680a9414b39:string:GOVTECH_DEMO", + "type": "f2779150-a9f7-48be-a4f4-7685ca9b6f33:string:EMBEDDED_RENDERER", + "url": "c3b6972d-e54a-43cf-83c4-11d2063207a6:string:https://demo-renderer.opencerts.io" + }, + "issuers": [ + { + "name": "14003050-4b55-47d2-9cc5-24e17b05275d:string:TrustVC Amoy Issuer", + "tokenRegistry": "ab2394fd-f6b9-4094-8f3c-37da1515abe7:string:0xa5f9a7106a599E4caAFacE6872da097aa802Cc64", + "identityProof": { + "type": "ef0308c9-0a7f-4fae-a38f-7d1f3797712b:string:DNS-TXT", + "location": "c8155f34-c97a-4745-a576-7e52eee1f5f0:string:example.tradetrust.io" + } + } + ], + "recipient": { + "name": "0f987694-19cf-4b29-a5ab-3aa7cde13246:string:TrustVC Amoy Test" + }, + "network": { + "chain": "f999cdb8-445e-4e0e-92b1-5d554bc3c852:string:POL", + "chainId": "a5a27920-e63b-49a9-b4df-2f2af37a8c24:string:80002" + } + }, + "signature": { + "type": "SHA3MerkleProof", + "targetHash": "8d4ddb4f0252c1d61f0b72ad585573317c2d3f9268ebbd6d785699e12ebbb077", + "proof": [], + "merkleRoot": "8d4ddb4f0252c1d61f0b72ad585573317c2d3f9268ebbd6d785699e12ebbb077" + } +} diff --git a/src/__tests__/fixtures/amoy-w3c-transferable-record-minted.json b/src/__tests__/fixtures/amoy-w3c-transferable-record-minted.json new file mode 100644 index 0000000..7e9d426 --- /dev/null +++ b/src/__tests__/fixtures/amoy-w3c-transferable-record-minted.json @@ -0,0 +1,64 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://w3id.org/security/data-integrity/v2", + "https://trustvc.io/context/render-method-context-v2.json", + "https://trustvc.io/context/promissory-note.json", + "https://trustvc.io/context/transferable-records-context.json", + "https://trustvc.io/context/qrcode-context.json" + ], + "renderMethod": [ + { + "type": "EMBEDDED_RENDERER", + "templateName": "PROMISSORY_NOTE", + "id": "https://generic-templates.tradetrust.io" + } + ], + "credentialSubject": { + "type": [ + "PromissoryNote" + ], + "drawerCompanyName": "XYZ Exports Pvt. Ltd.", + "drawerCompanyNo": "CIN-XYZ1234567", + "drawerJurisdiction": "India", + "drawerWalletAddress": "0x433097a1C1b8a3e9188d8C54eCC057B1D69f1638", + "drawerPlaceOfIssue": "Mumbai, India", + "draweeCompanyName": "XYZ Imports Ltd.", + "draweeCompanyNo": "REG-XYZ9876543", + "draweeJurisdiction": "California, United States", + "draweeWalletAddress": "0xca93690bb57eeab273c796a9309246bc0fb93649", + "dueDate": "2025-06-19", + "currency": "USD", + "amount": "50,000.00", + "clause": "Payment to be made in full without set-off or counterclaim, subject to terms agreed between Drawer and Drawee.", + "signerName": "John Doe", + "signerPosition": "Chief Financial Officer", + "signerTimeStamp": "2025-06-10", + "logo": "data:image/png;base64,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", + "pNoteId": "PN-9081-2231-SGP", + "commitmentDate": "2025-12-10" + }, + "type": [ + "VerifiableCredential" + ], + "credentialStatus": { + "type": "TransferableRecords", + "tokenNetwork": { + "chain": "POL", + "chainId": 80002 + }, + "tokenRegistry": "0xa5f9a7106a599E4caAFacE6872da097aa802Cc64", + "tokenId": "d320d1e7eaf6a0f9ec185c8b25470d027115ef2059e5b1bcb41cde09f799be75" + }, + "issuer": "did:web:trustvc.github.io:did:1", + "validFrom": "2024-04-01T12:19:52Z", + "id": "urn:uuid:019ea8ea-2cf5-7662-9ab6-4b8b261174c7", + "proof": { + "type": "DataIntegrityProof", + "created": "2026-06-08T20:26:19Z", + "verificationMethod": "did:web:trustvc.github.io:did:1#multikey-1", + "cryptosuite": "ecdsa-sd-2023", + "proofPurpose": "assertionMethod", + "proofValue": "u2V0AhVhA2a6bF4PPP3Mnid815CspDt-DMcGPm-iDwkYPBY2KgbprArYR9urtjfaaarNzxxsnBVuLUVJRmgBJdPAKkPhp0lgjgCQCcxtBLaHQBHKdzPUjrOoAUVnAqpYInt6hFitn3f96aotYIPgjo4q0O9-JKXdEK5MyNbPcr2XTCY0MD2hJ_gWjp6hdmB9YQCP8qUh7sZnTA9ATmpuIjMPwVkUQTACNPWiM1LYUHlPMxSVOChUnWnCXL7DRs2ZcwbDkBzLjHOoDMi1JxdjnzJ9YQG_tUBTFL8R9cKH04hvAdaNi71BK8qoQqNNe0f_89R2yuWR1fFe2dtI9edydW6qiNvSwXtuCFAhRQOZqlt_8Q3lYQPob4-pdjgIMUnLjYP64LYKq45uwgd6XyXGwUO1l7W7GpO_kgAKuM99t1MXp6IB6A9XbRNYaCSiJAgCbyqodHbJYQNPsYqo-lf-CPvcv0fC2kOXvaFQsouhiuCVdWSYXEsVCvxw96-Z-lZm9ywHLl1ZTCefxgikLkcYnLod232LGVqJYQDdGVqY1bWtTC6A_knvz9l_JGLJlFE_VFg4Ke4_Ipmzfu9mBQU-47e_11FB9ksO4cvvTstFWuxpAEC_JP7RqRJ5YQA3cxqybEuYIqSRkyDvmSaYIdcLrOsNWXL2kI-24SvhplRJH5S44BVPK_cvVAtHXLtDurfqeHjrmRnC0Cid_0DdYQEi0vKYmHUQGVuJuvUpmtoWsPCiW3uXUhrQUzXqK02YpLwV9Rv7mpi8Ttf4ZKyB94sdTh6YIJ9I3-A9OXjQsvFxYQDAl3ueb3hT243goN3x8oZWZ10whyuR8FOGUCBH0OLHBBvFDGvYJxgGpoyTanZP3rda5KTGp8FwRg0GPWJYL4wRYQJxc7azJM-xLzBNvdaRkXo79nFW44uNVh5MdURrVr8o-ZtBcSEAih0M22Si56IHwJEOdEeBg7-gR7T5Id1ERSpZYQMIuWoTRj1efe2uYiZyf1Ls65uCjTmdLXlstDDgGNVSeZY25cKuxiFORPkWDzxYF-f8UMXzN933O3f-eeB5bnPNYQGbc5MuhppkPQMSTO720vxYfXNSTT9efTsnx6JViQGsyNKlk01Uq5Q7uEyAl60bBFIfOCvwS8Fi5wlSs_dPb-uFYQMbKw4rwwtb9a4DGVHMOJ5jfDQ4sdHFUY3OC3tFf5L8M_shEJSbj_b6_9eNAuZ2GYIrKXM_md3UiWYHQX73TIXdYQGPFJpDLxCtFLpwd1Zc9_yXpRhG1TKKX9yJZArFM8x0ZZwAHSMUXt6mqADMDR1kd3o1jT4WsKZ9am3pWugyDFtJYQA5iROJ_BPhMBm1gPmJF71XcIFhMRSbxq_EZIKHTN4zrNKvJth4_tPir8IQpES8DxuV_25bYvmUMTh2RA3XAMlxYQBenzrw26uucwbHZ7wo_QpkLQFroVPJhBbgPoOHyO9nwkm77jOagHUHapviZdyCCXF6e_zia4dWek5kB5DmcMglYQJO44U9o4BUZ59DFch5dYDIdk7nUSKAd4TUK9QTYQMjauUaNAxpFeuwZdAh8oSSfQ3chjFmg1jpn9kesq43xWQxYQABM0ACr639sNX4x3jH_NRkfxFbiKDQJ-DukfF8UYD_Y1aY-j47JcHpIJhu1WhafVCqKkrRvr0dZ3PymQZnt7LVYQCXMa2Z1j_p28XLE0SqvKae10V-q-dwc_hGI_Eyx_obbPEw02ErzOd4dMpUTffsIU3Tauc-UwNs7iK_FdJYzaBtYQOSssxQPNn1qMR_AHonbY_UPNYb6aY9ek4HMrh2FEUWBllKOO_-j5nRIs1M_e9yrmMaxPYJWi4wxpaFgR1SkXZhYQBGUhHcZ7fEHYGkCuWcofP8VNs_z2FifSzQ-U2pok0Mz4M6hZ1Kn-r6c6AD0KyJUZq7yARCa6ZVwJB2OoF9Wy0hYQI7QjPqTZoDArXYrSa21EN_5zmCn7yWeXzyyDe3bEsV0jTbuQ14sTj5-OCXsqf8PITGVbWp2cZgE6dBch4KRR7hYQLcw1Pcl94lXpeNzjPIg3jtPgklA9_R3fNthHSt1PHOkEznmowHnZgZWo-3C4oyeD8Lpt0ARBOYSqbUqLGk6mGtYQIrpRQ5AyxUYZGtEXvcMe1Vbyc7Tn1rRB7Ev6Z0h__iAum798w81mnJU5eIlyRwAyIQ0cwnGf6Zo74JcQyh4sH9YQIfhd4d0Y23Cufs2VQBHi86tlqMtrHk2RTJy455m_DzX0qm_nQty90WDKDC92A3rRxPqi_ZxX2c-ChLrPoxLja9YQF_6ODroXBzfXkm9MYcyQVZ0SsfLdPliMmy9gem22PV7fdrSNvPY0hiqs6qPlFuVt2zBNEpf03R6axXzV2iAJhpYQNXGgigVpkgO8BrYvQWvDMKslyi6oQwG3i2AjcdzxCot72c79Kj-54G1kMMZs1E5jbyYuYzTOuFcXUjbJp0taYpYQKTpzL3cXC7dXgGDN6ke18BJ6pRvSuTAqK1UqIbfq6PyP3pys3fm_O4xsbEvAJjXddMBEigQ3dQOoQiBPX1GRrVYQMD5sLp7muEOz0LBub6_bJDvJmOwL2duPrU3FVXN1po0pcddNcBCYB7XGPHK-ptrqGpVIX4jyxJprKLQR0bsyJZYQJBIZ6wZV0yCPzN7p9q6-qMOCb1wa5jIew9NnG9akwDMiAciLZuR5TE0NQoalY7m7aICiM8lbEVByAdddwECxJZYQJXbLhIt1YYHRxAygGPM2m-i7E98ySYHLZ99MdZ6SSMvP_FnakQMlDeHoN7Z5V9f9von_QoqS4mijhOhR5wsfKpYQKFsz3VVgvYOYtvs-6mwX97tTOv6OlYYwa5NTzptPz_tttNjbtqT_8D0cwCVWjlc4M4daOmjXZo7KmXtqDnh3WGCZy9pc3N1ZXJqL3ZhbGlkRnJvbQ" + } + } \ No newline at end of file diff --git a/src/__tests__/fixtures/fixtures.ts b/src/__tests__/fixtures/fixtures.ts index 74c1f34..0ad8a74 100644 --- a/src/__tests__/fixtures/fixtures.ts +++ b/src/__tests__/fixtures/fixtures.ts @@ -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 diff --git a/src/__tests__/fixtures/pol-oa-token-registry-minted.json b/src/__tests__/fixtures/pol-oa-token-registry-minted.json new file mode 100644 index 0000000..f9d66ad --- /dev/null +++ b/src/__tests__/fixtures/pol-oa-token-registry-minted.json @@ -0,0 +1,33 @@ +{ + "version": "https://schema.openattestation.com/2.0/schema.json", + "data": { + "$template": { + "name": "d08b56ed-9f91-44d8-8748-4ef17c10084e:string:GOVTECH_DEMO", + "type": "17fbeec8-9712-4e3a-bf17-dd74ba348644:string:EMBEDDED_RENDERER", + "url": "a1061e20-318c-4bde-89d5-1ec42ef1eea9:string:https://demo-renderer.opencerts.io" + }, + "issuers": [ + { + "name": "7c8c3632-df92-492d-84fa-bb59363c9cf0:string:TrustVC POL Issuer", + "tokenRegistry": "0a0be8f4-c4af-4ec2-8bd8-ef787f72ea0a:string:0x0961d9C2dA9a7105fDFC9DC4ec45951C024F88B0", + "identityProof": { + "type": "21a24a77-34fc-4d8a-9cd4-7678304ce11d:string:DNS-TXT", + "location": "e201feac-f6cc-466c-98ae-3cc35dd03891:string:example.tradetrust.io" + } + } + ], + "recipient": { + "name": "9847a1cf-8151-42d7-b240-607d7a0f2fb7:string:TrustVC POL Test" + }, + "network": { + "chain": "da056415-f675-49d8-942b-6c4c76bf664e:string:POL", + "chainId": "39922aa7-aaa1-49b8-a77c-d230eae018fb:string:137" + } + }, + "signature": { + "type": "SHA3MerkleProof", + "targetHash": "5382d7c3c19d4b5730537a234b01b2084fdd71c3196dd0f5df00b23d9756d8d0", + "proof": [], + "merkleRoot": "5382d7c3c19d4b5730537a234b01b2084fdd71c3196dd0f5df00b23d9756d8d0" + } +} \ No newline at end of file diff --git a/src/__tests__/fixtures/pol-w3c-transferable-record-minted.json b/src/__tests__/fixtures/pol-w3c-transferable-record-minted.json new file mode 100644 index 0000000..0f7600a --- /dev/null +++ b/src/__tests__/fixtures/pol-w3c-transferable-record-minted.json @@ -0,0 +1,64 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://w3id.org/security/data-integrity/v2", + "https://trustvc.io/context/render-method-context-v2.json", + "https://trustvc.io/context/promissory-note.json", + "https://trustvc.io/context/transferable-records-context.json", + "https://trustvc.io/context/qrcode-context.json" + ], + "renderMethod": [ + { + "type": "EMBEDDED_RENDERER", + "templateName": "PROMISSORY_NOTE", + "id": "https://generic-templates.tradetrust.io" + } + ], + "credentialSubject": { + "type": [ + "PromissoryNote" + ], + "drawerCompanyName": "XYZ Exports Pvt. Ltd.", + "drawerCompanyNo": "CIN-XYZ1234567", + "drawerJurisdiction": "India", + "drawerWalletAddress": "0x433097a1C1b8a3e9188d8C54eCC057B1D69f1638", + "drawerPlaceOfIssue": "Mumbai, India", + "draweeCompanyName": "XYZ Imports Ltd.", + "draweeCompanyNo": "REG-XYZ9876543", + "draweeJurisdiction": "California, United States", + "draweeWalletAddress": "0xca93690bb57eeab273c796a9309246bc0fb93649", + "dueDate": "2025-06-19", + "currency": "USD", + "amount": "50,000.00", + "clause": "Payment to be made in full without set-off or counterclaim, subject to terms agreed between Drawer and Drawee.", + "signerName": "John Doe", + "signerPosition": "Chief Financial Officer", + "signerTimeStamp": "2025-06-10", + "logo": "data:image/png;base64,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", + "pNoteId": "PN-9081-2231-SGP", + "commitmentDate": "2025-12-10" + }, + "type": [ + "VerifiableCredential" + ], + "credentialStatus": { + "type": "TransferableRecords", + "tokenNetwork": { + "chain": "POL", + "chainId": 137 + }, + "tokenRegistry": "0x0961d9C2dA9a7105fDFC9DC4ec45951C024F88B0", + "tokenId": "1174afa500e1b265450b55200cb16487e92e7c5410cff84b693eda59194b10fd" + }, + "issuer": "did:web:trustvc.github.io:did:1", + "validFrom": "2024-04-01T12:19:52Z", + "id": "urn:uuid:019ea68d-006c-7887-9ea8-3f54f562ac65", + "proof": { + "type": "DataIntegrityProof", + "created": "2026-06-08T09:25:19Z", + "verificationMethod": "did:web:trustvc.github.io:did:1#multikey-1", + "cryptosuite": "ecdsa-sd-2023", + "proofPurpose": "assertionMethod", + "proofValue": "u2V0AhVhAHlKJtL9UV-_snL5rOlLyCznZu_oNyzOE3s6XAFR4USM0D68IJsm6qf5M01opyFXDW3Xn8mtLb6AZEgRWH6C4fVgjgCQD-WN_Q-KfWRbl6pQ5HB4u4khdD6xR1mPuWcTcLYNwPkhYIJy9GT0cT7-l_I9XOnoDDJyy5IV9LvUECuSSvNoluakUmB9YQDn8hL0e7mW-1DC0i74pcWcskNkMU82TgfA0P1eeJLFHdkuhd-HhqWH2wTd9CG48KJ0UvVkt3jzPXWHMhkkxzMRYQCnL8QQIZ-Ki2Cjf1_u43B3AM5BwAAXiC2NkfK6AhcWVoBnVtEBN2vBZc4pWJveWd_qVJU5ALqL3Dz1JmIAwkHlYQNfW5JmtYILhhIZufZ9j4FX9IA4x9hk0ULLhEenxAaYlfnYnewVsI3AvRdGmJc6QwL1PTR-hTVG3txGVL3SNWBtYQABtgC4W-r6VH649Et1xGg1NB2VE_ZshNSK2yQN1hDok36f6bwcAJ1n91y52u3XmpxOYyGmsPM8yp6udzO9YRwtYQAQz4UhnkiIQovoJHD6Y-kq3X2YegxXFTnh4zH_qNWvjhZVa0nx9CCDvBDpmlH0lWqSAkNiTTx0XWE298vAK54xYQGFfs4um2IWamhe_4da7qsInmTR5VZgIhqPlkfaQd4-Vw9INv8orNrqXOhIMssCG1MCAr1vnsM6zVq75fVnABP5YQA0idgyw13Uwb8N4e_CFH_kIX9M21lLmeZ_ZWA9z5HfsN8qxBEU4VeODqi97QjgRtVBKL4ijHXT1_-YZyZZByB5YQHcGLRUvnezhWKd8kDw2cn12hfnc6rLlyy3dXUDvUzfr6n3zUqCA_cDopBbmhtGIQTHMff4uuiiFnT0a9dRkC09YQKbQ0HIMNzM2IkbGp14_JouEXXhvR3rMCKb8y2S4QzrqrZZMbbTNQaoHvTi07DbEeZINZlfJa-OhtIt82ulauc1YQJGDTkF2B9txoMSEZqHn5dKuw5-XVxIt7c-FGeeMQ0cZ3bW3P5EorCyF3Vq3iCP2a8gGyndXbXRp0ZiJhxLp2y5YQGNf8SRVKhEMF5FSH4xgFokxbcp4Kl2T70gCfGVJigO_kjxyZt8H69gNDnNYSc8QzpkFKT3UxWz_tZSQ9Ia8IIJYQJx0MTX75Tw_qBhANFD_brQ6L-AzNBjm_OH5bh8gAVusyVhnDpmXY51gQrN6JyjqtLxaYtjY1cfDJwuC35atF8xYQHAl47bk03u2wlrCHigRI5e37vqpR1JDzegTysqwLWqduOWqgU948Rk2upFSrQyhmgFX7Fl7DtBuHo8ZpgMujUlYQChDnKQjbDqWalmDH8w8LX474u9CYIFWK4cQwBlKhBADVfKU1Qqica4w9bzA5GNfjB33CX9XUMplTJSowJEUr1FYQBkKA9cJV4Um_B88M2n5_baaiAPFznINZpMLK5EJgQ-0wUuvI50_t6K8bWegTer1fnQOSnAlfBtUcK8yYL_QOfJYQLlh6ZzWPSorsSl3JZm9rp2L_lxiTbY0DWY9pSJMMiA9EKQaTZkpZqeSKEmoT6Aq-eFAuTbwr906jhg4NprWI1RYQEghFR8i1Dc8TpbjkoLndak2DYuG1SxtPZgzVgp1BPyEj5GPyL4FL1a-ikORWgV-HVJMEfarAkSiarXyUgvNxMRYQHkm3JeRRLKAKaP6gAf657NnGmmA1uxzFQ4Gq--ypILf-bpWEE4cTvEWCqfQSD3am8bFOhUJXFB5Kw4UqcTsQMFYQP0klTeYpKIXTeZQfVGBu7TBu2ZAwpDQ2lVVP0mXhP84NSdKKq9sIhD2GczdN_WMe89fSLvOEFY89y_h2SEyWqNYQNkQsRlN7U6tNJClz7rTiRXBEYfJGcHr8DzOsbckVOcovkhLIXPQvza8k87XZVLbCQiM9EZjS-Qi_izstH7RBZ5YQP5o3vy24Z8-mdlf6gyILBobX49kkTJ_-K5QLVNRH49pbxMNri4y2TPRHK6dlH-RmqB0ODEJUtD22k2ctVpvZxlYQODUnQlNDHujgjU-zv2OKx8VULunh8F2Zc5a8zSe8et7nbYiHoR1zoJpny2FJNrcpaZsyCfYkFdMBY92-2fIMMVYQLriZAVY1yLNmVMmsvp1eNLJGuLhVbNW5Yeg0Q7MUDKiQWwfCIUAdpssw4NCzttMVc2BFOIUfLWeY2gOWZGeABJYQMFdZ5HH3AuukE_nIvGY3L7pCFj-fPC98peSXOrlSDndTpMD5YpAJ7llX1mu6eQh-3HM8pcXRyPMgIJ8Mts7brZYQCMMCir0KK66Cwn0PBloywyFio9K9FCVUNwm7EmR6cDET2f1eCMBuAWmogHnQq6Uaq9zbmPlh_61a2AX3plYA1NYQDTEXLX4HprsDgr6z9NQ4zsGughnID20MqLH8Ekcc-oaC-jePS9Oza-aF9AU83JPeYdixWUN_wIJzzp7HxPkCidYQOpN0b7tWiFYXRRF_WeNDkXzRKzUyx_AoysQSnjQ2tibBzdN0-YvGEkeHrxvurr1hh8K9YL4yNsaw7Xpx6CrjSJYQO9Q0rdzs8UlnYhVQYxFbEbmaJppeTTFYjH3rXZ9SmDvPTdkL57r8cqxHCPTpG1xgxL1fuYOrE__p7PZohCwiy1YQHJaNYwpgjeDOPO6Txoz0Q-jkVMDr9XUWM1qp-SL8JtLpqImZ9iw54uHKFxuMuETSSKXEN6ZnKlpbpLDM4FL3i1YQEUVVNdExM1KWU3orB-s5n50br-e7LubUo6FhZYghz1RFvSwFkGmtsqGOk67W0rtUd8TERoHjDq137ocHM4LRrNYQCG5DS_HAcrUXBI_eoxy1AJlKx9L4lY4VsNum6b4_F3lJJQ7wLor0NhbcpK3-0U-Iuf_J4_NKFiFwiQ9ZhE_Ll-CZy9pc3N1ZXJqL3ZhbGlkRnJvbQ" + } + } \ No newline at end of file diff --git a/src/__tests__/fixtures/pol-w3c-verifiable-document.json b/src/__tests__/fixtures/pol-w3c-verifiable-document.json new file mode 100644 index 0000000..c04a166 --- /dev/null +++ b/src/__tests__/fixtures/pol-w3c-verifiable-document.json @@ -0,0 +1,55 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://w3id.org/security/data-integrity/v2", + "https://trustvc.io/context/render-method-context-v2.json", + "https://trustvc.io/context/promissory-note.json", + "https://trustvc.io/context/transferable-records-context.json", + "https://trustvc.io/context/qrcode-context.json" + ], + "renderMethod": [ + { + "type": "EMBEDDED_RENDERER", + "templateName": "PROMISSORY_NOTE", + "id": "https://generic-templates.tradetrust.io" + } + ], + "credentialSubject": { + "type": [ + "PromissoryNote" + ], + "drawerCompanyName": "XYZ Exports Pvt. Ltd.", + "drawerCompanyNo": "CIN-XYZ1234567", + "drawerJurisdiction": "India", + "drawerWalletAddress": "0x433097a1C1b8a3e9188d8C54eCC057B1D69f1638", + "drawerPlaceOfIssue": "Mumbai, India", + "draweeCompanyName": "XYZ Imports Ltd.", + "draweeCompanyNo": "REG-XYZ9876543", + "draweeJurisdiction": "California, United States", + "draweeWalletAddress": "0xca93690bb57eeab273c796a9309246bc0fb93649", + "dueDate": "2025-06-19", + "currency": "USD", + "amount": "50,000.00", + "clause": "Payment to be made in full without set-off or counterclaim, subject to terms agreed between Drawer and Drawee.", + "signerName": "John Doe", + "signerPosition": "Chief Financial Officer", + "signerTimeStamp": "2025-06-10", + "logo": "data:image/png;base64,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", + "pNoteId": "PN-9081-2231-SGP", + "commitmentDate": "2025-12-10" + }, + "type": [ + "VerifiableCredential" + ], + "issuer": "did:web:trustvc.github.io:did:1", + "validFrom": "2024-04-01T12:19:52Z", + "id": "urn:uuid:019ea68d-006c-7887-9ea8-3f54f562ac65", + "proof": { + "type": "DataIntegrityProof", + "created": "2026-06-08T09:25:19Z", + "verificationMethod": "did:web:trustvc.github.io:did:1#multikey-1", + "cryptosuite": "ecdsa-sd-2023", + "proofPurpose": "assertionMethod", + "proofValue": "u2V0AhVhAHlKJtL9UV-_snL5rOlLyCznZu_oNyzOE3s6XAFR4USM0D68IJsm6qf5M01opyFXDW3Xn8mtLb6AZEgRWH6C4fVgjgCQD-WN_Q-KfWRbl6pQ5HB4u4khdD6xR1mPuWcTcLYNwPkhYIJy9GT0cT7-l_I9XOnoDDJyy5IV9LvUECuSSvNoluakUmB9YQDn8hL0e7mW-1DC0i74pcWcskNkMU82TgfA0P1eeJLFHdkuhd-HhqWH2wTd9CG48KJ0UvVkt3jzPXWHMhkkxzMRYQCnL8QQIZ-Ki2Cjf1_u43B3AM5BwAAXiC2NkfK6AhcWVoBnVtEBN2vBZc4pWJveWd_qVJU5ALqL3Dz1JmIAwkHlYQNfW5JmtYILhhIZufZ9j4FX9IA4x9hk0ULLhEenxAaYlfnYnewVsI3AvRdGmJc6QwL1PTR-hTVG3txGVL3SNWBtYQABtgC4W-r6VH649Et1xGg1NB2VE_ZshNSK2yQN1hDok36f6bwcAJ1n91y52u3XmpxOYyGmsPM8yp6udzO9YRwtYQAQz4UhnkiIQovoJHD6Y-kq3X2YegxXFTnh4zH_qNWvjhZVa0nx9CCDvBDpmlH0lWqSAkNiTTx0XWE298vAK54xYQGFfs4um2IWamhe_4da7qsInmTR5VZgIhqPlkfaQd4-Vw9INv8orNrqXOhIMssCG1MCAr1vnsM6zVq75fVnABP5YQA0idgyw13Uwb8N4e_CFH_kIX9M21lLmeZ_ZWA9z5HfsN8qxBEU4VeODqi97QjgRtVBKL4ijHXT1_-YZyZZByB5YQHcGLRUvnezhWKd8kDw2cn12hfnc6rLlyy3dXUDvUzfr6n3zUqCA_cDopBbmhtGIQTHMff4uuiiFnT0a9dRkC09YQKbQ0HIMNzM2IkbGp14_JouEXXhvR3rMCKb8y2S4QzrqrZZMbbTNQaoHvTi07DbEeZINZlfJa-OhtIt82ulauc1YQJGDTkF2B9txoMSEZqHn5dKuw5-XVxIt7c-FGeeMQ0cZ3bW3P5EorCyF3Vq3iCP2a8gGyndXbXRp0ZiJhxLp2y5YQGNf8SRVKhEMF5FSH4xgFokxbcp4Kl2T70gCfGVJigO_kjxyZt8H69gNDnNYSc8QzpkFKT3UxWz_tZSQ9Ia8IIJYQJx0MTX75Tw_qBhANFD_brQ6L-AzNBjm_OH5bh8gAVusyVhnDpmXY51gQrN6JyjqtLxaYtjY1cfDJwuC35atF8xYQHAl47bk03u2wlrCHigRI5e37vqpR1JDzegTysqwLWqduOWqgU948Rk2upFSrQyhmgFX7Fl7DtBuHo8ZpgMujUlYQChDnKQjbDqWalmDH8w8LX474u9CYIFWK4cQwBlKhBADVfKU1Qqica4w9bzA5GNfjB33CX9XUMplTJSowJEUr1FYQBkKA9cJV4Um_B88M2n5_baaiAPFznINZpMLK5EJgQ-0wUuvI50_t6K8bWegTer1fnQOSnAlfBtUcK8yYL_QOfJYQLlh6ZzWPSorsSl3JZm9rp2L_lxiTbY0DWY9pSJMMiA9EKQaTZkpZqeSKEmoT6Aq-eFAuTbwr906jhg4NprWI1RYQEghFR8i1Dc8TpbjkoLndak2DYuG1SxtPZgzVgp1BPyEj5GPyL4FL1a-ikORWgV-HVJMEfarAkSiarXyUgvNxMRYQHkm3JeRRLKAKaP6gAf657NnGmmA1uxzFQ4Gq--ypILf-bpWEE4cTvEWCqfQSD3am8bFOhUJXFB5Kw4UqcTsQMFYQP0klTeYpKIXTeZQfVGBu7TBu2ZAwpDQ2lVVP0mXhP84NSdKKq9sIhD2GczdN_WMe89fSLvOEFY89y_h2SEyWqNYQNkQsRlN7U6tNJClz7rTiRXBEYfJGcHr8DzOsbckVOcovkhLIXPQvza8k87XZVLbCQiM9EZjS-Qi_izstH7RBZ5YQP5o3vy24Z8-mdlf6gyILBobX49kkTJ_-K5QLVNRH49pbxMNri4y2TPRHK6dlH-RmqB0ODEJUtD22k2ctVpvZxlYQODUnQlNDHujgjU-zv2OKx8VULunh8F2Zc5a8zSe8et7nbYiHoR1zoJpny2FJNrcpaZsyCfYkFdMBY92-2fIMMVYQLriZAVY1yLNmVMmsvp1eNLJGuLhVbNW5Yeg0Q7MUDKiQWwfCIUAdpssw4NCzttMVc2BFOIUfLWeY2gOWZGeABJYQMFdZ5HH3AuukE_nIvGY3L7pCFj-fPC98peSXOrlSDndTpMD5YpAJ7llX1mu6eQh-3HM8pcXRyPMgIJ8Mts7brZYQCMMCir0KK66Cwn0PBloywyFio9K9FCVUNwm7EmR6cDET2f1eCMBuAWmogHnQq6Uaq9zbmPlh_61a2AX3plYA1NYQDTEXLX4HprsDgr6z9NQ4zsGughnID20MqLH8Ekcc-oaC-jePS9Oza-aF9AU83JPeYdixWUN_wIJzzp7HxPkCidYQOpN0b7tWiFYXRRF_WeNDkXzRKzUyx_AoysQSnjQ2tibBzdN0-YvGEkeHrxvurr1hh8K9YL4yNsaw7Xpx6CrjSJYQO9Q0rdzs8UlnYhVQYxFbEbmaJppeTTFYjH3rXZ9SmDvPTdkL57r8cqxHCPTpG1xgxL1fuYOrE__p7PZohCwiy1YQHJaNYwpgjeDOPO6Txoz0Q-jkVMDr9XUWM1qp-SL8JtLpqImZ9iw54uHKFxuMuETSSKXEN6ZnKlpbpLDM4FL3i1YQEUVVNdExM1KWU3orB-s5n50br-e7LubUo6FhZYghz1RFvSwFkGmtsqGOk67W0rtUd8TERoHjDq137ocHM4LRrNYQCG5DS_HAcrUXBI_eoxy1AJlKx9L4lY4VsNum6b4_F3lJJQ7wLor0NhbcpK3-0U-Iuf_J4_NKFiFwiQ9ZhE_Ll-CZy9pc3N1ZXJqL3ZhbGlkRnJvbQ" + } + } \ No newline at end of file diff --git a/src/__tests__/token-registry-functions/fixtures.ts b/src/__tests__/token-registry-functions/fixtures.ts index 9ab7c5d..3a1d35f 100644 --- a/src/__tests__/token-registry-functions/fixtures.ts +++ b/src/__tests__/token-registry-functions/fixtures.ts @@ -48,7 +48,7 @@ vi.mock('../../utils/ethers', async (importOriginal) => { ...original, // Keep all original exports getEthersContractFromProvider: vi.fn(() => MockContractConstructor), getEthersContractFactoryFromProvider: vi.fn(() => vi.fn()), - isV6EthersProvider: vi.fn(() => true), + isV6EthersProvider: vi.fn().mockImplementation(original.isV6EthersProvider), }; }); diff --git a/src/__tests__/token-registry-functions/transfers.test.ts b/src/__tests__/token-registry-functions/transfers.test.ts index f048598..eb18bae 100644 --- a/src/__tests__/token-registry-functions/transfers.test.ts +++ b/src/__tests__/token-registry-functions/transfers.test.ts @@ -74,6 +74,10 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc const isV5TT = titleEscrowVersion === 'v5'; const mockTitleEscrowContract = isV5TT ? mockV5TitleEscrowContract : mockV4TitleEscrowContract; const titleEscrowAddress = isV5TT ? '0xv5contract' : '0xv4contract'; + const gasTxOptions = + ethersVersion === 'v6' + ? { maxFeePerGas: 100n, maxPriorityFeePerGas: 50n } + : { maxFeePerGas: 100, maxPriorityFeePerGas: 50 }; // Handle both v5 and v6 contract constructors beforeAll(() => { @@ -166,7 +170,7 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc maxPriorityFeePerGas: 50, }); - const mockChainId = CHAIN_ID.mainnet; + const mockChainId = CHAIN_ID.pol; const originalChainData = SUPPORTED_CHAINS[mockChainId].gasStation; SUPPORTED_CHAINS[mockChainId] = { @@ -182,15 +186,15 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc }, wallet, params, - { id: 'doc-id', titleEscrowVersion }, + { id: 'doc-id', titleEscrowVersion, chainId: mockChainId }, ); const resultOptions = isV5TT ? ['0xholder', '0xencrypted_remarks'] : ['0xholder']; expect(gasStationMock).toHaveBeenCalled(); - expect(mockTitleEscrowContract.transferHolder).toHaveBeenCalledWith(...resultOptions, { - maxFeePerGas: 100, - maxPriorityFeePerGas: 50, - }); + expect(mockTitleEscrowContract.transferHolder).toHaveBeenCalledWith( + ...resultOptions, + gasTxOptions, + ); SUPPORTED_CHAINS[mockChainId] = { ...SUPPORTED_CHAINS[mockChainId], @@ -245,21 +249,8 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc if (isV5TT) expect(encrypt).toHaveBeenCalledWith('0xencrypted_remarks', 'doc-id'); const resultOptions = isV5TT - ? [ - '0xholder', - '0xencrypted_remarks', - { - maxFeePerGas: 100, - maxPriorityFeePerGas: 50, - }, - ] - : [ - '0xholder', - { - maxFeePerGas: 100, - maxPriorityFeePerGas: 50, - }, - ]; + ? ['0xholder', '0xencrypted_remarks', gasTxOptions] + : ['0xholder', gasTxOptions]; expect(mockTitleEscrowContract.transferHolder).toHaveBeenCalledWith(...resultOptions); expect(tx).toBe(txHash); @@ -343,7 +334,7 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc maxPriorityFeePerGas: 50, }); - const mockChainId = CHAIN_ID.mainnet; + const mockChainId = CHAIN_ID.pol; if (ethersVersion === 'v5') { wallet = new WalletV5(PRIVATE_KEY, Provider as any); vi.spyOn(wallet, 'getChainId').mockResolvedValue(mockChainId as unknown as number); @@ -367,15 +358,15 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc }, wallet, params, - { id: 'doc-id', titleEscrowVersion }, + { id: 'doc-id', titleEscrowVersion, chainId: mockChainId }, ); const resultOptions = isV5TT ? ['0xbeneficiary', '0xencrypted_remarks'] : ['0xbeneficiary']; expect(gasStationMock).toHaveBeenCalled(); - expect(mockTitleEscrowContract.transferBeneficiary).toHaveBeenCalledWith(...resultOptions, { - maxFeePerGas: 100, - maxPriorityFeePerGas: 50, - }); + expect(mockTitleEscrowContract.transferBeneficiary).toHaveBeenCalledWith( + ...resultOptions, + gasTxOptions, + ); SUPPORTED_CHAINS[mockChainId] = { ...SUPPORTED_CHAINS[mockChainId], @@ -430,8 +421,8 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc if (isV5TT) expect(encrypt).toHaveBeenCalledWith('0xencrypted_remarks', 'doc-id'); const resultOptions = isV5TT - ? ['0xbeneficiary', '0xencrypted_remarks', { maxFeePerGas: 100, maxPriorityFeePerGas: 50 }] - : ['0xbeneficiary', { maxFeePerGas: 100, maxPriorityFeePerGas: 50 }]; + ? ['0xbeneficiary', '0xencrypted_remarks', gasTxOptions] + : ['0xbeneficiary', gasTxOptions]; expect(mockTitleEscrowContract.transferBeneficiary).toHaveBeenCalledWith(...resultOptions); expect(tx).toBe(txHash); @@ -497,7 +488,7 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc maxPriorityFeePerGas: 50, }); - const mockChainId = CHAIN_ID.mainnet; + const mockChainId = CHAIN_ID.pol; if (ethersVersion === 'v5') { wallet = new WalletV5(PRIVATE_KEY, Provider as any); vi.spyOn(wallet, 'getChainId').mockResolvedValue(mockChainId as unknown as number); @@ -521,17 +512,17 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc }, wallet, params, - { id: 'doc-id', titleEscrowVersion }, + { id: 'doc-id', titleEscrowVersion, chainId: mockChainId }, ); const resultOptions = isV5TT ? ['0xbeneficiary', '0xholder', '0xencrypted_remarks'] : ['0xbeneficiary', '0xholder']; expect(gasStationMock).toHaveBeenCalled(); - expect(mockTitleEscrowContract.transferOwners).toHaveBeenCalledWith(...resultOptions, { - maxFeePerGas: 100, - maxPriorityFeePerGas: 50, - }); + expect(mockTitleEscrowContract.transferOwners).toHaveBeenCalledWith( + ...resultOptions, + gasTxOptions, + ); SUPPORTED_CHAINS[mockChainId] = { ...SUPPORTED_CHAINS[mockChainId], @@ -586,13 +577,8 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc if (isV5TT) expect(encrypt).toHaveBeenCalledWith('0xencrypted_remarks', 'doc-id'); const resultOptions = isV5TT - ? [ - '0xbeneficiary', - '0xholder', - '0xencrypted_remarks', - { maxFeePerGas: 100, maxPriorityFeePerGas: 50 }, - ] - : ['0xbeneficiary', '0xholder', { maxFeePerGas: 100, maxPriorityFeePerGas: 50 }]; + ? ['0xbeneficiary', '0xholder', '0xencrypted_remarks', gasTxOptions] + : ['0xbeneficiary', '0xholder', gasTxOptions]; expect(mockTitleEscrowContract.transferOwners).toHaveBeenCalledWith(...resultOptions); expect(tx).toBe(txHash); @@ -676,7 +662,7 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc maxPriorityFeePerGas: 50, }); - const mockChainId = CHAIN_ID.mainnet; + const mockChainId = CHAIN_ID.pol; if (ethersVersion === 'v5') { wallet = new WalletV5(PRIVATE_KEY, Provider as any); vi.spyOn(wallet, 'getChainId').mockResolvedValue(mockChainId as unknown as number); @@ -700,15 +686,12 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc }, wallet, params, - { id: 'doc-id', titleEscrowVersion }, + { id: 'doc-id', titleEscrowVersion, chainId: mockChainId }, ); const resultOptions = isV5TT ? ['0xbeneficiary', '0xencrypted_remarks'] : ['0xbeneficiary']; expect(gasStationMock).toHaveBeenCalled(); - expect(mockTitleEscrowContract.nominate).toHaveBeenCalledWith(...resultOptions, { - maxFeePerGas: 100, - maxPriorityFeePerGas: 50, - }); + expect(mockTitleEscrowContract.nominate).toHaveBeenCalledWith(...resultOptions, gasTxOptions); SUPPORTED_CHAINS[mockChainId] = { ...SUPPORTED_CHAINS[mockChainId], @@ -759,8 +742,8 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc if (isV5TT) expect(encrypt).toHaveBeenCalledWith('0xencrypted_remarks', 'doc-id'); const resultOptions = isV5TT - ? ['0xbeneficiary', '0xencrypted_remarks', { maxFeePerGas: 100, maxPriorityFeePerGas: 50 }] - : ['0xbeneficiary', { maxFeePerGas: 100, maxPriorityFeePerGas: 50 }]; + ? ['0xbeneficiary', '0xencrypted_remarks', gasTxOptions] + : ['0xbeneficiary', gasTxOptions]; expect(mockTitleEscrowContract.nominate).toHaveBeenCalledWith(...resultOptions); expect(tx).toBe(txHash); diff --git a/src/token-registry-functions/utils.ts b/src/token-registry-functions/utils.ts index 061b33a..85f2216 100644 --- a/src/token-registry-functions/utils.ts +++ b/src/token-registry-functions/utils.ts @@ -22,7 +22,17 @@ const getTxOptions = async ( maxPriorityFeePerGas = gasFees?.maxPriorityFeePerGas ?? 0; } } - return maxFeePerGas && maxPriorityFeePerGas ? { maxFeePerGas, maxPriorityFeePerGas } : {}; + if (!maxFeePerGas || !maxPriorityFeePerGas) { + return {}; + } + // Gas station returns ethers v5 BigNumber; ethers v6 requires bigint/BigNumberish + if (isV6EthersProvider(signer.provider)) { + return { + maxFeePerGas: BigInt(maxFeePerGas.toString()), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas.toString()), + }; + } + return { maxFeePerGas, maxPriorityFeePerGas }; }; // 🔍 Handles both Ethers v5 and v6 signer types diff --git a/src/utils/network/index.ts b/src/utils/network/index.ts index 53770bb..21b332d 100644 --- a/src/utils/network/index.ts +++ b/src/utils/network/index.ts @@ -17,4 +17,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'; diff --git a/src/utils/supportedChains/index.ts b/src/utils/supportedChains/index.ts index 6e4885f..d8f6c26 100644 --- a/src/utils/supportedChains/index.ts +++ b/src/utils/supportedChains/index.ts @@ -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', @@ -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', + [CHAIN_ID.pol]: { + id: CHAIN_ID.pol, + label: 'Polygon (POL)', name: 'matic', 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, }, }, @@ -79,15 +81,15 @@ export const SUPPORTED_CHAINS: supportedChains = { label: 'Polygon Amoy', name: 'amoy', type: 'test', - currency: 'MATIC', + currency: 'POL', iconImage: iconPolygon, - explorerUrl: 'https://www.oklink.com/amoy', - explorerApiUrl: `https://www.oklink.com/${process.env.OKLINK_API_KEY}`, + explorerUrl: 'https://amoy.polygonscan.com', + explorerApiUrl: `https://api-amoy.polygonscan.com/api?apikey=${process.env.POLYGONSCAN_API_KEY}`, rpcUrl: `https://polygon-amoy.infura.io/v3/${process.env.INFURA_API_KEY}`, gasStation: gasStation('https://gasstation.polygon.technology/amoy'), nativeCurrency: { - name: 'MATIC', - symbol: 'aMATIC', + name: 'POL', + symbol: 'POL', decimals: 18, }, }, diff --git a/src/utils/supportedChains/supportedChains.test.ts b/src/utils/supportedChains/supportedChains.test.ts index ecd488f..c4c10a6 100644 --- a/src/utils/supportedChains/supportedChains.test.ts +++ b/src/utils/supportedChains/supportedChains.test.ts @@ -21,27 +21,49 @@ 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('matic'); // ethers.js network name — unchanged by the POL rebrand + 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('matic'); // ethers.js network name — unchanged by the POL rebrand 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]; expect(id).toBe(CHAIN_ID.amoy); expect(name).toBe('amoy'); expect(type).toBe('test'); - expect(currency).toBe('MATIC'); - expect(explorerUrl).toBe('https://www.oklink.com/amoy'); + expect(currency).toBe('POL'); + expect(explorerUrl).toBe('https://amoy.polygonscan.com'); expect(rpcUrl).toContain('https://polygon-amoy.infura.io/v3/'); }); + it('should use PolygonScan as the explorer API for amoy', () => { + const { explorerApiUrl } = SUPPORTED_CHAINS[CHAIN_ID.amoy]; + + expect(explorerApiUrl).toContain('https://api-amoy.polygonscan.com/api'); + expect(explorerApiUrl).toContain('apikey='); + }); + it('should sepolia chain info correctly', () => { const { id, name, type, currency, explorerUrl } = SUPPORTED_CHAINS[CHAIN_ID.sepolia];