@trustvc/trustvc 1.2.11 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,6 +24,7 @@ TrustVC is a comprehensive wrapper library designed to simplify the signing and
24
24
  - [TradeTrustToken](#tradetrusttoken)
25
25
  - [a) Token Registry v4](#a-token-registry-v4)
26
26
  - [b) Token Registry V5](#b-token-registry-v5)
27
+ - [7. **Document Builder**](#7-document-builder)
27
28
 
28
29
  ## Installation
29
30
 
@@ -587,3 +588,155 @@ function rejectTransferOwners(bytes calldata _remark) external;
587
588
  ```
588
589
 
589
590
  For more information on Token Registry and Title Escrow contracts **version v5**, please visit the readme of [TradeTrust Token Registry V5](https://github.com/TradeTrust/token-registry/blob/master/README.md)
591
+
592
+ ### 7. **Document Builder**
593
+ > The `DocumentBuilder` class helps build and manage W3C Verifiable Credentials (VCs) with credential status features. It supports creating documents with two types of credential statuses: `transferableRecords` and `verifiableDocument`. It can sign the document using a private key, verify its signature, and serialize the document to a JSON format. Additionally, it allows for configuration of document rendering methods and expiration dates.
594
+
595
+ #### Usage
596
+
597
+ ##### Create a new DocumentBuilder instance
598
+ To create a new document, instantiate the `DocumentBuilder` with the base document (Verifiable Credential) that you want to build.
599
+
600
+ To learn more about defining custom contexts, check out the [Credential Subject - Custom Contexts guide](https://docs.tradetrust.io/docs/how-tos/credential-subject).
601
+
602
+ ```ts
603
+ // Adds a custom vocabulary used to define terms in the `credentialSubject`.
604
+ // Users can define their own context if they have domain-specific fields or custom data structures.
605
+ const builder = new DocumentBuilder({
606
+ '@context': 'https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v1.jsonld'
607
+ });
608
+ ```
609
+
610
+ ##### Set Credential Subject
611
+ Set the subject of the Verifiable Credential, which typically contains information about the entity the credential is issued to.
612
+
613
+ ```ts
614
+ builder.credentialSubject({
615
+ id: 'did:example:123',
616
+ name: 'John Doe',
617
+ });
618
+ ```
619
+
620
+ ##### Configure Credential Status
621
+ You can configure the credential status as either `transferableRecords` or `verifiableDocument`.
622
+
623
+ **Transferable Records**
624
+ ```ts
625
+ builder.credentialStatus({
626
+ // Refers to the supported network.
627
+ // See: https://docs.tradetrust.io/docs/introduction/key-components-of-tradetrust/blockchain/supported-network
628
+ chain: 'Ethereum',
629
+ chainId: 1,
630
+ tokenRegistry: '0x1234567890abcdef...',
631
+ rpcProviderUrl: 'https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID',
632
+ });
633
+ ```
634
+
635
+ > ⚠️ **Disclaimer:**
636
+ > This builder **does not mint** documents on-chain. If you're using `transferableRecords`, you'll need to mint the document.
637
+ > [See the minting guide here](https://docs.tradetrust.io/docs/how-tos/credential-status#2-minting-the-credential)
638
+
639
+
640
+ **Verifiable Document**
641
+ ```ts
642
+ builder.credentialStatus({
643
+ url: 'https://example.com/status-list',
644
+ // `index: <placeholder>` refers to the bit position in the status list that will be set for revocation.
645
+ // Note: A document with the specific index must be marked as not revoked in the status list.
646
+ index: <placeholder>,
647
+ purpose: 'revocation',
648
+ });
649
+ ```
650
+
651
+ ##### Set Expiration Date
652
+ You can set an expiration date for the document.
653
+
654
+ ```ts
655
+ builder.expirationDate('2026-01-01T00:00:00Z');
656
+ ```
657
+
658
+ ##### Define Rendering Method
659
+ Set the rendering method to be used for the document.
660
+
661
+ ```ts
662
+ builder.renderMethod({
663
+ id: 'https://example.com/rendering-method',
664
+ type: 'EMBEDDED_RENDERER',
665
+ templateName: 'BILL_OF_LADING',
666
+ });
667
+ ```
668
+
669
+ ##### Sign the Document
670
+ To sign the document, provide a `PrivateKeyPair` from `@trustvc/trustvc`.
671
+
672
+ ```ts
673
+ const privateKey: PrivateKeyPair = {
674
+ id: 'did:example:456#key1',
675
+ controller: 'did:example:456',
676
+ type: VerificationType.Bls12381G2Key2020,
677
+ publicKeyBase58: 'your-public-key-base58',
678
+ privateKeyBase58: 'your-private-key-base58',
679
+ };
680
+
681
+ const signedDocument = await builder.sign(privateKey);
682
+ console.log(signedDocument);
683
+ ```
684
+
685
+ Example Output After Signing
686
+ ```json
687
+ {
688
+ "@context": [
689
+ "https://www.w3.org/2018/credentials/v1",
690
+ "https://w3c-ccg.github.io/citizenship-vocab/contexts/citizenship-v1.jsonld",
691
+ "https://w3id.org/vc/status-list/2021/v1",
692
+ "https://trustvc.io/context/render-method-context.json",
693
+ "https://w3id.org/security/bbs/v1"
694
+ ],
695
+ "type": ["VerifiableCredential"],
696
+ "credentialSubject": {
697
+ "id": "did:example:123",
698
+ "name": "John Doe"
699
+ },
700
+ "expirationDate": "2026-01-01T00:00:00Z",
701
+ "renderMethod": [
702
+ {
703
+ "id": "https://example.com/rendering-method",
704
+ "type": "EMBEDDED_RENDERER",
705
+ "templateName": "BILL_OF_LADING"
706
+ }
707
+ ],
708
+ "credentialStatus": {
709
+ "id": "https://example.com/status-list#<placeholder>",
710
+ "type": "StatusList2021Entry",
711
+ "statusPurpose": "revocation",
712
+ "statusListIndex": "<placeholder>",
713
+ "statusListCredential": "https://example.com/status-list"
714
+ },
715
+ "issuer": "did:example:456",
716
+ "issuanceDate": "2025-01-01T00:00:00Z",
717
+ "id": "urn:bnid:_:0195fec2-4ae1-7cca-9182-03fd7da5142b",
718
+ "proof": {
719
+ "type": "BbsBlsSignature2020",
720
+ "created": "2025-01-01T00:00:01Z",
721
+ "proofPurpose": "assertionMethod",
722
+ "proofValue": "rV56L+QYozATRy3GOVLomzUo99sXtw2x0Cy9dEkHJ15wi4cS12cQJRIwzONVi3YscdhaSKoqD1jWmwb5A/khLZnDq5eo3QzDgTVClYuV86opL3HJyoS4+t2rRt3wl+chnATy2jqr5zMEvcVJ3gdXpQ==",
723
+ "verificationMethod": "did:example:456#key1"
724
+ }
725
+ }
726
+ ```
727
+
728
+ ##### Verify the Document
729
+ To verify the signature of the signed document:
730
+
731
+ ```ts
732
+ const isVerified = await builder.verify();
733
+ console.log(isVerified); // true or false
734
+ ```
735
+
736
+ ##### Convert Document to JSON String
737
+ To get the current state of the document as a JSON string:
738
+
739
+ ```ts
740
+ const documentJson = builder.toString();
741
+ console.log(documentJson);
742
+ ```
@@ -1,5 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ var w3c = require('../w3c');
4
+ var w3cCredentialStatus = require('@trustvc/w3c-credential-status');
5
+ var w3cVc = require('@trustvc/w3c-vc');
6
+ var ethers = require('ethers');
7
+ var tokenRegistryV4$1 = require('@tradetrust-tt/token-registry-v4');
8
+ var tokenRegistryV5$1 = require('@tradetrust-tt/token-registry-v5');
9
+ var tokenRegistryV4 = require('../token-registry-v4');
10
+ var tokenRegistryV5 = require('../token-registry-v5');
11
+ var tradetrustUtils = require('@tradetrust-tt/tradetrust-utils');
12
+
3
13
  var __defProp = Object.defineProperty;
4
14
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
15
  class DocumentBuilder {
@@ -7,8 +17,194 @@ class DocumentBuilder {
7
17
  __name(this, "DocumentBuilder");
8
18
  }
9
19
  document;
10
- constructor() {
11
- this.document = new DocumentBuilder();
20
+ // Holds the document to be built and signed.
21
+ documentType = "w3c";
22
+ // Default to W3C
23
+ selectedStatusType = null;
24
+ // Tracks selected status type.
25
+ statusConfig = {};
26
+ // Configuration for the credential status.
27
+ rpcProviderUrl;
28
+ // Holds the RPC provider URL for verifying token registry.
29
+ requiredFields = ["credentialSubject"];
30
+ // Required fields that must be present in the document.
31
+ isSigned = false;
32
+ // Tracks if a document is signed
33
+ /**
34
+ * Constructor to initialize the document builder.
35
+ * @param {Partial<VerifiableCredential>} input - The input document.
36
+ * @param {string} [documentType] - The type of the document (default is "w3c").
37
+ */
38
+ constructor(input, documentType = "w3c") {
39
+ this.document = this.initializeDocument(input);
40
+ this.documentType = documentType;
41
+ }
42
+ // Sets the credential subject of the document.
43
+ credentialSubject(subject) {
44
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
45
+ this.document.credentialSubject = subject;
46
+ return this;
47
+ }
48
+ // Configures the credential status of the document based on the provided type (Transferable Records or Verifiable Document).
49
+ credentialStatus(config) {
50
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
51
+ const isTransferable = this.isTransferableRecordsConfig(config);
52
+ const isVerifiable = this.isVerifiableDocumentConfig(config);
53
+ if (isTransferable && isVerifiable) {
54
+ throw new Error(
55
+ "Configuration Error: Do not mix transferable records and verifiable document properties."
56
+ );
57
+ }
58
+ if (isTransferable) {
59
+ this.selectedStatusType = "transferableRecords";
60
+ this.statusConfig = {
61
+ type: "TransferableRecords",
62
+ tokenNetwork: { chain: config.chain, chainId: config.chainId },
63
+ tokenRegistry: config.tokenRegistry
64
+ };
65
+ this.rpcProviderUrl = config.rpcProviderUrl;
66
+ this.addContext("https://trustvc.io/context/transferable-records-context.json");
67
+ } else if (isVerifiable) {
68
+ this.selectedStatusType = "verifiableDocument";
69
+ this.statusConfig = {
70
+ id: `${config.url}#${config.index}`,
71
+ type: "StatusList2021Entry",
72
+ statusPurpose: config.purpose || "revocation",
73
+ // Set status purpose to "revocation" by default.
74
+ statusListIndex: config.index,
75
+ statusListCredential: config.url
76
+ };
77
+ this.addContext("https://w3id.org/vc/status-list/2021/v1");
78
+ } else {
79
+ throw new Error("Configuration Error: Missing required fields for credential status.");
80
+ }
81
+ return this;
82
+ }
83
+ // Sets the expiration date of the document.
84
+ expirationDate(date) {
85
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
86
+ this.document.expirationDate = typeof date === "string" ? date : date.toISOString();
87
+ return this;
88
+ }
89
+ // Defines the rendering method for the document.
90
+ renderMethod(method) {
91
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
92
+ this.document.renderMethod = [method];
93
+ this.addContext("https://trustvc.io/context/render-method-context.json");
94
+ return this;
95
+ }
96
+ // Sign the document using the provided private key and an optional cryptographic suite.
97
+ async sign(privateKey, cryptoSuite) {
98
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
99
+ if (this.selectedStatusType) {
100
+ this.document.credentialStatus = this.statusConfig;
101
+ }
102
+ this.validateRequiredFields(this.document);
103
+ if (this.selectedStatusType === "verifiableDocument") {
104
+ w3cCredentialStatus.assertCredentialStatus(this.document.credentialStatus);
105
+ const verificationResult = await w3cVc.verifyCredentialStatus(this.document.credentialStatus);
106
+ if (verificationResult.error)
107
+ throw new Error(`Credential Verification Failed: ${verificationResult.error}`);
108
+ if (verificationResult.status)
109
+ throw new Error("Credential Verification Failed: Invalid credential status detected.");
110
+ } else if (this.selectedStatusType === "transferableRecords") {
111
+ w3cCredentialStatus.assertTransferableRecords(this.document.credentialStatus, "sign");
112
+ await this.verifyTokenRegistry();
113
+ }
114
+ this.document.issuer = privateKey.id.split("#")[0];
115
+ this.document.issuanceDate = this.document.issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
116
+ this.addContext("https://w3id.org/security/bbs/v1");
117
+ const signedVC = await w3c.signW3C(this.document, privateKey, cryptoSuite);
118
+ if (signedVC.error) throw new Error(`Signing Error: ${signedVC.error}`);
119
+ this.isSigned = true;
120
+ return signedVC.signed;
121
+ }
122
+ // Verify the document.
123
+ async verify() {
124
+ if (!this.isSigned) throw new Error("Verification Error: Document is not signed yet.");
125
+ const verificationResult = await w3c.verifyW3CSignature(
126
+ this.document
127
+ );
128
+ if (verificationResult.error)
129
+ throw new Error(`Verification Error: ${verificationResult.error}`);
130
+ return verificationResult.verified;
131
+ }
132
+ // Returns the current state of the document as a JSON string.
133
+ toString() {
134
+ return JSON.stringify(this.document, null, 2);
135
+ }
136
+ // Type guard for transferable records configuration
137
+ isTransferableRecordsConfig(config) {
138
+ return config && typeof config.tokenRegistry === "string" && typeof config.chain === "string" && typeof config.chainId === "number" && typeof config.rpcProviderUrl === "string";
139
+ }
140
+ // Type guard for verifiable document configuration
141
+ isVerifiableDocumentConfig(config) {
142
+ return config && typeof config.url === "string" && typeof config.index === "number";
143
+ }
144
+ // Private helper method to validate that the required fields are present in the input document.
145
+ validateRequiredFields(input) {
146
+ this.requiredFields.forEach((field) => {
147
+ if (!input[field]) {
148
+ throw new Error(`Validation Error: Missing required field "${field}" in the credential.`);
149
+ }
150
+ });
151
+ }
152
+ // Private helper method to initialize the document with required context and type, adding the necessary context URL.
153
+ initializeDocument(input) {
154
+ if (input.proof) throw new Error("Configuration Error: Document is already signed.");
155
+ return {
156
+ ...input,
157
+ "@context": this.buildContext(input["@context"]),
158
+ type: Array.from(new Set([].concat(input.type || [], "VerifiableCredential")))
159
+ };
160
+ }
161
+ // Private helper method to build the context for the document, ensuring uniqueness and adding the default W3C context.
162
+ buildContext(context) {
163
+ return [
164
+ "https://www.w3.org/2018/credentials/v1",
165
+ ...Array.isArray(context) ? context : context ? [context] : []
166
+ ].filter((v, i, a) => a.indexOf(v) === i);
167
+ }
168
+ // Private helper method to add a new context to the document if it does not already exist.
169
+ addContext(context) {
170
+ if (!this.document["@context"].includes(context)) {
171
+ this.document["@context"].push(context);
172
+ }
173
+ }
174
+ // Private helper method to verify that the token registry supports the required interface for transferable records.
175
+ async verifyTokenRegistry() {
176
+ const chainId = this.document.credentialStatus.tokenNetwork.chainId;
177
+ if (!(chainId in tradetrustUtils.SUPPORTED_CHAINS)) {
178
+ throw new Error(`Unsupported Chain: Chain ID ${chainId} is not supported.`);
179
+ }
180
+ try {
181
+ const provider = new ethers.ethers.providers.JsonRpcProvider(this.rpcProviderUrl);
182
+ const isV4Supported = await this.supportsInterface(
183
+ tokenRegistryV4.v4Contracts.TradeTrustToken__factory,
184
+ tokenRegistryV4$1.constants.contractInterfaceId.TradeTrustTokenMintable,
185
+ provider
186
+ );
187
+ const isV5Supported = await this.supportsInterface(
188
+ tokenRegistryV5.v5Contracts.TradeTrustToken__factory,
189
+ tokenRegistryV5$1.constants.contractInterfaceId.TradeTrustTokenMintable,
190
+ provider
191
+ );
192
+ if (!isV4Supported && !isV5Supported)
193
+ throw new Error("Token registry version is not supported.");
194
+ } catch (error) {
195
+ if (error.message === "Token registry version is not supported.") {
196
+ throw error;
197
+ } else {
198
+ throw new Error(
199
+ `Network Error: Unable to verify token registry. Please check the RPC URL or token registry address.`
200
+ );
201
+ }
202
+ }
203
+ }
204
+ // Private helper method to check if a contract supports a specific interface ID.
205
+ async supportsInterface(contractFactory, interfaceId, provider) {
206
+ const contract = contractFactory.connect(this.statusConfig.tokenRegistry, provider);
207
+ return contract.supportsInterface(interfaceId);
12
208
  }
13
209
  }
14
210
 
@@ -4,6 +4,7 @@ var decrypt = require('./decrypt');
4
4
  var encrypt = require('./encrypt');
5
5
  var verify = require('./verify');
6
6
  var endorsementChain = require('./endorsement-chain');
7
+ var documentBuilder = require('./documentBuilder');
7
8
 
8
9
 
9
10
 
@@ -31,3 +32,9 @@ Object.keys(endorsementChain).forEach(function (k) {
31
32
  get: function () { return endorsementChain[k]; }
32
33
  });
33
34
  });
35
+ Object.keys(documentBuilder).forEach(function (k) {
36
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
37
+ enumerable: true,
38
+ get: function () { return documentBuilder[k]; }
39
+ });
40
+ });
@@ -1,3 +1,13 @@
1
+ import { signW3C, verifyW3CSignature } from '../w3c';
2
+ import { assertCredentialStatus, assertTransferableRecords } from '@trustvc/w3c-credential-status';
3
+ import { verifyCredentialStatus } from '@trustvc/w3c-vc';
4
+ import { ethers } from 'ethers';
5
+ import { constants } from '@tradetrust-tt/token-registry-v4';
6
+ import { constants as constants$1 } from '@tradetrust-tt/token-registry-v5';
7
+ import { v4Contracts } from '../token-registry-v4';
8
+ import { v5Contracts } from '../token-registry-v5';
9
+ import { SUPPORTED_CHAINS } from '@tradetrust-tt/tradetrust-utils';
10
+
1
11
  var __defProp = Object.defineProperty;
2
12
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
13
  class DocumentBuilder {
@@ -5,8 +15,194 @@ class DocumentBuilder {
5
15
  __name(this, "DocumentBuilder");
6
16
  }
7
17
  document;
8
- constructor() {
9
- this.document = new DocumentBuilder();
18
+ // Holds the document to be built and signed.
19
+ documentType = "w3c";
20
+ // Default to W3C
21
+ selectedStatusType = null;
22
+ // Tracks selected status type.
23
+ statusConfig = {};
24
+ // Configuration for the credential status.
25
+ rpcProviderUrl;
26
+ // Holds the RPC provider URL for verifying token registry.
27
+ requiredFields = ["credentialSubject"];
28
+ // Required fields that must be present in the document.
29
+ isSigned = false;
30
+ // Tracks if a document is signed
31
+ /**
32
+ * Constructor to initialize the document builder.
33
+ * @param {Partial<VerifiableCredential>} input - The input document.
34
+ * @param {string} [documentType] - The type of the document (default is "w3c").
35
+ */
36
+ constructor(input, documentType = "w3c") {
37
+ this.document = this.initializeDocument(input);
38
+ this.documentType = documentType;
39
+ }
40
+ // Sets the credential subject of the document.
41
+ credentialSubject(subject) {
42
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
43
+ this.document.credentialSubject = subject;
44
+ return this;
45
+ }
46
+ // Configures the credential status of the document based on the provided type (Transferable Records or Verifiable Document).
47
+ credentialStatus(config) {
48
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
49
+ const isTransferable = this.isTransferableRecordsConfig(config);
50
+ const isVerifiable = this.isVerifiableDocumentConfig(config);
51
+ if (isTransferable && isVerifiable) {
52
+ throw new Error(
53
+ "Configuration Error: Do not mix transferable records and verifiable document properties."
54
+ );
55
+ }
56
+ if (isTransferable) {
57
+ this.selectedStatusType = "transferableRecords";
58
+ this.statusConfig = {
59
+ type: "TransferableRecords",
60
+ tokenNetwork: { chain: config.chain, chainId: config.chainId },
61
+ tokenRegistry: config.tokenRegistry
62
+ };
63
+ this.rpcProviderUrl = config.rpcProviderUrl;
64
+ this.addContext("https://trustvc.io/context/transferable-records-context.json");
65
+ } else if (isVerifiable) {
66
+ this.selectedStatusType = "verifiableDocument";
67
+ this.statusConfig = {
68
+ id: `${config.url}#${config.index}`,
69
+ type: "StatusList2021Entry",
70
+ statusPurpose: config.purpose || "revocation",
71
+ // Set status purpose to "revocation" by default.
72
+ statusListIndex: config.index,
73
+ statusListCredential: config.url
74
+ };
75
+ this.addContext("https://w3id.org/vc/status-list/2021/v1");
76
+ } else {
77
+ throw new Error("Configuration Error: Missing required fields for credential status.");
78
+ }
79
+ return this;
80
+ }
81
+ // Sets the expiration date of the document.
82
+ expirationDate(date) {
83
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
84
+ this.document.expirationDate = typeof date === "string" ? date : date.toISOString();
85
+ return this;
86
+ }
87
+ // Defines the rendering method for the document.
88
+ renderMethod(method) {
89
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
90
+ this.document.renderMethod = [method];
91
+ this.addContext("https://trustvc.io/context/render-method-context.json");
92
+ return this;
93
+ }
94
+ // Sign the document using the provided private key and an optional cryptographic suite.
95
+ async sign(privateKey, cryptoSuite) {
96
+ if (this.isSigned) throw new Error("Configuration Error: Document is already signed.");
97
+ if (this.selectedStatusType) {
98
+ this.document.credentialStatus = this.statusConfig;
99
+ }
100
+ this.validateRequiredFields(this.document);
101
+ if (this.selectedStatusType === "verifiableDocument") {
102
+ assertCredentialStatus(this.document.credentialStatus);
103
+ const verificationResult = await verifyCredentialStatus(this.document.credentialStatus);
104
+ if (verificationResult.error)
105
+ throw new Error(`Credential Verification Failed: ${verificationResult.error}`);
106
+ if (verificationResult.status)
107
+ throw new Error("Credential Verification Failed: Invalid credential status detected.");
108
+ } else if (this.selectedStatusType === "transferableRecords") {
109
+ assertTransferableRecords(this.document.credentialStatus, "sign");
110
+ await this.verifyTokenRegistry();
111
+ }
112
+ this.document.issuer = privateKey.id.split("#")[0];
113
+ this.document.issuanceDate = this.document.issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
114
+ this.addContext("https://w3id.org/security/bbs/v1");
115
+ const signedVC = await signW3C(this.document, privateKey, cryptoSuite);
116
+ if (signedVC.error) throw new Error(`Signing Error: ${signedVC.error}`);
117
+ this.isSigned = true;
118
+ return signedVC.signed;
119
+ }
120
+ // Verify the document.
121
+ async verify() {
122
+ if (!this.isSigned) throw new Error("Verification Error: Document is not signed yet.");
123
+ const verificationResult = await verifyW3CSignature(
124
+ this.document
125
+ );
126
+ if (verificationResult.error)
127
+ throw new Error(`Verification Error: ${verificationResult.error}`);
128
+ return verificationResult.verified;
129
+ }
130
+ // Returns the current state of the document as a JSON string.
131
+ toString() {
132
+ return JSON.stringify(this.document, null, 2);
133
+ }
134
+ // Type guard for transferable records configuration
135
+ isTransferableRecordsConfig(config) {
136
+ return config && typeof config.tokenRegistry === "string" && typeof config.chain === "string" && typeof config.chainId === "number" && typeof config.rpcProviderUrl === "string";
137
+ }
138
+ // Type guard for verifiable document configuration
139
+ isVerifiableDocumentConfig(config) {
140
+ return config && typeof config.url === "string" && typeof config.index === "number";
141
+ }
142
+ // Private helper method to validate that the required fields are present in the input document.
143
+ validateRequiredFields(input) {
144
+ this.requiredFields.forEach((field) => {
145
+ if (!input[field]) {
146
+ throw new Error(`Validation Error: Missing required field "${field}" in the credential.`);
147
+ }
148
+ });
149
+ }
150
+ // Private helper method to initialize the document with required context and type, adding the necessary context URL.
151
+ initializeDocument(input) {
152
+ if (input.proof) throw new Error("Configuration Error: Document is already signed.");
153
+ return {
154
+ ...input,
155
+ "@context": this.buildContext(input["@context"]),
156
+ type: Array.from(new Set([].concat(input.type || [], "VerifiableCredential")))
157
+ };
158
+ }
159
+ // Private helper method to build the context for the document, ensuring uniqueness and adding the default W3C context.
160
+ buildContext(context) {
161
+ return [
162
+ "https://www.w3.org/2018/credentials/v1",
163
+ ...Array.isArray(context) ? context : context ? [context] : []
164
+ ].filter((v, i, a) => a.indexOf(v) === i);
165
+ }
166
+ // Private helper method to add a new context to the document if it does not already exist.
167
+ addContext(context) {
168
+ if (!this.document["@context"].includes(context)) {
169
+ this.document["@context"].push(context);
170
+ }
171
+ }
172
+ // Private helper method to verify that the token registry supports the required interface for transferable records.
173
+ async verifyTokenRegistry() {
174
+ const chainId = this.document.credentialStatus.tokenNetwork.chainId;
175
+ if (!(chainId in SUPPORTED_CHAINS)) {
176
+ throw new Error(`Unsupported Chain: Chain ID ${chainId} is not supported.`);
177
+ }
178
+ try {
179
+ const provider = new ethers.providers.JsonRpcProvider(this.rpcProviderUrl);
180
+ const isV4Supported = await this.supportsInterface(
181
+ v4Contracts.TradeTrustToken__factory,
182
+ constants.contractInterfaceId.TradeTrustTokenMintable,
183
+ provider
184
+ );
185
+ const isV5Supported = await this.supportsInterface(
186
+ v5Contracts.TradeTrustToken__factory,
187
+ constants$1.contractInterfaceId.TradeTrustTokenMintable,
188
+ provider
189
+ );
190
+ if (!isV4Supported && !isV5Supported)
191
+ throw new Error("Token registry version is not supported.");
192
+ } catch (error) {
193
+ if (error.message === "Token registry version is not supported.") {
194
+ throw error;
195
+ } else {
196
+ throw new Error(
197
+ `Network Error: Unable to verify token registry. Please check the RPC URL or token registry address.`
198
+ );
199
+ }
200
+ }
201
+ }
202
+ // Private helper method to check if a contract supports a specific interface ID.
203
+ async supportsInterface(contractFactory, interfaceId, provider) {
204
+ const contract = contractFactory.connect(this.statusConfig.tokenRegistry, provider);
205
+ return contract.supportsInterface(interfaceId);
10
206
  }
11
207
  }
12
208
 
@@ -2,3 +2,4 @@ export * from './decrypt';
2
2
  export * from './encrypt';
3
3
  export * from './verify';
4
4
  export * from './endorsement-chain';
5
+ export * from './documentBuilder';
@@ -1,6 +1,46 @@
1
+ import { PrivateKeyPair } from '@trustvc/w3c-issuer';
2
+ import { VerifiableCredential, SignedVerifiableCredential } from '@trustvc/w3c-vc';
3
+
4
+ interface W3CVerifiableDocumentConfig {
5
+ url: string;
6
+ index: number;
7
+ purpose?: string;
8
+ }
9
+ interface W3CTransferableRecordsConfig {
10
+ chain: string;
11
+ chainId: number;
12
+ tokenRegistry: string;
13
+ rpcProviderUrl: string;
14
+ }
15
+ interface RenderMethod {
16
+ id: string;
17
+ type: string;
18
+ templateName: string;
19
+ }
1
20
  declare class DocumentBuilder {
2
- document: DocumentBuilder;
3
- constructor();
21
+ private document;
22
+ private documentType;
23
+ private selectedStatusType;
24
+ private statusConfig;
25
+ private rpcProviderUrl;
26
+ private requiredFields;
27
+ private isSigned;
28
+ constructor(input: Partial<VerifiableCredential>, documentType?: string);
29
+ credentialSubject(subject: Partial<VerifiableCredential>): this;
30
+ credentialStatus(config: W3CTransferableRecordsConfig | W3CVerifiableDocumentConfig): this;
31
+ expirationDate(date: string | Date): this;
32
+ renderMethod(method: RenderMethod): this;
33
+ sign(privateKey: PrivateKeyPair, cryptoSuite?: string): Promise<SignedVerifiableCredential>;
34
+ verify(): Promise<boolean>;
35
+ toString(): string;
36
+ private isTransferableRecordsConfig;
37
+ private isVerifiableDocumentConfig;
38
+ private validateRequiredFields;
39
+ private initializeDocument;
40
+ private buildContext;
41
+ private addContext;
42
+ private verifyTokenRegistry;
43
+ private supportsInterface;
4
44
  }
5
45
 
6
- export { DocumentBuilder };
46
+ export { DocumentBuilder, type RenderMethod, type W3CTransferableRecordsConfig, type W3CVerifiableDocumentConfig };
@@ -7,9 +7,11 @@ export { fetchEventTime, mergeTransfersV4, mergeTransfersV5, sortLogChain } from
7
7
  export { getEndorsementChain } from './endorsement-chain/retrieveEndorsementChain.js';
8
8
  export { EndorsementChain, ParsedLog, TitleEscrowTransferEvent, TitleEscrowTransferEventType, TokenTransferEvent, TokenTransferEventType, TradeTrustTokenEventType, TransferBaseEvent, TransferEvent, TransferEventType, TypedEvent } from './endorsement-chain/types.js';
9
9
  export { TitleEscrowInterface, fetchEndorsementChain, getTitleEscrowAddress, isTitleEscrowVersion } from './endorsement-chain/useEndorsementChain.js';
10
+ export { DocumentBuilder, RenderMethod, W3CTransferableRecordsConfig, W3CVerifiableDocumentConfig } from './documentBuilder.js';
10
11
  import '@trustvc/w3c-vc';
11
12
  import '@tradetrust-tt/tt-verify/dist/types/src/types/core';
12
13
  import 'ethersV6';
13
14
  import '@ethersproject/abstract-provider';
14
15
  import 'ethers';
15
16
  import 'ethers/lib/utils';
17
+ import '@trustvc/w3c-issuer';
@@ -19,6 +19,7 @@ export { fetchEventTime, mergeTransfersV4, mergeTransfersV5, sortLogChain } from
19
19
  export { getEndorsementChain } from './core/endorsement-chain/retrieveEndorsementChain.js';
20
20
  export { EndorsementChain, ParsedLog, TitleEscrowTransferEvent, TitleEscrowTransferEventType, TokenTransferEvent, TokenTransferEventType, TradeTrustTokenEventType, TransferBaseEvent, TransferEvent, TransferEventType, TypedEvent } from './core/endorsement-chain/types.js';
21
21
  export { TitleEscrowInterface, fetchEndorsementChain, getTitleEscrowAddress, isTitleEscrowVersion } from './core/endorsement-chain/useEndorsementChain.js';
22
+ export { DocumentBuilder, RenderMethod, W3CTransferableRecordsConfig, W3CVerifiableDocumentConfig } from './core/documentBuilder.js';
22
23
  export { signOA } from './open-attestation/sign.js';
23
24
  export { KeyPair } from './open-attestation/types.js';
24
25
  export { diagnose, getAssetId, getDocumentData, getIssuerAddress, getTemplateURL, isObfuscated, isRawV2Document, isRawV3Document, isSignedWrappedV2Document, isSignedWrappedV3Document, isTransferableAsset, isWrappedV2Document, isWrappedV3Document } from './open-attestation/utils.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustvc/trustvc",
3
- "version": "1.2.11",
3
+ "version": "1.4.0",
4
4
  "description": "TrustVC library",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",