@trustvc/trustvc 1.0.0-alpha.6 → 1.0.0-alpha.7
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/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.d.mts +7 -0
- package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.d.ts +7 -0
- package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.js +132 -0
- package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.d.mts +30 -0
- package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.d.ts +30 -0
- package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.js +2 -0
- package/dist/core/fragments/document-status/transferableRecords/utils.d.mts +18 -0
- package/dist/core/fragments/document-status/transferableRecords/utils.d.ts +18 -0
- package/dist/core/fragments/document-status/transferableRecords/utils.js +75 -0
- package/dist/core/verify.js +12 -2
- package/dist/esm/core/fragments/document-status/transferableRecords/transferableRecordVerifier.js +109 -0
- package/dist/esm/core/fragments/document-status/transferableRecords/transferableRecordVerifier.types.js +1 -0
- package/dist/esm/core/fragments/document-status/transferableRecords/utils.js +72 -0
- package/dist/esm/core/verify.js +12 -2
- package/package.json +2 -1
package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.d.mts
ADDED
|
@@ -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 };
|
package/dist/core/fragments/document-status/transferableRecords/transferableRecordVerifier.d.ts
ADDED
|
@@ -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;
|
package/dist/core/verify.js
CHANGED
|
@@ -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
|
-
[
|
|
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
|
-
[
|
|
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
|
package/dist/esm/core/fragments/document-status/transferableRecords/transferableRecordVerifier.js
ADDED
|
@@ -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 @@
|
|
|
1
|
+
|
|
@@ -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 };
|
package/dist/esm/core/verify.js
CHANGED
|
@@ -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
|
-
[
|
|
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
|
-
[
|
|
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.
|
|
3
|
+
"version": "1.0.0-alpha.7",
|
|
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",
|