@trustvc/trustvc 1.0.0-alpha.6 → 1.0.0-alpha.8

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.
Files changed (23) hide show
  1. package/dist/config.d.mts +3 -0
  2. package/dist/config.d.ts +3 -0
  3. package/dist/config.js +5 -0
  4. package/dist/core/decrypt.js +4 -5
  5. package/dist/core/encrypt.js +2 -3
  6. package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.d.mts +7 -0
  7. package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.d.ts +7 -0
  8. package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.js +132 -0
  9. package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.d.mts +30 -0
  10. package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.d.ts +30 -0
  11. package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.js +2 -0
  12. package/dist/core/fragments/document-status/transferableRecords/utils.d.mts +18 -0
  13. package/dist/core/fragments/document-status/transferableRecords/utils.d.ts +18 -0
  14. package/dist/core/fragments/document-status/transferableRecords/utils.js +75 -0
  15. package/dist/core/verify.js +12 -2
  16. package/dist/esm/config.js +3 -0
  17. package/dist/esm/core/decrypt.js +4 -5
  18. package/dist/esm/core/encrypt.js +2 -3
  19. package/dist/esm/core/fragments/document-status/transferableRecords/transferableRecordVerifier.js +109 -0
  20. package/dist/esm/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.js +1 -0
  21. package/dist/esm/core/fragments/document-status/transferableRecords/utils.js +72 -0
  22. package/dist/esm/core/verify.js +12 -2
  23. package/package.json +2 -1
@@ -0,0 +1,3 @@
1
+ declare const DEFAULT_KEY = "4d5a4e3f2f6d2b0a1f2e9b8f8a3c7a0b8d4f5c2e7b1a1c3f2e7b8c2d5a4f7e3e";
2
+
3
+ export { DEFAULT_KEY };
@@ -0,0 +1,3 @@
1
+ declare const DEFAULT_KEY = "4d5a4e3f2f6d2b0a1f2e9b8f8a3c7a0b8d4f5c2e7b1a1c3f2e7b8c2d5a4f7e3e";
2
+
3
+ export { DEFAULT_KEY };
package/dist/config.js ADDED
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_KEY = "4d5a4e3f2f6d2b0a1f2e9b8f8a3c7a0b8d4f5c2e7b1a1c3f2e7b8c2d5a4f7e3e";
4
+
5
+ exports.DEFAULT_KEY = DEFAULT_KEY;
@@ -1,20 +1,19 @@
1
1
  'use strict';
2
2
 
3
+ var config = require('src/config');
3
4
  var stringUtils = require('../utils/stringUtils');
4
5
  var tsChacha20 = require('ts-chacha20');
5
6
 
6
7
  var __defProp = Object.defineProperty;
7
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
9
  function decrypt(encryptedMessage, key, nonce) {
9
- if (key.length === 0) {
10
- throw new Error("Key length must not be 0");
11
- }
12
- key = stringUtils.generate32ByteKey(key ?? "");
10
+ key = key.length > 0 ? key : config.DEFAULT_KEY;
11
+ key = stringUtils.generate32ByteKey(key);
13
12
  nonce = stringUtils.generate12ByteNonce(nonce ?? "");
14
13
  const keyBuffer = stringUtils.stringToUint8Array(key);
15
14
  const nonceBuffer = stringUtils.stringToUint8Array(nonce);
16
15
  const chacha20 = new tsChacha20.Chacha20(keyBuffer, nonceBuffer);
17
- const encryptedBuffer = Buffer.from(encryptedMessage, "hex");
16
+ const encryptedBuffer = new Uint8Array(Buffer.from(encryptedMessage, "hex"));
18
17
  const decrypted = chacha20.decrypt(encryptedBuffer);
19
18
  return Buffer.from(decrypted).toString("utf-8");
20
19
  }
@@ -1,14 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ var config = require('src/config');
3
4
  var stringUtils = require('../utils/stringUtils');
4
5
  var tsChacha20 = require('ts-chacha20');
5
6
 
6
7
  var __defProp = Object.defineProperty;
7
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
9
  function encrypt(message, key, nonce) {
9
- if (key.length === 0) {
10
- throw new Error("Key length must not be 0");
11
- }
10
+ key = key.length > 0 ? key : config.DEFAULT_KEY;
12
11
  key = stringUtils.generate32ByteKey(key);
13
12
  nonce = stringUtils.generate12ByteNonce(nonce ?? "");
14
13
  const keyBuffer = stringUtils.stringToUint8Array(key);
@@ -0,0 +1,7 @@
1
+ import { VerifierType } from './transferableRecordVerifier.types.mjs';
2
+ import '@govtechsg/oa-verify';
3
+
4
+ declare const TRANSFERABLE_RECORDS_TYPE = "TransferableRecords";
5
+ declare const credentialStatusTransferableRecordVerifier: VerifierType;
6
+
7
+ export { TRANSFERABLE_RECORDS_TYPE, credentialStatusTransferableRecordVerifier };
@@ -0,0 +1,7 @@
1
+ import { VerifierType } from './transferableRecordVerifier.types.js';
2
+ import '@govtechsg/oa-verify';
3
+
4
+ declare const TRANSFERABLE_RECORDS_TYPE = "TransferableRecords";
5
+ declare const credentialStatusTransferableRecordVerifier: VerifierType;
6
+
7
+ export { TRANSFERABLE_RECORDS_TYPE, credentialStatusTransferableRecordVerifier };
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ var oaVerify = require('@govtechsg/oa-verify');
4
+ var openAttestation = require('@govtechsg/open-attestation');
5
+ var w3cVC = require('@trustvc/w3c-vc');
6
+ var utils = require('./utils');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var w3cVC__namespace = /*#__PURE__*/_interopNamespace(w3cVC);
27
+
28
+ var __defProp = Object.defineProperty;
29
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
30
+ const TRANSFERABLE_RECORDS_TYPE = "TransferableRecords";
31
+ const type = "DOCUMENT_STATUS";
32
+ const name = TRANSFERABLE_RECORDS_TYPE;
33
+ const verify = /* @__PURE__ */ __name(async (document, options) => {
34
+ let signedDocument;
35
+ let tokenId;
36
+ if (openAttestation.v4.isSignedWrappedDocument(document)) {
37
+ signedDocument = document;
38
+ tokenId = "0x" + signedDocument?.proof?.targetHash;
39
+ } else if (w3cVC__namespace.isSignedDocument(document)) {
40
+ signedDocument = document;
41
+ tokenId = "0x" + signedDocument?.credentialStatus?.tokenId;
42
+ }
43
+ const credentialStatus = signedDocument?.credentialStatus;
44
+ if (!credentialStatus?.tokenRegistry) {
45
+ throw new oaVerify.CodedError(
46
+ "Document's credentialStatus does not have tokenRegistry",
47
+ oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT,
48
+ oaVerify.OpenAttestationEthereumTokenRegistryStatusCode[oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT]
49
+ );
50
+ }
51
+ if (!credentialStatus?.tokenNetwork || !credentialStatus?.tokenNetwork?.chainId) {
52
+ throw new oaVerify.CodedError(
53
+ "Document's credentialStatus does not have tokenNetwork.chainId",
54
+ oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT,
55
+ oaVerify.OpenAttestationEthereumTokenRegistryStatusCode[oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT]
56
+ );
57
+ }
58
+ const { provider } = options;
59
+ const mintStatus = await utils.isTokenMintedOnRegistry({
60
+ tokenRegistryAddress: credentialStatus?.tokenRegistry,
61
+ tokenId,
62
+ provider
63
+ });
64
+ const result = {
65
+ name,
66
+ type,
67
+ status: "INVALID",
68
+ data: {
69
+ tokenRegistry: credentialStatus.tokenRegistry
70
+ }
71
+ };
72
+ if (oaVerify.ValidTokenRegistryStatus.guard(mintStatus)) {
73
+ result.status = "VALID";
74
+ } else {
75
+ result.reason = mintStatus.reason;
76
+ }
77
+ return result;
78
+ }, "verify");
79
+ const skip = /* @__PURE__ */ __name(async () => {
80
+ return {
81
+ status: "SKIPPED",
82
+ type,
83
+ name,
84
+ reason: {
85
+ code: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.SKIPPED,
86
+ codeString: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode[oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.SKIPPED],
87
+ message: `Document does not have TransferableRecords status`
88
+ }
89
+ };
90
+ }, "skip");
91
+ const test = /* @__PURE__ */ __name((document) => {
92
+ if (document?.credentialStatus?.type === TRANSFERABLE_RECORDS_TYPE) {
93
+ return true;
94
+ }
95
+ return false;
96
+ }, "test");
97
+ const credentialStatusTransferableRecordVerifier = {
98
+ skip,
99
+ test,
100
+ verify: /* @__PURE__ */ __name(async (...args) => {
101
+ try {
102
+ return await verify(...args);
103
+ } catch (e) {
104
+ if (e instanceof oaVerify.CodedError) {
105
+ const err = {
106
+ name,
107
+ type,
108
+ status: "ERROR",
109
+ reason: {
110
+ code: e.code,
111
+ codeString: e.codeString,
112
+ message: e.message
113
+ }
114
+ };
115
+ return err;
116
+ }
117
+ return {
118
+ name,
119
+ type,
120
+ status: "ERROR",
121
+ reason: {
122
+ code: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.UNEXPECTED_ERROR,
123
+ codeString: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode[oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.UNEXPECTED_ERROR],
124
+ message: e instanceof Error ? e.message : "An unexpected error occurred"
125
+ }
126
+ };
127
+ }
128
+ }, "verify")
129
+ };
130
+
131
+ exports.TRANSFERABLE_RECORDS_TYPE = TRANSFERABLE_RECORDS_TYPE;
132
+ exports.credentialStatusTransferableRecordVerifier = credentialStatusTransferableRecordVerifier;
@@ -0,0 +1,30 @@
1
+ import { OpenAttestationEthereumTokenRegistryStatusCode, VerificationFragment, ErrorVerificationFragment, Verifier } from '@govtechsg/oa-verify';
2
+
3
+ type TransferableRecordsErrorReason = {
4
+ code: OpenAttestationEthereumTokenRegistryStatusCode;
5
+ codeString: string;
6
+ message: string;
7
+ };
8
+ type TransferableRecordsResultFragment = VerificationFragment & {
9
+ status: 'VALID' | 'INVALID';
10
+ data: {
11
+ tokenRegistry: string;
12
+ };
13
+ reason?: TransferableRecordsErrorReason;
14
+ };
15
+ type TransferableRecordsErrorFragment = Omit<ErrorVerificationFragment<never>, 'data'> & {
16
+ data?: never;
17
+ reason: TransferableRecordsErrorReason;
18
+ };
19
+ type TransferableRecordsVerificationFragment = TransferableRecordsResultFragment | TransferableRecordsErrorFragment;
20
+ type VerifierType = Verifier<TransferableRecordsVerificationFragment>;
21
+ type TransferableRecordCredentialStatus = {
22
+ type: 'TransferableRecords';
23
+ tokenRegistry: string;
24
+ tokenNetwork: {
25
+ chainId: number;
26
+ name: string;
27
+ };
28
+ };
29
+
30
+ export type { TransferableRecordCredentialStatus, TransferableRecordsErrorFragment, TransferableRecordsErrorReason, TransferableRecordsResultFragment, TransferableRecordsVerificationFragment, VerifierType };
@@ -0,0 +1,30 @@
1
+ import { OpenAttestationEthereumTokenRegistryStatusCode, VerificationFragment, ErrorVerificationFragment, Verifier } from '@govtechsg/oa-verify';
2
+
3
+ type TransferableRecordsErrorReason = {
4
+ code: OpenAttestationEthereumTokenRegistryStatusCode;
5
+ codeString: string;
6
+ message: string;
7
+ };
8
+ type TransferableRecordsResultFragment = VerificationFragment & {
9
+ status: 'VALID' | 'INVALID';
10
+ data: {
11
+ tokenRegistry: string;
12
+ };
13
+ reason?: TransferableRecordsErrorReason;
14
+ };
15
+ type TransferableRecordsErrorFragment = Omit<ErrorVerificationFragment<never>, 'data'> & {
16
+ data?: never;
17
+ reason: TransferableRecordsErrorReason;
18
+ };
19
+ type TransferableRecordsVerificationFragment = TransferableRecordsResultFragment | TransferableRecordsErrorFragment;
20
+ type VerifierType = Verifier<TransferableRecordsVerificationFragment>;
21
+ type TransferableRecordCredentialStatus = {
22
+ type: 'TransferableRecords';
23
+ tokenRegistry: string;
24
+ tokenNetwork: {
25
+ chainId: number;
26
+ name: string;
27
+ };
28
+ };
29
+
30
+ export type { TransferableRecordCredentialStatus, TransferableRecordsErrorFragment, TransferableRecordsErrorReason, TransferableRecordsResultFragment, TransferableRecordsVerificationFragment, VerifierType };
@@ -0,0 +1,18 @@
1
+ import { ValidTokenRegistryStatus, InvalidTokenRegistryStatus } from '@govtechsg/oa-verify';
2
+ import { providers, errors } from 'ethers';
3
+
4
+ type EthersError = {
5
+ message?: string;
6
+ data?: string;
7
+ method?: string;
8
+ reason?: string;
9
+ code?: errors;
10
+ };
11
+ declare const decodeError: (error: EthersError) => "Document has not been issued under token registry" | "Token registry is not found" | "ENS name is not configured" | "Invalid token registry address" | "Invalid contract arguments";
12
+ declare const isTokenMintedOnRegistry: ({ tokenRegistryAddress, tokenId, provider, }: {
13
+ tokenRegistryAddress: string;
14
+ tokenId: string;
15
+ provider: providers.Provider;
16
+ }) => Promise<ValidTokenRegistryStatus | InvalidTokenRegistryStatus>;
17
+
18
+ export { decodeError, isTokenMintedOnRegistry };
@@ -0,0 +1,18 @@
1
+ import { ValidTokenRegistryStatus, InvalidTokenRegistryStatus } from '@govtechsg/oa-verify';
2
+ import { providers, errors } from 'ethers';
3
+
4
+ type EthersError = {
5
+ message?: string;
6
+ data?: string;
7
+ method?: string;
8
+ reason?: string;
9
+ code?: errors;
10
+ };
11
+ declare const decodeError: (error: EthersError) => "Document has not been issued under token registry" | "Token registry is not found" | "ENS name is not configured" | "Invalid token registry address" | "Invalid contract arguments";
12
+ declare const isTokenMintedOnRegistry: ({ tokenRegistryAddress, tokenId, provider, }: {
13
+ tokenRegistryAddress: string;
14
+ tokenId: string;
15
+ provider: providers.Provider;
16
+ }) => Promise<ValidTokenRegistryStatus | InvalidTokenRegistryStatus>;
17
+
18
+ export { decodeError, isTokenMintedOnRegistry };
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ var oaVerify = require('@govtechsg/oa-verify');
4
+ var contracts = require('@tradetrust-tt/token-registry/dist/contracts');
5
+ var ethers = require('ethers');
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ const isNonExistentToken = /* @__PURE__ */ __name((error) => {
10
+ const message = error.message;
11
+ if (!message) {
12
+ return error.data && error.data.slice(0, 10) === "0x7e273289";
13
+ }
14
+ return message.includes("owner query for nonexistent token");
15
+ }, "isNonExistentToken");
16
+ const isMissingTokenRegistry = /* @__PURE__ */ __name((error) => {
17
+ return !error.reason && error.method?.toLowerCase() === "ownerOf(uint256)".toLowerCase() && error.code === ethers.errors.CALL_EXCEPTION;
18
+ }, "isMissingTokenRegistry");
19
+ const decodeError = /* @__PURE__ */ __name((error) => {
20
+ const reason = error.reason && Array.isArray(error.reason) ? error.reason[0] : error.reason ?? "";
21
+ switch (true) {
22
+ case isNonExistentToken(error):
23
+ return `Document has not been issued under token registry`;
24
+ case isMissingTokenRegistry(error):
25
+ return `Token registry is not found`;
26
+ case (reason.toLowerCase() === "ENS name not configured".toLowerCase() && error.code === ethers.errors.UNSUPPORTED_OPERATION):
27
+ return "ENS name is not configured";
28
+ case (reason.toLowerCase() === "invalid address".toLowerCase() && error.code === ethers.errors.INVALID_ARGUMENT):
29
+ return `Invalid token registry address`;
30
+ case error.code === ethers.errors.INVALID_ARGUMENT:
31
+ return `Invalid contract arguments`;
32
+ case error.code === ethers.errors.SERVER_ERROR:
33
+ case error.code === ethers.errors.NETWORK_ERROR:
34
+ throw new oaVerify.CodedError(
35
+ "Unable to connect to the network, please try again later",
36
+ oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.SERVER_ERROR,
37
+ oaVerify.OpenAttestationEthereumTokenRegistryStatusCode[oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.SERVER_ERROR]
38
+ );
39
+ default:
40
+ throw error;
41
+ }
42
+ }, "decodeError");
43
+ const isTokenMintedOnRegistry = /* @__PURE__ */ __name(async ({
44
+ tokenRegistryAddress,
45
+ tokenId,
46
+ provider
47
+ }) => {
48
+ try {
49
+ const tokenRegistryContract = contracts.TradeTrustToken__factory.connect(tokenRegistryAddress, provider);
50
+ const minted = await tokenRegistryContract.ownerOf(tokenId).then((owner) => owner !== ethers.constants.AddressZero);
51
+ return minted ? { minted, address: tokenRegistryAddress } : {
52
+ minted,
53
+ address: tokenRegistryAddress,
54
+ reason: {
55
+ code: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED,
56
+ codeString: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode[oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED],
57
+ message: `Document ${tokenId} has not been issued under contract ${tokenRegistryAddress}`
58
+ }
59
+ };
60
+ } catch (error) {
61
+ return {
62
+ minted: false,
63
+ address: tokenRegistryAddress,
64
+ reason: {
65
+ message: decodeError(error),
66
+ // message: (error as Error).message,
67
+ code: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED,
68
+ codeString: oaVerify.OpenAttestationEthereumTokenRegistryStatusCode[oaVerify.OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED]
69
+ }
70
+ };
71
+ }
72
+ }, "isTokenMintedOnRegistry");
73
+
74
+ exports.decodeError = decodeError;
75
+ exports.isTokenMintedOnRegistry = isTokenMintedOnRegistry;
@@ -6,13 +6,18 @@ var w3cSignatureIntegrity = require('./fragments/document-integrity/w3cSignature
6
6
  var w3cCredentialStatus = require('./fragments/document-status/w3cCredentialStatus');
7
7
  var w3cIssuerIdentity = require('./fragments/issuer-identity/w3cIssuerIdentity');
8
8
  var openAttestation = require('@govtechsg/open-attestation');
9
+ var transferableRecordVerifier = require('./fragments/document-status/transferableRecords/transferableRecordVerifier');
9
10
 
10
11
  var __defProp = Object.defineProperty;
11
12
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
12
13
  const verifyDocument = /* @__PURE__ */ __name(async (document, rpcProviderUrl) => {
13
14
  if (openAttestation.utils.isWrappedV2Document(document) || openAttestation.utils.isWrappedV3Document(document) || openAttestation.utils.isWrappedV4Document(document)) {
14
15
  const verify = oaVerify.verificationBuilder(
15
- [...oaVerify.openAttestationVerifiers, oaVerify.openAttestationDidIdentityProof],
16
+ [
17
+ ...oaVerify.openAttestationVerifiers,
18
+ oaVerify.openAttestationDidIdentityProof,
19
+ transferableRecordVerifier.credentialStatusTransferableRecordVerifier
20
+ ],
16
21
  {
17
22
  provider: new ethers.ethers.providers.JsonRpcProvider(rpcProviderUrl)
18
23
  // Use user-provided provider URL
@@ -21,7 +26,12 @@ const verifyDocument = /* @__PURE__ */ __name(async (document, rpcProviderUrl) =
21
26
  return verify(document);
22
27
  } else {
23
28
  const verify = oaVerify.verificationBuilder(
24
- [w3cSignatureIntegrity.w3cSignatureIntegrity, w3cCredentialStatus.w3cCredentialStatus, w3cIssuerIdentity.w3cIssuerIdentity],
29
+ [
30
+ w3cSignatureIntegrity.w3cSignatureIntegrity,
31
+ w3cCredentialStatus.w3cCredentialStatus,
32
+ w3cIssuerIdentity.w3cIssuerIdentity,
33
+ transferableRecordVerifier.credentialStatusTransferableRecordVerifier
34
+ ],
25
35
  {
26
36
  provider: new ethers.ethers.providers.JsonRpcProvider(rpcProviderUrl)
27
37
  // Use user-provided provider URL
@@ -0,0 +1,3 @@
1
+ const DEFAULT_KEY = "4d5a4e3f2f6d2b0a1f2e9b8f8a3c7a0b8d4f5c2e7b1a1c3f2e7b8c2d5a4f7e3e";
2
+
3
+ export { DEFAULT_KEY };
@@ -1,18 +1,17 @@
1
+ import { DEFAULT_KEY } from 'src/config';
1
2
  import { generate32ByteKey, generate12ByteNonce, stringToUint8Array } from '../utils/stringUtils';
2
3
  import { Chacha20 } from 'ts-chacha20';
3
4
 
4
5
  var __defProp = Object.defineProperty;
5
6
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
7
  function decrypt(encryptedMessage, key, nonce) {
7
- if (key.length === 0) {
8
- throw new Error("Key length must not be 0");
9
- }
10
- key = generate32ByteKey(key ?? "");
8
+ key = key.length > 0 ? key : DEFAULT_KEY;
9
+ key = generate32ByteKey(key);
11
10
  nonce = generate12ByteNonce(nonce ?? "");
12
11
  const keyBuffer = stringToUint8Array(key);
13
12
  const nonceBuffer = stringToUint8Array(nonce);
14
13
  const chacha20 = new Chacha20(keyBuffer, nonceBuffer);
15
- const encryptedBuffer = Buffer.from(encryptedMessage, "hex");
14
+ const encryptedBuffer = new Uint8Array(Buffer.from(encryptedMessage, "hex"));
16
15
  const decrypted = chacha20.decrypt(encryptedBuffer);
17
16
  return Buffer.from(decrypted).toString("utf-8");
18
17
  }
@@ -1,12 +1,11 @@
1
+ import { DEFAULT_KEY } from 'src/config';
1
2
  import { generate32ByteKey, generate12ByteNonce, stringToUint8Array } from '../utils/stringUtils';
2
3
  import { Chacha20 } from 'ts-chacha20';
3
4
 
4
5
  var __defProp = Object.defineProperty;
5
6
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
7
  function encrypt(message, key, nonce) {
7
- if (key.length === 0) {
8
- throw new Error("Key length must not be 0");
9
- }
8
+ key = key.length > 0 ? key : DEFAULT_KEY;
10
9
  key = generate32ByteKey(key);
11
10
  nonce = generate12ByteNonce(nonce ?? "");
12
11
  const keyBuffer = stringToUint8Array(key);
@@ -0,0 +1,109 @@
1
+ import { OpenAttestationEthereumTokenRegistryStatusCode, CodedError, ValidTokenRegistryStatus } from '@govtechsg/oa-verify';
2
+ import { v4 } from '@govtechsg/open-attestation';
3
+ import * as w3cVC from '@trustvc/w3c-vc';
4
+ import { isTokenMintedOnRegistry } from './utils';
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ const TRANSFERABLE_RECORDS_TYPE = "TransferableRecords";
9
+ const type = "DOCUMENT_STATUS";
10
+ const name = TRANSFERABLE_RECORDS_TYPE;
11
+ const verify = /* @__PURE__ */ __name(async (document, options) => {
12
+ let signedDocument;
13
+ let tokenId;
14
+ if (v4.isSignedWrappedDocument(document)) {
15
+ signedDocument = document;
16
+ tokenId = "0x" + signedDocument?.proof?.targetHash;
17
+ } else if (w3cVC.isSignedDocument(document)) {
18
+ signedDocument = document;
19
+ tokenId = "0x" + signedDocument?.credentialStatus?.tokenId;
20
+ }
21
+ const credentialStatus = signedDocument?.credentialStatus;
22
+ if (!credentialStatus?.tokenRegistry) {
23
+ throw new CodedError(
24
+ "Document's credentialStatus does not have tokenRegistry",
25
+ OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT,
26
+ OpenAttestationEthereumTokenRegistryStatusCode[OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT]
27
+ );
28
+ }
29
+ if (!credentialStatus?.tokenNetwork || !credentialStatus?.tokenNetwork?.chainId) {
30
+ throw new CodedError(
31
+ "Document's credentialStatus does not have tokenNetwork.chainId",
32
+ OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT,
33
+ OpenAttestationEthereumTokenRegistryStatusCode[OpenAttestationEthereumTokenRegistryStatusCode.UNRECOGNIZED_DOCUMENT]
34
+ );
35
+ }
36
+ const { provider } = options;
37
+ const mintStatus = await isTokenMintedOnRegistry({
38
+ tokenRegistryAddress: credentialStatus?.tokenRegistry,
39
+ tokenId,
40
+ provider
41
+ });
42
+ const result = {
43
+ name,
44
+ type,
45
+ status: "INVALID",
46
+ data: {
47
+ tokenRegistry: credentialStatus.tokenRegistry
48
+ }
49
+ };
50
+ if (ValidTokenRegistryStatus.guard(mintStatus)) {
51
+ result.status = "VALID";
52
+ } else {
53
+ result.reason = mintStatus.reason;
54
+ }
55
+ return result;
56
+ }, "verify");
57
+ const skip = /* @__PURE__ */ __name(async () => {
58
+ return {
59
+ status: "SKIPPED",
60
+ type,
61
+ name,
62
+ reason: {
63
+ code: OpenAttestationEthereumTokenRegistryStatusCode.SKIPPED,
64
+ codeString: OpenAttestationEthereumTokenRegistryStatusCode[OpenAttestationEthereumTokenRegistryStatusCode.SKIPPED],
65
+ message: `Document does not have TransferableRecords status`
66
+ }
67
+ };
68
+ }, "skip");
69
+ const test = /* @__PURE__ */ __name((document) => {
70
+ if (document?.credentialStatus?.type === TRANSFERABLE_RECORDS_TYPE) {
71
+ return true;
72
+ }
73
+ return false;
74
+ }, "test");
75
+ const credentialStatusTransferableRecordVerifier = {
76
+ skip,
77
+ test,
78
+ verify: /* @__PURE__ */ __name(async (...args) => {
79
+ try {
80
+ return await verify(...args);
81
+ } catch (e) {
82
+ if (e instanceof CodedError) {
83
+ const err = {
84
+ name,
85
+ type,
86
+ status: "ERROR",
87
+ reason: {
88
+ code: e.code,
89
+ codeString: e.codeString,
90
+ message: e.message
91
+ }
92
+ };
93
+ return err;
94
+ }
95
+ return {
96
+ name,
97
+ type,
98
+ status: "ERROR",
99
+ reason: {
100
+ code: OpenAttestationEthereumTokenRegistryStatusCode.UNEXPECTED_ERROR,
101
+ codeString: OpenAttestationEthereumTokenRegistryStatusCode[OpenAttestationEthereumTokenRegistryStatusCode.UNEXPECTED_ERROR],
102
+ message: e instanceof Error ? e.message : "An unexpected error occurred"
103
+ }
104
+ };
105
+ }
106
+ }, "verify")
107
+ };
108
+
109
+ export { TRANSFERABLE_RECORDS_TYPE, credentialStatusTransferableRecordVerifier };
@@ -0,0 +1,72 @@
1
+ import { CodedError, OpenAttestationEthereumTokenRegistryStatusCode } from '@govtechsg/oa-verify';
2
+ import { TradeTrustToken__factory } from '@tradetrust-tt/token-registry/dist/contracts';
3
+ import { errors, constants } from 'ethers';
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ const isNonExistentToken = /* @__PURE__ */ __name((error) => {
8
+ const message = error.message;
9
+ if (!message) {
10
+ return error.data && error.data.slice(0, 10) === "0x7e273289";
11
+ }
12
+ return message.includes("owner query for nonexistent token");
13
+ }, "isNonExistentToken");
14
+ const isMissingTokenRegistry = /* @__PURE__ */ __name((error) => {
15
+ return !error.reason && error.method?.toLowerCase() === "ownerOf(uint256)".toLowerCase() && error.code === errors.CALL_EXCEPTION;
16
+ }, "isMissingTokenRegistry");
17
+ const decodeError = /* @__PURE__ */ __name((error) => {
18
+ const reason = error.reason && Array.isArray(error.reason) ? error.reason[0] : error.reason ?? "";
19
+ switch (true) {
20
+ case isNonExistentToken(error):
21
+ return `Document has not been issued under token registry`;
22
+ case isMissingTokenRegistry(error):
23
+ return `Token registry is not found`;
24
+ case (reason.toLowerCase() === "ENS name not configured".toLowerCase() && error.code === errors.UNSUPPORTED_OPERATION):
25
+ return "ENS name is not configured";
26
+ case (reason.toLowerCase() === "invalid address".toLowerCase() && error.code === errors.INVALID_ARGUMENT):
27
+ return `Invalid token registry address`;
28
+ case error.code === errors.INVALID_ARGUMENT:
29
+ return `Invalid contract arguments`;
30
+ case error.code === errors.SERVER_ERROR:
31
+ case error.code === errors.NETWORK_ERROR:
32
+ throw new CodedError(
33
+ "Unable to connect to the network, please try again later",
34
+ OpenAttestationEthereumTokenRegistryStatusCode.SERVER_ERROR,
35
+ OpenAttestationEthereumTokenRegistryStatusCode[OpenAttestationEthereumTokenRegistryStatusCode.SERVER_ERROR]
36
+ );
37
+ default:
38
+ throw error;
39
+ }
40
+ }, "decodeError");
41
+ const isTokenMintedOnRegistry = /* @__PURE__ */ __name(async ({
42
+ tokenRegistryAddress,
43
+ tokenId,
44
+ provider
45
+ }) => {
46
+ try {
47
+ const tokenRegistryContract = TradeTrustToken__factory.connect(tokenRegistryAddress, provider);
48
+ const minted = await tokenRegistryContract.ownerOf(tokenId).then((owner) => owner !== constants.AddressZero);
49
+ return minted ? { minted, address: tokenRegistryAddress } : {
50
+ minted,
51
+ address: tokenRegistryAddress,
52
+ reason: {
53
+ code: OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED,
54
+ codeString: OpenAttestationEthereumTokenRegistryStatusCode[OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED],
55
+ message: `Document ${tokenId} has not been issued under contract ${tokenRegistryAddress}`
56
+ }
57
+ };
58
+ } catch (error) {
59
+ return {
60
+ minted: false,
61
+ address: tokenRegistryAddress,
62
+ reason: {
63
+ message: decodeError(error),
64
+ // message: (error as Error).message,
65
+ code: OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED,
66
+ codeString: OpenAttestationEthereumTokenRegistryStatusCode[OpenAttestationEthereumTokenRegistryStatusCode.DOCUMENT_NOT_MINTED]
67
+ }
68
+ };
69
+ }
70
+ }, "isTokenMintedOnRegistry");
71
+
72
+ export { decodeError, isTokenMintedOnRegistry };
@@ -4,13 +4,18 @@ import { w3cSignatureIntegrity } from './fragments/document-integrity/w3cSignatu
4
4
  import { w3cCredentialStatus } from './fragments/document-status/w3cCredentialStatus';
5
5
  import { w3cIssuerIdentity } from './fragments/issuer-identity/w3cIssuerIdentity';
6
6
  import { utils } from '@govtechsg/open-attestation';
7
+ import { credentialStatusTransferableRecordVerifier } from './fragments/document-status/transferableRecords/transferableRecordVerifier';
7
8
 
8
9
  var __defProp = Object.defineProperty;
9
10
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
11
  const verifyDocument = /* @__PURE__ */ __name(async (document, rpcProviderUrl) => {
11
12
  if (utils.isWrappedV2Document(document) || utils.isWrappedV3Document(document) || utils.isWrappedV4Document(document)) {
12
13
  const verify = verificationBuilder(
13
- [...openAttestationVerifiers, openAttestationDidIdentityProof],
14
+ [
15
+ ...openAttestationVerifiers,
16
+ openAttestationDidIdentityProof,
17
+ credentialStatusTransferableRecordVerifier
18
+ ],
14
19
  {
15
20
  provider: new ethers.providers.JsonRpcProvider(rpcProviderUrl)
16
21
  // Use user-provided provider URL
@@ -19,7 +24,12 @@ const verifyDocument = /* @__PURE__ */ __name(async (document, rpcProviderUrl) =
19
24
  return verify(document);
20
25
  } else {
21
26
  const verify = verificationBuilder(
22
- [w3cSignatureIntegrity, w3cCredentialStatus, w3cIssuerIdentity],
27
+ [
28
+ w3cSignatureIntegrity,
29
+ w3cCredentialStatus,
30
+ w3cIssuerIdentity,
31
+ credentialStatusTransferableRecordVerifier
32
+ ],
23
33
  {
24
34
  provider: new ethers.providers.JsonRpcProvider(rpcProviderUrl)
25
35
  // Use user-provided provider URL
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustvc/trustvc",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "TrustVC library",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -70,6 +70,7 @@
70
70
  "prettier": "^3.3.2",
71
71
  "rimraf": "^5.0.10",
72
72
  "semantic-release": "^20.1.3",
73
+ "ts-node": "^10.9.2",
73
74
  "tsup": "^8.3.0",
74
75
  "typescript": "^5.6.2",
75
76
  "typescript-eslint": "^8.8.0",