@oma3/omatrust 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/app-registry/index.cjs +244 -0
- package/dist/app-registry/index.cjs.map +1 -0
- package/dist/app-registry/index.d.cts +1 -0
- package/dist/app-registry/index.d.ts +1 -0
- package/dist/app-registry/index.js +209 -0
- package/dist/app-registry/index.js.map +1 -0
- package/dist/identity/index.cjs +332 -0
- package/dist/identity/index.cjs.map +1 -0
- package/dist/identity/index.d.cts +1 -0
- package/dist/identity/index.d.ts +1 -0
- package/dist/identity/index.js +296 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/index-ChbJxwOA.d.cts +415 -0
- package/dist/index-ChbJxwOA.d.ts +415 -0
- package/dist/index-QZDExA4I.d.cts +90 -0
- package/dist/index-QZDExA4I.d.ts +90 -0
- package/dist/index-_Bkhoj8k.d.cts +93 -0
- package/dist/index-_Bkhoj8k.d.ts +93 -0
- package/dist/index.cjs +1838 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +1824 -0
- package/dist/index.js.map +1 -0
- package/dist/reputation/index.cjs +1470 -0
- package/dist/reputation/index.cjs.map +1 -0
- package/dist/reputation/index.d.cts +1 -0
- package/dist/reputation/index.d.ts +1 -0
- package/dist/reputation/index.js +1416 -0
- package/dist/reputation/index.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,1470 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var easSdk = require('@ethereum-attestation-service/eas-sdk');
|
|
4
|
+
var ethers = require('ethers');
|
|
5
|
+
var canonicalize = require('canonicalize');
|
|
6
|
+
var promises = require('dns/promises');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var canonicalize__default = /*#__PURE__*/_interopDefault(canonicalize);
|
|
11
|
+
|
|
12
|
+
// src/reputation/submit.ts
|
|
13
|
+
|
|
14
|
+
// src/shared/errors.ts
|
|
15
|
+
var OmaTrustError = class extends Error {
|
|
16
|
+
code;
|
|
17
|
+
details;
|
|
18
|
+
constructor(code, message, details) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "OmaTrustError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.details = details;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/reputation/encode.ts
|
|
27
|
+
function normalizeSchema(schema) {
|
|
28
|
+
if (Array.isArray(schema)) {
|
|
29
|
+
if (schema.length === 0) {
|
|
30
|
+
throw new OmaTrustError("INVALID_INPUT", "schema array cannot be empty");
|
|
31
|
+
}
|
|
32
|
+
return schema;
|
|
33
|
+
}
|
|
34
|
+
if (typeof schema !== "string" || schema.trim().length === 0) {
|
|
35
|
+
throw new OmaTrustError("INVALID_INPUT", "schema must be a non-empty string or SchemaField[]");
|
|
36
|
+
}
|
|
37
|
+
const fields = schema.split(",").map((part) => part.trim()).filter((part) => part.length > 0).map((part) => {
|
|
38
|
+
const pieces = part.split(/\s+/);
|
|
39
|
+
if (pieces.length < 2) {
|
|
40
|
+
throw new OmaTrustError("INVALID_INPUT", "Invalid schema field", { part });
|
|
41
|
+
}
|
|
42
|
+
const type = pieces[0];
|
|
43
|
+
const name = pieces.slice(1).join(" ");
|
|
44
|
+
return { type, name };
|
|
45
|
+
});
|
|
46
|
+
if (fields.length === 0) {
|
|
47
|
+
throw new OmaTrustError("INVALID_INPUT", "No schema fields found");
|
|
48
|
+
}
|
|
49
|
+
return fields;
|
|
50
|
+
}
|
|
51
|
+
function schemaToString(schema) {
|
|
52
|
+
if (typeof schema === "string") {
|
|
53
|
+
return schema;
|
|
54
|
+
}
|
|
55
|
+
return schema.map((field) => `${field.type} ${field.name}`).join(", ");
|
|
56
|
+
}
|
|
57
|
+
function encodeAttestationData(schema, data) {
|
|
58
|
+
if (!data || typeof data !== "object") {
|
|
59
|
+
throw new OmaTrustError("INVALID_INPUT", "data must be an object");
|
|
60
|
+
}
|
|
61
|
+
const fields = normalizeSchema(schema);
|
|
62
|
+
const schemaString = schemaToString(fields);
|
|
63
|
+
const encoder = new easSdk.SchemaEncoder(schemaString);
|
|
64
|
+
const encoded = encoder.encodeData(
|
|
65
|
+
fields.map((field) => ({
|
|
66
|
+
name: field.name,
|
|
67
|
+
type: field.type,
|
|
68
|
+
value: data[field.name]
|
|
69
|
+
}))
|
|
70
|
+
);
|
|
71
|
+
return encoded;
|
|
72
|
+
}
|
|
73
|
+
function decodeAttestationData(schema, encodedData) {
|
|
74
|
+
if (typeof encodedData !== "string" || !encodedData.startsWith("0x")) {
|
|
75
|
+
throw new OmaTrustError("INVALID_INPUT", "encodedData must be a hex string");
|
|
76
|
+
}
|
|
77
|
+
const fields = normalizeSchema(schema);
|
|
78
|
+
const schemaString = schemaToString(fields);
|
|
79
|
+
const encoder = new easSdk.SchemaEncoder(schemaString);
|
|
80
|
+
const decoded = encoder.decodeData(encodedData);
|
|
81
|
+
const result = {};
|
|
82
|
+
for (const item of decoded) {
|
|
83
|
+
const value = item.value;
|
|
84
|
+
if (value && typeof value === "object" && "value" in value) {
|
|
85
|
+
result[item.name] = value.value;
|
|
86
|
+
} else {
|
|
87
|
+
result[item.name] = value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
function extractExpirationTime(data) {
|
|
93
|
+
const keys = ["expirationTime", "expiration", "validUntil", "expiresAt", "expires"];
|
|
94
|
+
for (const key of keys) {
|
|
95
|
+
const value = data[key];
|
|
96
|
+
if (value === void 0 || value === null) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === "bigint") {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
if (typeof value === "number") {
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
106
|
+
return BigInt(value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/shared/assert.ts
|
|
113
|
+
function assertString(value, name, code = "INVALID_INPUT") {
|
|
114
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
115
|
+
throw new OmaTrustError(code, `${name} must be a non-empty string`, { value });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/identity/caip.ts
|
|
120
|
+
var CAIP_10_REGEX = /^(?<namespace>[a-z0-9-]+):(?<reference>[a-zA-Z0-9-]+):(?<address>.+)$/;
|
|
121
|
+
function parseCaip10(input) {
|
|
122
|
+
assertString(input, "input", "INVALID_CAIP");
|
|
123
|
+
const trimmed = input.trim();
|
|
124
|
+
const match = trimmed.match(CAIP_10_REGEX);
|
|
125
|
+
if (!match?.groups) {
|
|
126
|
+
throw new OmaTrustError("INVALID_CAIP", "Invalid CAIP-10 format", { input });
|
|
127
|
+
}
|
|
128
|
+
const namespace = match.groups.namespace;
|
|
129
|
+
const reference = match.groups.reference;
|
|
130
|
+
const address = match.groups.address;
|
|
131
|
+
if (!namespace || !reference || !address) {
|
|
132
|
+
throw new OmaTrustError("INVALID_CAIP", "Invalid CAIP-10 components", { input });
|
|
133
|
+
}
|
|
134
|
+
return { namespace, reference, address };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/identity/did.ts
|
|
138
|
+
var DID_REGEX = /^did:[a-z0-9]+:.+$/i;
|
|
139
|
+
function isValidDid(did) {
|
|
140
|
+
return DID_REGEX.test(did);
|
|
141
|
+
}
|
|
142
|
+
function extractDidMethod(did) {
|
|
143
|
+
const match = did.match(/^did:([a-z0-9]+):/i);
|
|
144
|
+
return match ? match[1] : null;
|
|
145
|
+
}
|
|
146
|
+
function normalizeDomain(domain) {
|
|
147
|
+
assertString(domain, "domain", "INVALID_DID");
|
|
148
|
+
return domain.trim().toLowerCase().replace(/\.$/, "");
|
|
149
|
+
}
|
|
150
|
+
function normalizeDidWeb(input) {
|
|
151
|
+
assertString(input, "input", "INVALID_DID");
|
|
152
|
+
const trimmed = input.trim();
|
|
153
|
+
if (trimmed.startsWith("did:") && !trimmed.startsWith("did:web:")) {
|
|
154
|
+
throw new OmaTrustError("INVALID_DID", "Expected did:web DID", { input });
|
|
155
|
+
}
|
|
156
|
+
const identifier = trimmed.startsWith("did:web:") ? trimmed.slice("did:web:".length) : trimmed;
|
|
157
|
+
const [host, ...pathParts] = identifier.split("/");
|
|
158
|
+
if (!host) {
|
|
159
|
+
throw new OmaTrustError("INVALID_DID", "Invalid did:web identifier", { input });
|
|
160
|
+
}
|
|
161
|
+
const normalizedHost = normalizeDomain(host);
|
|
162
|
+
const path = pathParts.length > 0 ? `/${pathParts.join("/")}` : "";
|
|
163
|
+
return `did:web:${normalizedHost}${path}`;
|
|
164
|
+
}
|
|
165
|
+
function normalizeDidPkh(input) {
|
|
166
|
+
assertString(input, "input", "INVALID_DID");
|
|
167
|
+
const trimmed = input.trim();
|
|
168
|
+
if (!trimmed.startsWith("did:pkh:")) {
|
|
169
|
+
throw new OmaTrustError("INVALID_DID", "Expected did:pkh DID", { input });
|
|
170
|
+
}
|
|
171
|
+
const parts = trimmed.split(":");
|
|
172
|
+
if (parts.length !== 5) {
|
|
173
|
+
throw new OmaTrustError("INVALID_DID", "Invalid did:pkh format", { input });
|
|
174
|
+
}
|
|
175
|
+
const [, , namespace, chainId, address] = parts;
|
|
176
|
+
if (!namespace || !chainId || !address) {
|
|
177
|
+
throw new OmaTrustError("INVALID_DID", "Invalid did:pkh components", { input });
|
|
178
|
+
}
|
|
179
|
+
return `did:pkh:${namespace.toLowerCase()}:${chainId}:${address.toLowerCase()}`;
|
|
180
|
+
}
|
|
181
|
+
function normalizeDidHandle(input) {
|
|
182
|
+
assertString(input, "input", "INVALID_DID");
|
|
183
|
+
const trimmed = input.trim();
|
|
184
|
+
if (!trimmed.startsWith("did:handle:")) {
|
|
185
|
+
throw new OmaTrustError("INVALID_DID", "Expected did:handle DID", { input });
|
|
186
|
+
}
|
|
187
|
+
const parts = trimmed.split(":");
|
|
188
|
+
if (parts.length !== 4) {
|
|
189
|
+
throw new OmaTrustError("INVALID_DID", "Invalid did:handle format", { input });
|
|
190
|
+
}
|
|
191
|
+
const [, , platform, username] = parts;
|
|
192
|
+
if (!platform || !username) {
|
|
193
|
+
throw new OmaTrustError("INVALID_DID", "Invalid did:handle components", { input });
|
|
194
|
+
}
|
|
195
|
+
return `did:handle:${platform.toLowerCase()}:${username}`;
|
|
196
|
+
}
|
|
197
|
+
function normalizeDidKey(input) {
|
|
198
|
+
assertString(input, "input", "INVALID_DID");
|
|
199
|
+
const trimmed = input.trim();
|
|
200
|
+
if (!trimmed.startsWith("did:key:")) {
|
|
201
|
+
throw new OmaTrustError("INVALID_DID", "Expected did:key DID", { input });
|
|
202
|
+
}
|
|
203
|
+
return trimmed;
|
|
204
|
+
}
|
|
205
|
+
function normalizeDid(input) {
|
|
206
|
+
assertString(input, "input", "INVALID_DID");
|
|
207
|
+
const trimmed = input.trim();
|
|
208
|
+
if (!trimmed.startsWith("did:")) {
|
|
209
|
+
return normalizeDidWeb(trimmed);
|
|
210
|
+
}
|
|
211
|
+
if (!isValidDid(trimmed)) {
|
|
212
|
+
throw new OmaTrustError("INVALID_DID", "Invalid DID format", { input });
|
|
213
|
+
}
|
|
214
|
+
const method = extractDidMethod(trimmed);
|
|
215
|
+
switch (method) {
|
|
216
|
+
case "web":
|
|
217
|
+
return normalizeDidWeb(trimmed);
|
|
218
|
+
case "pkh":
|
|
219
|
+
return normalizeDidPkh(trimmed);
|
|
220
|
+
case "handle":
|
|
221
|
+
return normalizeDidHandle(trimmed);
|
|
222
|
+
case "key":
|
|
223
|
+
return normalizeDidKey(trimmed);
|
|
224
|
+
default:
|
|
225
|
+
return trimmed;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function computeDidHash(did) {
|
|
229
|
+
const normalized = normalizeDid(did);
|
|
230
|
+
return ethers.keccak256(ethers.toUtf8Bytes(normalized));
|
|
231
|
+
}
|
|
232
|
+
function computeDidAddress(didHash) {
|
|
233
|
+
assertString(didHash, "didHash", "INVALID_DID");
|
|
234
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(didHash)) {
|
|
235
|
+
throw new OmaTrustError("INVALID_DID", "didHash must be 32-byte hex", { didHash });
|
|
236
|
+
}
|
|
237
|
+
const truncated = `0x${didHash.slice(-40)}`;
|
|
238
|
+
return ethers.getAddress(truncated);
|
|
239
|
+
}
|
|
240
|
+
function didToAddress(did) {
|
|
241
|
+
return computeDidAddress(computeDidHash(did));
|
|
242
|
+
}
|
|
243
|
+
function buildDidPkh(namespace, chainId, address) {
|
|
244
|
+
assertString(namespace, "namespace", "INVALID_DID");
|
|
245
|
+
assertString(address, "address", "INVALID_DID");
|
|
246
|
+
if (chainId === "" || chainId === null || chainId === void 0) {
|
|
247
|
+
throw new OmaTrustError("INVALID_DID", "chainId is required", { chainId });
|
|
248
|
+
}
|
|
249
|
+
return `did:pkh:${namespace.toLowerCase()}:${chainId}:${address.toLowerCase()}`;
|
|
250
|
+
}
|
|
251
|
+
function buildEvmDidPkh(chainId, address) {
|
|
252
|
+
return buildDidPkh("eip155", chainId, address);
|
|
253
|
+
}
|
|
254
|
+
function parseDidPkh(did) {
|
|
255
|
+
if (!did.startsWith("did:pkh:")) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const parts = did.split(":");
|
|
259
|
+
if (parts.length !== 5) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const [, , namespace, chainId, address] = parts;
|
|
263
|
+
if (!namespace || !chainId || !address) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
return { namespace, chainId, address };
|
|
267
|
+
}
|
|
268
|
+
function extractAddressFromDid(identifier) {
|
|
269
|
+
assertString(identifier, "identifier", "INVALID_DID");
|
|
270
|
+
if (identifier.startsWith("did:pkh:")) {
|
|
271
|
+
const pkh = parseDidPkh(normalizeDidPkh(identifier));
|
|
272
|
+
if (!pkh) {
|
|
273
|
+
throw new OmaTrustError("INVALID_DID", "Invalid did:pkh identifier", { identifier });
|
|
274
|
+
}
|
|
275
|
+
return pkh.address;
|
|
276
|
+
}
|
|
277
|
+
if (identifier.startsWith("did:ethr:")) {
|
|
278
|
+
const parts = identifier.replace("did:ethr:", "").split(":");
|
|
279
|
+
const address = parts.length === 1 ? parts[0] : parts[1];
|
|
280
|
+
if (!address || !ethers.isAddress(address)) {
|
|
281
|
+
throw new OmaTrustError("INVALID_DID", "Invalid did:ethr identifier", { identifier });
|
|
282
|
+
}
|
|
283
|
+
return ethers.getAddress(address);
|
|
284
|
+
}
|
|
285
|
+
if (identifier.match(/^[a-z0-9-]+:[a-zA-Z0-9-]+:0x[a-fA-F0-9]{40}$/)) {
|
|
286
|
+
const parsed = parseCaip10(identifier);
|
|
287
|
+
return parsed.address;
|
|
288
|
+
}
|
|
289
|
+
if (ethers.isAddress(identifier)) {
|
|
290
|
+
return ethers.getAddress(identifier);
|
|
291
|
+
}
|
|
292
|
+
throw new OmaTrustError("INVALID_DID", "Unsupported identifier format", { identifier });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/reputation/internal.ts
|
|
296
|
+
var ZERO_UID = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
297
|
+
function toBigIntOrDefault(value, fallback) {
|
|
298
|
+
if (value === void 0 || value === null) {
|
|
299
|
+
return fallback;
|
|
300
|
+
}
|
|
301
|
+
return typeof value === "bigint" ? value : BigInt(Math.floor(value));
|
|
302
|
+
}
|
|
303
|
+
function withAutoSubjectDidHash(schema, data) {
|
|
304
|
+
const fields = normalizeSchema(schema);
|
|
305
|
+
const hasSubjectDidHash = fields.some((field) => field.name === "subjectDidHash");
|
|
306
|
+
if (!hasSubjectDidHash) {
|
|
307
|
+
return { ...data };
|
|
308
|
+
}
|
|
309
|
+
const subject = data.subject;
|
|
310
|
+
if (typeof subject === "string" && subject.startsWith("did:")) {
|
|
311
|
+
return {
|
|
312
|
+
...data,
|
|
313
|
+
subjectDidHash: computeDidHash(subject)
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return { ...data };
|
|
317
|
+
}
|
|
318
|
+
function resolveRecipientAddress(data) {
|
|
319
|
+
const subject = data.subject;
|
|
320
|
+
if (typeof subject === "string" && subject.startsWith("did:")) {
|
|
321
|
+
return didToAddress(subject);
|
|
322
|
+
}
|
|
323
|
+
const subjectDidHash = data.subjectDidHash;
|
|
324
|
+
if (typeof subjectDidHash === "string" && /^0x[0-9a-fA-F]{64}$/.test(subjectDidHash)) {
|
|
325
|
+
return computeDidAddress(subjectDidHash);
|
|
326
|
+
}
|
|
327
|
+
const recipient = data.recipient;
|
|
328
|
+
if (typeof recipient === "string" && ethers.isAddress(recipient)) {
|
|
329
|
+
return ethers.getAddress(recipient);
|
|
330
|
+
}
|
|
331
|
+
return ethers.ZeroAddress;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/reputation/submit.ts
|
|
335
|
+
async function submitAttestation(params) {
|
|
336
|
+
if (!params || typeof params !== "object") {
|
|
337
|
+
throw new OmaTrustError("INVALID_INPUT", "params must be provided");
|
|
338
|
+
}
|
|
339
|
+
if (!params.signer) {
|
|
340
|
+
throw new OmaTrustError("INVALID_INPUT", "signer is required");
|
|
341
|
+
}
|
|
342
|
+
const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
|
|
343
|
+
const encodedData = encodeAttestationData(params.schema, dataWithHash);
|
|
344
|
+
const expiration = toBigIntOrDefault(
|
|
345
|
+
params.expirationTime ?? extractExpirationTime(dataWithHash),
|
|
346
|
+
0n
|
|
347
|
+
);
|
|
348
|
+
const recipient = resolveRecipientAddress(dataWithHash);
|
|
349
|
+
const eas = new easSdk.EAS(params.easContractAddress);
|
|
350
|
+
eas.connect(params.signer);
|
|
351
|
+
try {
|
|
352
|
+
const tx = await eas.attest({
|
|
353
|
+
schema: params.schemaUid,
|
|
354
|
+
data: {
|
|
355
|
+
recipient: recipient || ethers.ZeroAddress,
|
|
356
|
+
expirationTime: expiration,
|
|
357
|
+
revocable: params.revocable ?? true,
|
|
358
|
+
refUID: params.refUid ?? ZERO_UID,
|
|
359
|
+
data: encodedData,
|
|
360
|
+
value: toBigIntOrDefault(params.value, 0n)
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
const uid = await tx.wait();
|
|
364
|
+
const txAny = tx;
|
|
365
|
+
const txHash = txAny.tx?.hash ?? txAny.hash ?? txAny.transaction?.hash ?? ZERO_UID;
|
|
366
|
+
return {
|
|
367
|
+
uid,
|
|
368
|
+
txHash,
|
|
369
|
+
receipt: txAny.tx
|
|
370
|
+
};
|
|
371
|
+
} catch (err) {
|
|
372
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to submit attestation", { err });
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function buildDelegatedAttestationTypedData(params) {
|
|
376
|
+
const dataWithHash = withAutoSubjectDidHash(params.schema, params.data);
|
|
377
|
+
const encodedData = encodeAttestationData(params.schema, dataWithHash);
|
|
378
|
+
const recipient = resolveRecipientAddress(dataWithHash);
|
|
379
|
+
const expiration = toBigIntOrDefault(
|
|
380
|
+
params.expirationTime ?? extractExpirationTime(dataWithHash),
|
|
381
|
+
0n
|
|
382
|
+
);
|
|
383
|
+
return {
|
|
384
|
+
domain: {
|
|
385
|
+
name: "EAS",
|
|
386
|
+
version: "1.4.0",
|
|
387
|
+
chainId: params.chainId,
|
|
388
|
+
verifyingContract: params.easContractAddress
|
|
389
|
+
},
|
|
390
|
+
types: {
|
|
391
|
+
Attest: [
|
|
392
|
+
{ name: "attester", type: "address" },
|
|
393
|
+
{ name: "schema", type: "bytes32" },
|
|
394
|
+
{ name: "recipient", type: "address" },
|
|
395
|
+
{ name: "expirationTime", type: "uint64" },
|
|
396
|
+
{ name: "revocable", type: "bool" },
|
|
397
|
+
{ name: "refUID", type: "bytes32" },
|
|
398
|
+
{ name: "data", type: "bytes" },
|
|
399
|
+
{ name: "value", type: "uint256" },
|
|
400
|
+
{ name: "nonce", type: "uint256" },
|
|
401
|
+
{ name: "deadline", type: "uint64" }
|
|
402
|
+
]
|
|
403
|
+
},
|
|
404
|
+
message: {
|
|
405
|
+
attester: params.attester,
|
|
406
|
+
schema: params.schemaUid,
|
|
407
|
+
recipient,
|
|
408
|
+
expirationTime: expiration,
|
|
409
|
+
revocable: params.revocable ?? true,
|
|
410
|
+
refUID: params.refUid ?? ZERO_UID,
|
|
411
|
+
data: encodedData,
|
|
412
|
+
value: toBigIntOrDefault(params.value, 0n),
|
|
413
|
+
nonce: toBigIntOrDefault(params.nonce, 0n),
|
|
414
|
+
deadline: toBigIntOrDefault(params.deadline, BigInt(Math.floor(Date.now() / 1e3) + 600))
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function splitSignature(signature) {
|
|
419
|
+
try {
|
|
420
|
+
const parsed = ethers.Signature.from(signature);
|
|
421
|
+
return {
|
|
422
|
+
v: parsed.v,
|
|
423
|
+
r: parsed.r,
|
|
424
|
+
s: parsed.s
|
|
425
|
+
};
|
|
426
|
+
} catch (err) {
|
|
427
|
+
throw new OmaTrustError("INVALID_INPUT", "Invalid signature", { err });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async function prepareDelegatedAttestation(params) {
|
|
431
|
+
if (!params || typeof params !== "object") {
|
|
432
|
+
throw new OmaTrustError("INVALID_INPUT", "params are required");
|
|
433
|
+
}
|
|
434
|
+
const typedData = buildDelegatedAttestationTypedData(params);
|
|
435
|
+
return {
|
|
436
|
+
delegatedRequest: {
|
|
437
|
+
schema: params.schemaUid,
|
|
438
|
+
attester: params.attester,
|
|
439
|
+
easContractAddress: params.easContractAddress,
|
|
440
|
+
chainId: params.chainId,
|
|
441
|
+
...typedData.message
|
|
442
|
+
},
|
|
443
|
+
typedData
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
async function submitDelegatedAttestation(params) {
|
|
447
|
+
if (!params.relayUrl || typeof params.relayUrl !== "string") {
|
|
448
|
+
throw new OmaTrustError("INVALID_INPUT", "relayUrl is required");
|
|
449
|
+
}
|
|
450
|
+
let response;
|
|
451
|
+
try {
|
|
452
|
+
const body = JSON.stringify(
|
|
453
|
+
{
|
|
454
|
+
prepared: params.prepared,
|
|
455
|
+
signature: params.signature,
|
|
456
|
+
attester: params.attester
|
|
457
|
+
},
|
|
458
|
+
(_key, value) => typeof value === "bigint" ? value.toString() : value
|
|
459
|
+
);
|
|
460
|
+
response = await fetch(params.relayUrl, {
|
|
461
|
+
method: "POST",
|
|
462
|
+
headers: {
|
|
463
|
+
"Content-Type": "application/json"
|
|
464
|
+
},
|
|
465
|
+
body
|
|
466
|
+
});
|
|
467
|
+
} catch (err) {
|
|
468
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to submit delegated attestation", { err });
|
|
469
|
+
}
|
|
470
|
+
let payload;
|
|
471
|
+
try {
|
|
472
|
+
payload = await response.json();
|
|
473
|
+
} catch {
|
|
474
|
+
payload = {};
|
|
475
|
+
}
|
|
476
|
+
if (!response.ok) {
|
|
477
|
+
throw new OmaTrustError("NETWORK_ERROR", "Relay submission failed", {
|
|
478
|
+
status: response.status,
|
|
479
|
+
payload
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
const uid = payload.uid ?? ZERO_UID;
|
|
483
|
+
const txHash = payload.txHash;
|
|
484
|
+
const status = payload.status ?? "submitted";
|
|
485
|
+
return {
|
|
486
|
+
uid,
|
|
487
|
+
txHash,
|
|
488
|
+
status
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
var EAS_EVENT_ABI = [
|
|
492
|
+
"event Attested(address indexed recipient, address indexed attester, bytes32 uid, bytes32 indexed schemaUID)"
|
|
493
|
+
];
|
|
494
|
+
function parseAttestation(attestation, schema) {
|
|
495
|
+
const rawData = attestation.data ?? "0x";
|
|
496
|
+
const decoded = schema ? decodeAttestationData(schema, rawData) : {};
|
|
497
|
+
const toBigIntSafe = (value) => {
|
|
498
|
+
if (typeof value === "bigint") {
|
|
499
|
+
return value;
|
|
500
|
+
}
|
|
501
|
+
if (typeof value === "number") {
|
|
502
|
+
return BigInt(value);
|
|
503
|
+
}
|
|
504
|
+
if (typeof value === "string" && value.length > 0) {
|
|
505
|
+
return BigInt(value);
|
|
506
|
+
}
|
|
507
|
+
return 0n;
|
|
508
|
+
};
|
|
509
|
+
return {
|
|
510
|
+
uid: attestation.uid,
|
|
511
|
+
schema: attestation.schema,
|
|
512
|
+
attester: attestation.attester,
|
|
513
|
+
recipient: attestation.recipient,
|
|
514
|
+
revocable: Boolean(attestation.revocable),
|
|
515
|
+
revocationTime: toBigIntSafe(attestation.revocationTime),
|
|
516
|
+
expirationTime: toBigIntSafe(attestation.expirationTime),
|
|
517
|
+
time: toBigIntSafe(attestation.time),
|
|
518
|
+
refUID: attestation.refUID,
|
|
519
|
+
data: decoded,
|
|
520
|
+
raw: schema ? void 0 : rawData
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
async function getAttestation(params) {
|
|
524
|
+
try {
|
|
525
|
+
const eas = new easSdk.EAS(params.easContractAddress);
|
|
526
|
+
eas.connect(params.provider);
|
|
527
|
+
const attestation = await eas.getAttestation(params.uid);
|
|
528
|
+
if (!attestation || !attestation.uid || String(attestation.uid) === "0x".padEnd(66, "0")) {
|
|
529
|
+
throw new OmaTrustError("ATTESTATION_NOT_FOUND", "Attestation not found", { uid: params.uid });
|
|
530
|
+
}
|
|
531
|
+
return parseAttestation(attestation, params.schema);
|
|
532
|
+
} catch (err) {
|
|
533
|
+
if (err instanceof OmaTrustError) {
|
|
534
|
+
throw err;
|
|
535
|
+
}
|
|
536
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to read attestation", { err });
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async function getAttestationsForDid(params) {
|
|
540
|
+
const provider = params.provider;
|
|
541
|
+
const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
|
|
542
|
+
const toBlock = params.toBlock ?? await provider.getBlockNumber();
|
|
543
|
+
const fromBlock = params.fromBlock ?? Math.max(0, toBlock - 5e4);
|
|
544
|
+
const filter = contract.filters.Attested(didToAddress(params.did));
|
|
545
|
+
let events;
|
|
546
|
+
try {
|
|
547
|
+
events = await contract.queryFilter(filter, fromBlock, toBlock);
|
|
548
|
+
} catch (err) {
|
|
549
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to query attestation events", { err });
|
|
550
|
+
}
|
|
551
|
+
const eas = new easSdk.EAS(params.easContractAddress);
|
|
552
|
+
eas.connect(provider);
|
|
553
|
+
const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
|
|
554
|
+
const results = [];
|
|
555
|
+
for (const event of events) {
|
|
556
|
+
if (!("args" in event && Array.isArray(event.args))) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
const args = event.args;
|
|
560
|
+
const uid = args?.[2];
|
|
561
|
+
const schemaUid = args?.[3];
|
|
562
|
+
if (!uid || !schemaUid) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
const attestation = await eas.getAttestation(uid);
|
|
569
|
+
if (!attestation || !attestation.uid) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
results.push(parseAttestation(attestation));
|
|
573
|
+
}
|
|
574
|
+
results.sort((a, b) => Number(b.time - a.time));
|
|
575
|
+
return results;
|
|
576
|
+
}
|
|
577
|
+
async function listAttestations(params) {
|
|
578
|
+
const limit = params.limit ?? 20;
|
|
579
|
+
if (limit <= 0) {
|
|
580
|
+
return [];
|
|
581
|
+
}
|
|
582
|
+
const results = await getAttestationsForDid(params);
|
|
583
|
+
return results.slice(0, limit);
|
|
584
|
+
}
|
|
585
|
+
async function getLatestAttestations(params) {
|
|
586
|
+
const provider = params.provider;
|
|
587
|
+
const contract = new ethers.Contract(params.easContractAddress, EAS_EVENT_ABI, provider);
|
|
588
|
+
const currentBlock = await provider.getBlockNumber();
|
|
589
|
+
const fromBlock = params.fromBlock ?? Math.max(0, currentBlock - 5e4);
|
|
590
|
+
const events = await contract.queryFilter(contract.filters.Attested(), fromBlock, currentBlock);
|
|
591
|
+
const eas = new easSdk.EAS(params.easContractAddress);
|
|
592
|
+
eas.connect(provider);
|
|
593
|
+
const schemaFilter = params.schemas?.map((schema) => schema.toLowerCase());
|
|
594
|
+
const results = [];
|
|
595
|
+
for (const event of events) {
|
|
596
|
+
if (!("args" in event && Array.isArray(event.args))) {
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
const args = event.args;
|
|
600
|
+
const uid = args?.[2];
|
|
601
|
+
const schemaUid = args?.[3];
|
|
602
|
+
if (!uid || !schemaUid) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (schemaFilter && !schemaFilter.includes(schemaUid.toLowerCase())) {
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const attestation = await eas.getAttestation(uid);
|
|
609
|
+
if (!attestation || !attestation.uid) {
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
results.push(parseAttestation(attestation));
|
|
613
|
+
}
|
|
614
|
+
results.sort((a, b) => Number(b.time - a.time));
|
|
615
|
+
return results.slice(0, params.limit ?? 20);
|
|
616
|
+
}
|
|
617
|
+
function deduplicateReviews(attestations) {
|
|
618
|
+
const seen = /* @__PURE__ */ new Map();
|
|
619
|
+
for (const attestation of attestations) {
|
|
620
|
+
const subject = String(attestation.data.subject ?? "");
|
|
621
|
+
const version = String(attestation.data.version ?? "");
|
|
622
|
+
const major = getMajorVersion(version);
|
|
623
|
+
const key = `${attestation.attester.toLowerCase()}|${subject}|${major}`;
|
|
624
|
+
if (!seen.has(key)) {
|
|
625
|
+
seen.set(key, attestation);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
const current = seen.get(key);
|
|
629
|
+
if (attestation.time > current.time) {
|
|
630
|
+
seen.set(key, attestation);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return [...seen.values()];
|
|
634
|
+
}
|
|
635
|
+
function calculateAverageUserReviewRating(attestations) {
|
|
636
|
+
const deduped = deduplicateReviews(attestations);
|
|
637
|
+
const ratings = deduped.map((attestation) => attestation.data.ratingValue).filter((value) => typeof value === "number" || typeof value === "bigint").map((value) => Number(value));
|
|
638
|
+
if (ratings.length === 0) {
|
|
639
|
+
return 0;
|
|
640
|
+
}
|
|
641
|
+
const total = ratings.reduce((sum, value) => sum + value, 0);
|
|
642
|
+
return total / ratings.length;
|
|
643
|
+
}
|
|
644
|
+
function getMajorVersion(version) {
|
|
645
|
+
const match = version.match(/^(\d+)/);
|
|
646
|
+
if (!match) {
|
|
647
|
+
throw new OmaTrustError("INVALID_INPUT", "Invalid semantic version", { version });
|
|
648
|
+
}
|
|
649
|
+
return Number(match[1]);
|
|
650
|
+
}
|
|
651
|
+
var CHAIN_CONFIGS = {
|
|
652
|
+
1: {
|
|
653
|
+
decimals: 18,
|
|
654
|
+
nativeSymbol: "ETH",
|
|
655
|
+
explorer: "https://etherscan.io",
|
|
656
|
+
base: {
|
|
657
|
+
"shared-control": 100000000000000n,
|
|
658
|
+
"commercial-tx": 1000000000000n
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
10: {
|
|
662
|
+
decimals: 18,
|
|
663
|
+
nativeSymbol: "ETH",
|
|
664
|
+
explorer: "https://optimistic.etherscan.io",
|
|
665
|
+
base: {
|
|
666
|
+
"shared-control": 100000000000000n,
|
|
667
|
+
"commercial-tx": 1000000000000n
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
137: {
|
|
671
|
+
decimals: 18,
|
|
672
|
+
nativeSymbol: "POL",
|
|
673
|
+
explorer: "https://polygonscan.com",
|
|
674
|
+
base: {
|
|
675
|
+
"shared-control": 100000000000000n,
|
|
676
|
+
"commercial-tx": 1000000000000n
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
8453: {
|
|
680
|
+
decimals: 18,
|
|
681
|
+
nativeSymbol: "ETH",
|
|
682
|
+
explorer: "https://basescan.org",
|
|
683
|
+
base: {
|
|
684
|
+
"shared-control": 100000000000000n,
|
|
685
|
+
"commercial-tx": 1000000000000n
|
|
686
|
+
}
|
|
687
|
+
},
|
|
688
|
+
42161: {
|
|
689
|
+
decimals: 18,
|
|
690
|
+
nativeSymbol: "ETH",
|
|
691
|
+
explorer: "https://arbiscan.io",
|
|
692
|
+
base: {
|
|
693
|
+
"shared-control": 100000000000000n,
|
|
694
|
+
"commercial-tx": 1000000000000n
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
11155111: {
|
|
698
|
+
decimals: 18,
|
|
699
|
+
nativeSymbol: "ETH",
|
|
700
|
+
explorer: "https://sepolia.etherscan.io",
|
|
701
|
+
base: {
|
|
702
|
+
"shared-control": 100000000000000n,
|
|
703
|
+
"commercial-tx": 1000000000000n
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
6623: {
|
|
707
|
+
decimals: 18,
|
|
708
|
+
nativeSymbol: "OMA",
|
|
709
|
+
explorer: "https://explorer.chain.oma3.org",
|
|
710
|
+
base: {
|
|
711
|
+
"shared-control": 10000000000000000n,
|
|
712
|
+
"commercial-tx": 100000000000000n
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
66238: {
|
|
716
|
+
decimals: 18,
|
|
717
|
+
nativeSymbol: "OMA",
|
|
718
|
+
explorer: "https://explorer.testnet.chain.oma3.org",
|
|
719
|
+
base: {
|
|
720
|
+
"shared-control": 10000000000000000n,
|
|
721
|
+
"commercial-tx": 100000000000000n
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
var AMOUNT_DOMAIN = "OMATrust:Amount:v1";
|
|
726
|
+
function getConfig(chainId) {
|
|
727
|
+
const config = CHAIN_CONFIGS[chainId];
|
|
728
|
+
if (!config) {
|
|
729
|
+
throw new OmaTrustError("UNSUPPORTED_CHAIN", "Chain is not supported", {
|
|
730
|
+
chainId,
|
|
731
|
+
supported: getSupportedChainIds()
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
return config;
|
|
735
|
+
}
|
|
736
|
+
function getSupportedChainIds() {
|
|
737
|
+
return Object.keys(CHAIN_CONFIGS).map((id) => Number(id));
|
|
738
|
+
}
|
|
739
|
+
function isChainSupported(chainId) {
|
|
740
|
+
return chainId in CHAIN_CONFIGS;
|
|
741
|
+
}
|
|
742
|
+
function getChainConstants(chainId, purpose) {
|
|
743
|
+
const config = getConfig(chainId);
|
|
744
|
+
const base = config.base[purpose];
|
|
745
|
+
const range = base / 10n;
|
|
746
|
+
return {
|
|
747
|
+
base,
|
|
748
|
+
range,
|
|
749
|
+
decimals: config.decimals,
|
|
750
|
+
nativeSymbol: config.nativeSymbol
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function constructSeed(subjectDidHash, counterpartyDidHash, purpose) {
|
|
754
|
+
const seed = {
|
|
755
|
+
domain: AMOUNT_DOMAIN,
|
|
756
|
+
subjectDidHash,
|
|
757
|
+
counterpartyIdHash: counterpartyDidHash,
|
|
758
|
+
proofPurpose: purpose
|
|
759
|
+
};
|
|
760
|
+
const canonical = canonicalize__default.default(seed);
|
|
761
|
+
if (!canonical) {
|
|
762
|
+
throw new OmaTrustError("INVALID_INPUT", "Failed to canonicalize seed");
|
|
763
|
+
}
|
|
764
|
+
return ethers.toUtf8Bytes(canonical);
|
|
765
|
+
}
|
|
766
|
+
function hashSeed(seedBytes, chainId) {
|
|
767
|
+
if (!isChainSupported(chainId)) {
|
|
768
|
+
throw new OmaTrustError("UNSUPPORTED_CHAIN", "Chain is not supported", { chainId });
|
|
769
|
+
}
|
|
770
|
+
if (chainId === 0) {
|
|
771
|
+
return ethers.sha256(seedBytes);
|
|
772
|
+
}
|
|
773
|
+
return ethers.keccak256(seedBytes);
|
|
774
|
+
}
|
|
775
|
+
function calculateTransferAmount(subject, counterparty, chainId, purpose) {
|
|
776
|
+
const { base, range } = getChainConstants(chainId, purpose);
|
|
777
|
+
const subjectDidHash = computeDidHash(subject);
|
|
778
|
+
const counterpartyDidHash = computeDidHash(counterparty);
|
|
779
|
+
const seed = constructSeed(subjectDidHash, counterpartyDidHash, purpose);
|
|
780
|
+
const hashed = hashSeed(seed, chainId);
|
|
781
|
+
const offset = BigInt(hashed) % range;
|
|
782
|
+
return base + offset;
|
|
783
|
+
}
|
|
784
|
+
function calculateTransferAmountFromAddresses(subjectAddress, counterpartyAddress, chainId, purpose) {
|
|
785
|
+
return calculateTransferAmount(
|
|
786
|
+
buildEvmDidPkh(chainId, subjectAddress),
|
|
787
|
+
buildEvmDidPkh(chainId, counterpartyAddress),
|
|
788
|
+
chainId,
|
|
789
|
+
purpose
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
function createTxEncodedValueProof(chainId, txHash, purpose) {
|
|
793
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(txHash)) {
|
|
794
|
+
throw new OmaTrustError("INVALID_INPUT", "txHash must be a 32-byte hex string", { txHash });
|
|
795
|
+
}
|
|
796
|
+
return {
|
|
797
|
+
proofType: "tx-encoded-value",
|
|
798
|
+
proofPurpose: purpose,
|
|
799
|
+
proofObject: {
|
|
800
|
+
chainId: `eip155:${chainId}`,
|
|
801
|
+
txHash
|
|
802
|
+
},
|
|
803
|
+
version: 1,
|
|
804
|
+
issuedAt: Math.floor(Date.now() / 1e3)
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
function formatTransferAmount(amount, chainId) {
|
|
808
|
+
const config = getConfig(chainId);
|
|
809
|
+
const normalized = typeof amount === "number" ? BigInt(Math.floor(amount)) : amount;
|
|
810
|
+
return `${ethers.formatUnits(normalized, config.decimals)} ${config.nativeSymbol}`;
|
|
811
|
+
}
|
|
812
|
+
function getExplorerTxUrl(chainId, txHash) {
|
|
813
|
+
return `${getConfig(chainId).explorer}/tx/${txHash}`;
|
|
814
|
+
}
|
|
815
|
+
function getExplorerAddressUrl(chainId, address) {
|
|
816
|
+
return `${getConfig(chainId).explorer}/address/${address}`;
|
|
817
|
+
}
|
|
818
|
+
async function fetchDidDocument(domain) {
|
|
819
|
+
const normalized = domain.toLowerCase().replace(/\.$/, "");
|
|
820
|
+
const url = `https://${normalized}/.well-known/did.json`;
|
|
821
|
+
let response;
|
|
822
|
+
try {
|
|
823
|
+
response = await fetch(url, { headers: { Accept: "application/json" } });
|
|
824
|
+
} catch (err) {
|
|
825
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to fetch DID document", { domain, err });
|
|
826
|
+
}
|
|
827
|
+
if (!response.ok) {
|
|
828
|
+
throw new OmaTrustError("NETWORK_ERROR", "DID document fetch failed", {
|
|
829
|
+
domain,
|
|
830
|
+
status: response.status
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
const body = await response.json();
|
|
834
|
+
return body;
|
|
835
|
+
}
|
|
836
|
+
function extractAddressesFromDidDocument(didDocument) {
|
|
837
|
+
const methods = didDocument.verificationMethod;
|
|
838
|
+
if (!Array.isArray(methods)) {
|
|
839
|
+
return [];
|
|
840
|
+
}
|
|
841
|
+
const addresses = /* @__PURE__ */ new Set();
|
|
842
|
+
for (const method of methods) {
|
|
843
|
+
const blockchainAccountId = method.blockchainAccountId;
|
|
844
|
+
if (typeof blockchainAccountId === "string") {
|
|
845
|
+
try {
|
|
846
|
+
addresses.add(ethers.getAddress(extractAddressFromDid(blockchainAccountId)));
|
|
847
|
+
} catch {
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const publicKeyHex = method.publicKeyHex;
|
|
851
|
+
if (typeof publicKeyHex === "string") {
|
|
852
|
+
const prefixed = publicKeyHex.startsWith("0x") ? publicKeyHex : `0x${publicKeyHex}`;
|
|
853
|
+
if (ethers.isAddress(prefixed)) {
|
|
854
|
+
addresses.add(ethers.getAddress(prefixed));
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return [...addresses];
|
|
859
|
+
}
|
|
860
|
+
function verifyDidDocumentControllerDid(didDocument, expectedControllerDid) {
|
|
861
|
+
let expectedAddress;
|
|
862
|
+
try {
|
|
863
|
+
expectedAddress = ethers.getAddress(extractAddressFromDid(expectedControllerDid));
|
|
864
|
+
} catch {
|
|
865
|
+
return { valid: false, reason: "Expected controller DID does not resolve to an EVM address" };
|
|
866
|
+
}
|
|
867
|
+
const addresses = extractAddressesFromDidDocument(didDocument);
|
|
868
|
+
if (addresses.some((address) => address.toLowerCase() === expectedAddress.toLowerCase())) {
|
|
869
|
+
return { valid: true };
|
|
870
|
+
}
|
|
871
|
+
return {
|
|
872
|
+
valid: false,
|
|
873
|
+
reason: `No matching address found in DID document (expected ${expectedAddress})`
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function buildEip712Domain(name, version, chainId, verifyingContract) {
|
|
877
|
+
return { name, version, chainId, verifyingContract };
|
|
878
|
+
}
|
|
879
|
+
function getOmaTrustProofEip712Types() {
|
|
880
|
+
return {
|
|
881
|
+
primaryType: "OmaTrustProof",
|
|
882
|
+
types: {
|
|
883
|
+
OmaTrustProof: [
|
|
884
|
+
{ name: "signer", type: "address" },
|
|
885
|
+
{ name: "authorizedEntity", type: "string" },
|
|
886
|
+
{ name: "signingPurpose", type: "string" },
|
|
887
|
+
{ name: "creationTimestamp", type: "uint256" },
|
|
888
|
+
{ name: "expirationTimestamp", type: "uint256" },
|
|
889
|
+
{ name: "randomValue", type: "bytes32" },
|
|
890
|
+
{ name: "statement", type: "string" }
|
|
891
|
+
]
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
function verifyEip712Signature(typedData, signature) {
|
|
896
|
+
try {
|
|
897
|
+
const signer = ethers.verifyTypedData(
|
|
898
|
+
typedData.domain,
|
|
899
|
+
typedData.types,
|
|
900
|
+
typedData.message,
|
|
901
|
+
signature
|
|
902
|
+
);
|
|
903
|
+
return { valid: true, signer };
|
|
904
|
+
} catch (err) {
|
|
905
|
+
throw new OmaTrustError("INVALID_INPUT", "Failed to verify EIP-712 signature", { err });
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
function parseDnsTxtRecord(record) {
|
|
909
|
+
if (!record || typeof record !== "string") {
|
|
910
|
+
throw new OmaTrustError("INVALID_INPUT", "record must be a non-empty string", { record });
|
|
911
|
+
}
|
|
912
|
+
const entries = record.split(/[;\s]+/).map((entry) => entry.trim()).filter(Boolean);
|
|
913
|
+
const parsed = {};
|
|
914
|
+
for (const entry of entries) {
|
|
915
|
+
const [key, ...valueParts] = entry.split("=");
|
|
916
|
+
if (!key) {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
const value = valueParts.join("=");
|
|
920
|
+
if (!value) {
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
parsed[key.trim()] = value.trim();
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
version: parsed.v,
|
|
927
|
+
controller: parsed.controller,
|
|
928
|
+
...parsed
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
function buildDnsTxtRecord(controllerDid) {
|
|
932
|
+
const normalized = normalizeDid(controllerDid);
|
|
933
|
+
return `v=1;controller=${normalized}`;
|
|
934
|
+
}
|
|
935
|
+
async function verifyDnsTxtControllerDid(domain, expectedControllerDid) {
|
|
936
|
+
if (!domain || typeof domain !== "string") {
|
|
937
|
+
throw new OmaTrustError("INVALID_INPUT", "domain must be a non-empty string", { domain });
|
|
938
|
+
}
|
|
939
|
+
const expected = normalizeDid(expectedControllerDid);
|
|
940
|
+
const host = `_omatrust.${domain.toLowerCase().replace(/\.$/, "")}`;
|
|
941
|
+
let records;
|
|
942
|
+
try {
|
|
943
|
+
records = await promises.resolveTxt(host);
|
|
944
|
+
} catch (err) {
|
|
945
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to resolve DNS TXT records", { domain, err });
|
|
946
|
+
}
|
|
947
|
+
for (const recordParts of records) {
|
|
948
|
+
const record = recordParts.join("");
|
|
949
|
+
const parsed = parseDnsTxtRecord(record);
|
|
950
|
+
if (parsed.version === "1" && parsed.controller && normalizeDid(parsed.controller) === expected) {
|
|
951
|
+
return { valid: true, record };
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return { valid: false, reason: "No TXT record matched expected controller DID" };
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// src/reputation/verify.ts
|
|
958
|
+
function parseChainId(input) {
|
|
959
|
+
if (typeof input === "number") {
|
|
960
|
+
return input;
|
|
961
|
+
}
|
|
962
|
+
if (typeof input === "string") {
|
|
963
|
+
if (input.includes(":")) {
|
|
964
|
+
const [, chainId] = input.split(":");
|
|
965
|
+
return Number(chainId);
|
|
966
|
+
}
|
|
967
|
+
return Number(input);
|
|
968
|
+
}
|
|
969
|
+
return NaN;
|
|
970
|
+
}
|
|
971
|
+
function parseProofs(attestation) {
|
|
972
|
+
const rawProofs = attestation.data.proofs;
|
|
973
|
+
if (!Array.isArray(rawProofs)) {
|
|
974
|
+
return [];
|
|
975
|
+
}
|
|
976
|
+
return rawProofs.map((entry) => {
|
|
977
|
+
if (typeof entry === "string") {
|
|
978
|
+
try {
|
|
979
|
+
return JSON.parse(entry);
|
|
980
|
+
} catch {
|
|
981
|
+
return null;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (entry && typeof entry === "object") {
|
|
985
|
+
return entry;
|
|
986
|
+
}
|
|
987
|
+
return null;
|
|
988
|
+
}).filter((proof) => Boolean(proof?.proofType));
|
|
989
|
+
}
|
|
990
|
+
function getProofPurpose(proof) {
|
|
991
|
+
if (proof.proofPurpose === "commercial-tx") {
|
|
992
|
+
return "commercial-tx";
|
|
993
|
+
}
|
|
994
|
+
return "shared-control";
|
|
995
|
+
}
|
|
996
|
+
function decodeJwtPayload(compactJws) {
|
|
997
|
+
const parts = compactJws.split(".");
|
|
998
|
+
if (parts.length !== 3) {
|
|
999
|
+
throw new OmaTrustError("PROOF_VERIFICATION_FAILED", "Invalid compact JWS format");
|
|
1000
|
+
}
|
|
1001
|
+
const payloadB64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
1002
|
+
const padded = payloadB64.padEnd(payloadB64.length + (4 - payloadB64.length % 4) % 4, "=");
|
|
1003
|
+
let json;
|
|
1004
|
+
if (typeof atob === "function") {
|
|
1005
|
+
json = decodeURIComponent(
|
|
1006
|
+
Array.from(atob(padded)).map((char) => `%${char.charCodeAt(0).toString(16).padStart(2, "0")}`).join("")
|
|
1007
|
+
);
|
|
1008
|
+
} else {
|
|
1009
|
+
json = Buffer.from(padded, "base64").toString("utf8");
|
|
1010
|
+
}
|
|
1011
|
+
return JSON.parse(json);
|
|
1012
|
+
}
|
|
1013
|
+
async function verifyProof(params) {
|
|
1014
|
+
const { proof, provider, expectedSubject, expectedController } = params;
|
|
1015
|
+
try {
|
|
1016
|
+
switch (proof.proofType) {
|
|
1017
|
+
case "tx-encoded-value": {
|
|
1018
|
+
if (!provider) {
|
|
1019
|
+
return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
|
|
1020
|
+
}
|
|
1021
|
+
if (!expectedSubject || !expectedController) {
|
|
1022
|
+
return {
|
|
1023
|
+
valid: false,
|
|
1024
|
+
proofType: proof.proofType,
|
|
1025
|
+
reason: "expectedSubject and expectedController are required"
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
const proofObject = proof.proofObject;
|
|
1029
|
+
const chainId = parseChainId(proofObject.chainId);
|
|
1030
|
+
const tx = await provider.getTransaction(
|
|
1031
|
+
proofObject.txHash
|
|
1032
|
+
);
|
|
1033
|
+
if (!tx) {
|
|
1034
|
+
return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
|
|
1035
|
+
}
|
|
1036
|
+
const expectedAmount = calculateTransferAmount(
|
|
1037
|
+
expectedSubject,
|
|
1038
|
+
expectedController,
|
|
1039
|
+
chainId,
|
|
1040
|
+
getProofPurpose(proof)
|
|
1041
|
+
);
|
|
1042
|
+
const subjectAddress = ethers.getAddress(extractAddressFromDid(expectedSubject));
|
|
1043
|
+
const controllerAddress = ethers.getAddress(extractAddressFromDid(expectedController));
|
|
1044
|
+
if (tx.from && ethers.getAddress(tx.from) !== subjectAddress) {
|
|
1045
|
+
return { valid: false, proofType: proof.proofType, reason: "Transaction sender mismatch" };
|
|
1046
|
+
}
|
|
1047
|
+
if (tx.to && ethers.getAddress(tx.to) !== controllerAddress) {
|
|
1048
|
+
return { valid: false, proofType: proof.proofType, reason: "Transaction recipient mismatch" };
|
|
1049
|
+
}
|
|
1050
|
+
if (BigInt(tx.value) !== expectedAmount) {
|
|
1051
|
+
return { valid: false, proofType: proof.proofType, reason: "Transaction amount mismatch" };
|
|
1052
|
+
}
|
|
1053
|
+
return { valid: true, proofType: proof.proofType };
|
|
1054
|
+
}
|
|
1055
|
+
case "tx-interaction": {
|
|
1056
|
+
if (!provider) {
|
|
1057
|
+
return { valid: false, proofType: proof.proofType, reason: "Provider is required" };
|
|
1058
|
+
}
|
|
1059
|
+
const proofObject = proof.proofObject;
|
|
1060
|
+
const tx = await provider.getTransaction(
|
|
1061
|
+
proofObject.txHash
|
|
1062
|
+
);
|
|
1063
|
+
if (!tx) {
|
|
1064
|
+
return { valid: false, proofType: proof.proofType, reason: "Transaction not found" };
|
|
1065
|
+
}
|
|
1066
|
+
if (!tx.to) {
|
|
1067
|
+
return { valid: false, proofType: proof.proofType, reason: "Transaction target missing" };
|
|
1068
|
+
}
|
|
1069
|
+
return { valid: true, proofType: proof.proofType };
|
|
1070
|
+
}
|
|
1071
|
+
case "pop-eip712": {
|
|
1072
|
+
const object = proof.proofObject;
|
|
1073
|
+
const typedData = {
|
|
1074
|
+
domain: object.domain,
|
|
1075
|
+
types: {
|
|
1076
|
+
OmaTrustProof: [
|
|
1077
|
+
{ name: "signer", type: "address" },
|
|
1078
|
+
{ name: "authorizedEntity", type: "string" },
|
|
1079
|
+
{ name: "signingPurpose", type: "string" },
|
|
1080
|
+
{ name: "creationTimestamp", type: "uint256" },
|
|
1081
|
+
{ name: "expirationTimestamp", type: "uint256" },
|
|
1082
|
+
{ name: "randomValue", type: "bytes32" },
|
|
1083
|
+
{ name: "statement", type: "string" }
|
|
1084
|
+
]
|
|
1085
|
+
},
|
|
1086
|
+
message: object.message
|
|
1087
|
+
};
|
|
1088
|
+
const verification = verifyEip712Signature(typedData, object.signature);
|
|
1089
|
+
if (!verification.valid || !verification.signer) {
|
|
1090
|
+
return { valid: false, proofType: proof.proofType, reason: "Invalid EIP-712 signature" };
|
|
1091
|
+
}
|
|
1092
|
+
if (typeof object.message.signer === "string") {
|
|
1093
|
+
const expected = ethers.getAddress(object.message.signer);
|
|
1094
|
+
if (ethers.getAddress(verification.signer) !== expected) {
|
|
1095
|
+
return { valid: false, proofType: proof.proofType, reason: "Recovered signer mismatch" };
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
return { valid: true, proofType: proof.proofType };
|
|
1099
|
+
}
|
|
1100
|
+
case "pop-jws": {
|
|
1101
|
+
if (typeof proof.proofObject !== "string") {
|
|
1102
|
+
return { valid: false, proofType: proof.proofType, reason: "Invalid JWS proof payload" };
|
|
1103
|
+
}
|
|
1104
|
+
const payload = decodeJwtPayload(proof.proofObject);
|
|
1105
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1106
|
+
const exp = payload.exp;
|
|
1107
|
+
if (typeof exp === "number" && exp < now) {
|
|
1108
|
+
return { valid: false, proofType: proof.proofType, reason: "JWS proof expired" };
|
|
1109
|
+
}
|
|
1110
|
+
return { valid: true, proofType: proof.proofType };
|
|
1111
|
+
}
|
|
1112
|
+
case "x402-receipt":
|
|
1113
|
+
case "x402-offer": {
|
|
1114
|
+
if (!proof.proofObject || typeof proof.proofObject !== "object") {
|
|
1115
|
+
return { valid: false, proofType: proof.proofType, reason: "Invalid x402 proof object" };
|
|
1116
|
+
}
|
|
1117
|
+
return { valid: true, proofType: proof.proofType };
|
|
1118
|
+
}
|
|
1119
|
+
case "evidence-pointer": {
|
|
1120
|
+
const object = proof.proofObject;
|
|
1121
|
+
if (!object.url) {
|
|
1122
|
+
return { valid: false, proofType: proof.proofType, reason: "Missing evidence URL" };
|
|
1123
|
+
}
|
|
1124
|
+
const response = await fetch(object.url);
|
|
1125
|
+
if (!response.ok) {
|
|
1126
|
+
return { valid: false, proofType: proof.proofType, reason: `Evidence fetch failed (${response.status})` };
|
|
1127
|
+
}
|
|
1128
|
+
if (object.url.endsWith("/.well-known/did.json") && expectedController) {
|
|
1129
|
+
const didDoc = await response.json();
|
|
1130
|
+
const didCheck = verifyDidDocumentControllerDid(didDoc, expectedController);
|
|
1131
|
+
return {
|
|
1132
|
+
valid: didCheck.valid,
|
|
1133
|
+
proofType: proof.proofType,
|
|
1134
|
+
reason: didCheck.reason
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
const body = await response.text();
|
|
1138
|
+
if (expectedController && !body.includes(expectedController)) {
|
|
1139
|
+
try {
|
|
1140
|
+
const parsed = parseDnsTxtRecord(body);
|
|
1141
|
+
if (parsed.controller !== expectedController) {
|
|
1142
|
+
return {
|
|
1143
|
+
valid: false,
|
|
1144
|
+
proofType: proof.proofType,
|
|
1145
|
+
reason: "Evidence does not include expected controller DID"
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
} catch {
|
|
1149
|
+
return {
|
|
1150
|
+
valid: false,
|
|
1151
|
+
proofType: proof.proofType,
|
|
1152
|
+
reason: "Evidence does not include expected controller DID"
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
return { valid: true, proofType: proof.proofType };
|
|
1157
|
+
}
|
|
1158
|
+
default:
|
|
1159
|
+
return { valid: false, proofType: proof.proofType, reason: "Unsupported proof type" };
|
|
1160
|
+
}
|
|
1161
|
+
} catch (err) {
|
|
1162
|
+
throw new OmaTrustError("PROOF_VERIFICATION_FAILED", "Proof verification failed", {
|
|
1163
|
+
proofType: proof.proofType,
|
|
1164
|
+
err
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
async function verifyAttestation(params) {
|
|
1169
|
+
const proofs = parseProofs(params.attestation);
|
|
1170
|
+
const checks = {};
|
|
1171
|
+
const reasons = [];
|
|
1172
|
+
const now = BigInt(Math.floor(Date.now() / 1e3));
|
|
1173
|
+
if (params.attestation.revocationTime > 0n) {
|
|
1174
|
+
checks.revocation = false;
|
|
1175
|
+
reasons.push("attestation revoked");
|
|
1176
|
+
} else {
|
|
1177
|
+
checks.revocation = true;
|
|
1178
|
+
}
|
|
1179
|
+
if (params.attestation.expirationTime > 0n && params.attestation.expirationTime < now) {
|
|
1180
|
+
checks.expiration = false;
|
|
1181
|
+
reasons.push("attestation expired");
|
|
1182
|
+
} else {
|
|
1183
|
+
checks.expiration = true;
|
|
1184
|
+
}
|
|
1185
|
+
if (proofs.length === 0) {
|
|
1186
|
+
checks.proofs = false;
|
|
1187
|
+
reasons.push("no proofs provided");
|
|
1188
|
+
}
|
|
1189
|
+
const selectedProofTypes = params.checks ?? proofs.map((proof) => proof.proofType);
|
|
1190
|
+
for (const proof of proofs) {
|
|
1191
|
+
if (!selectedProofTypes.includes(proof.proofType)) {
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
const result = await verifyProof({
|
|
1195
|
+
proof,
|
|
1196
|
+
provider: params.provider,
|
|
1197
|
+
expectedSubject: params.context?.subject,
|
|
1198
|
+
expectedController: params.context?.controller
|
|
1199
|
+
});
|
|
1200
|
+
checks[proof.proofType] = result.valid;
|
|
1201
|
+
if (!result.valid) {
|
|
1202
|
+
reasons.push(result.reason ?? `${proof.proofType} verification failed`);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
return {
|
|
1206
|
+
valid: reasons.length === 0,
|
|
1207
|
+
checks,
|
|
1208
|
+
reasons
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// src/reputation/schema.ts
|
|
1213
|
+
async function verifySchemaExists(schemaRegistry, schemaUid) {
|
|
1214
|
+
const registry = schemaRegistry;
|
|
1215
|
+
try {
|
|
1216
|
+
const schema = await registry.getSchema({ uid: schemaUid });
|
|
1217
|
+
return Boolean(schema && schema.uid && schema.uid !== "0x".padEnd(66, "0"));
|
|
1218
|
+
} catch {
|
|
1219
|
+
return false;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
async function getSchemaDetails(schemaRegistry, schemaUid) {
|
|
1223
|
+
const registry = schemaRegistry;
|
|
1224
|
+
try {
|
|
1225
|
+
const details = await registry.getSchema({ uid: schemaUid });
|
|
1226
|
+
if (!details || !details.uid || details.uid === "0x".padEnd(66, "0")) {
|
|
1227
|
+
throw new OmaTrustError("SCHEMA_NOT_FOUND", "Schema was not found", { schemaUid });
|
|
1228
|
+
}
|
|
1229
|
+
return {
|
|
1230
|
+
uid: formatSchemaUid(details.uid),
|
|
1231
|
+
schema: details.schema,
|
|
1232
|
+
resolver: details.resolver,
|
|
1233
|
+
revocable: Boolean(details.revocable)
|
|
1234
|
+
};
|
|
1235
|
+
} catch (err) {
|
|
1236
|
+
if (err instanceof OmaTrustError) {
|
|
1237
|
+
throw err;
|
|
1238
|
+
}
|
|
1239
|
+
throw new OmaTrustError("NETWORK_ERROR", "Failed to read schema details", { schemaUid, err });
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
function formatSchemaUid(schemaUid) {
|
|
1243
|
+
if (!schemaUid) {
|
|
1244
|
+
throw new OmaTrustError("INVALID_INPUT", "schemaUid is required");
|
|
1245
|
+
}
|
|
1246
|
+
const value = schemaUid.startsWith("0x") ? schemaUid.toLowerCase() : `0x${schemaUid.toLowerCase()}`;
|
|
1247
|
+
if (!/^0x[0-9a-f]{64}$/.test(value)) {
|
|
1248
|
+
throw new OmaTrustError("INVALID_INPUT", "schemaUid must be a 32-byte hex string", { schemaUid });
|
|
1249
|
+
}
|
|
1250
|
+
return value;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/reputation/witness.ts
|
|
1254
|
+
async function callMethod(params, method) {
|
|
1255
|
+
let response;
|
|
1256
|
+
try {
|
|
1257
|
+
response = await fetch(params.gatewayUrl, {
|
|
1258
|
+
method: "POST",
|
|
1259
|
+
headers: { "Content-Type": "application/json" },
|
|
1260
|
+
body: JSON.stringify({
|
|
1261
|
+
attestationUid: params.attestationUid,
|
|
1262
|
+
chainId: params.chainId,
|
|
1263
|
+
easContract: params.easContract,
|
|
1264
|
+
schemaUid: params.schemaUid,
|
|
1265
|
+
subject: params.subject,
|
|
1266
|
+
controller: params.controller,
|
|
1267
|
+
method
|
|
1268
|
+
}),
|
|
1269
|
+
signal: AbortSignal.timeout(params.timeoutMs ?? 15e3)
|
|
1270
|
+
});
|
|
1271
|
+
} catch (err) {
|
|
1272
|
+
throw new OmaTrustError("NETWORK_ERROR", "Controller witness request failed", { method, err });
|
|
1273
|
+
}
|
|
1274
|
+
const details = await response.json().catch(() => void 0);
|
|
1275
|
+
if (!response.ok) {
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
return {
|
|
1279
|
+
ok: true,
|
|
1280
|
+
method,
|
|
1281
|
+
details
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
async function callControllerWitness(params) {
|
|
1285
|
+
const dnsResult = await callMethod(params, "dns-txt").catch(() => null);
|
|
1286
|
+
if (dnsResult) {
|
|
1287
|
+
return dnsResult;
|
|
1288
|
+
}
|
|
1289
|
+
const didJsonResult = await callMethod(params, "did-json").catch(() => null);
|
|
1290
|
+
if (didJsonResult) {
|
|
1291
|
+
return didJsonResult;
|
|
1292
|
+
}
|
|
1293
|
+
return {
|
|
1294
|
+
ok: false,
|
|
1295
|
+
method: "did-json"
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// src/reputation/proof/tx-interaction.ts
|
|
1300
|
+
function createTxInteractionProof(chainId, txHash) {
|
|
1301
|
+
if (!/^0x[0-9a-fA-F]{64}$/.test(txHash)) {
|
|
1302
|
+
throw new OmaTrustError("INVALID_INPUT", "txHash must be a 32-byte hex string", { txHash });
|
|
1303
|
+
}
|
|
1304
|
+
return {
|
|
1305
|
+
proofType: "tx-interaction",
|
|
1306
|
+
proofPurpose: "commercial-tx",
|
|
1307
|
+
proofObject: {
|
|
1308
|
+
chainId: `eip155:${chainId}`,
|
|
1309
|
+
txHash
|
|
1310
|
+
},
|
|
1311
|
+
version: 1,
|
|
1312
|
+
issuedAt: Math.floor(Date.now() / 1e3)
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
async function createPopEip712Proof(params, signFn) {
|
|
1316
|
+
if (!params.signer || !params.authorizedEntity) {
|
|
1317
|
+
throw new OmaTrustError("INVALID_INPUT", "signer and authorizedEntity are required", { params });
|
|
1318
|
+
}
|
|
1319
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1320
|
+
const creationTimestamp = params.creationTimestamp ?? now;
|
|
1321
|
+
const expirationTimestamp = params.expirationTimestamp ?? now + 600;
|
|
1322
|
+
const randomValue = params.randomValue ?? ethers.hexlify(ethers.randomBytes(32));
|
|
1323
|
+
const message = {
|
|
1324
|
+
signer: params.signer,
|
|
1325
|
+
authorizedEntity: params.authorizedEntity,
|
|
1326
|
+
signingPurpose: params.signingPurpose,
|
|
1327
|
+
creationTimestamp,
|
|
1328
|
+
expirationTimestamp,
|
|
1329
|
+
randomValue,
|
|
1330
|
+
statement: params.statement ?? "This is not a transaction or asset approval."
|
|
1331
|
+
};
|
|
1332
|
+
const { types, primaryType } = getOmaTrustProofEip712Types();
|
|
1333
|
+
const typedData = {
|
|
1334
|
+
domain: {
|
|
1335
|
+
name: "OMATrust Proof",
|
|
1336
|
+
version: "1",
|
|
1337
|
+
chainId: params.chainId
|
|
1338
|
+
},
|
|
1339
|
+
types,
|
|
1340
|
+
primaryType,
|
|
1341
|
+
message
|
|
1342
|
+
};
|
|
1343
|
+
const signature = await signFn(typedData);
|
|
1344
|
+
return {
|
|
1345
|
+
proofType: "pop-eip712",
|
|
1346
|
+
proofObject: {
|
|
1347
|
+
domain: typedData.domain,
|
|
1348
|
+
message,
|
|
1349
|
+
signature
|
|
1350
|
+
},
|
|
1351
|
+
version: 1,
|
|
1352
|
+
issuedAt: creationTimestamp,
|
|
1353
|
+
expiresAt: expirationTimestamp
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// src/reputation/proof/pop-jws.ts
|
|
1358
|
+
async function createPopJwsProof(params, signFn) {
|
|
1359
|
+
if (!params.issuer || !params.audience) {
|
|
1360
|
+
throw new OmaTrustError("INVALID_INPUT", "issuer and audience are required", { params });
|
|
1361
|
+
}
|
|
1362
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1363
|
+
const payload = {
|
|
1364
|
+
iss: params.issuer,
|
|
1365
|
+
aud: params.audience,
|
|
1366
|
+
purpose: params.purpose,
|
|
1367
|
+
iat: params.issuedAt ?? now,
|
|
1368
|
+
exp: params.expiresAt ?? now + 600,
|
|
1369
|
+
nonce: params.nonce ?? (globalThis.crypto?.randomUUID ? globalThis.crypto.randomUUID() : `${Date.now()}-${Math.random()}`)
|
|
1370
|
+
};
|
|
1371
|
+
const header = {
|
|
1372
|
+
typ: "JWT",
|
|
1373
|
+
alg: "ES256K"
|
|
1374
|
+
};
|
|
1375
|
+
const jws = await signFn(payload, header);
|
|
1376
|
+
return {
|
|
1377
|
+
proofType: "pop-jws",
|
|
1378
|
+
proofObject: jws,
|
|
1379
|
+
proofPurpose: params.purpose,
|
|
1380
|
+
version: 1,
|
|
1381
|
+
issuedAt: payload.iat,
|
|
1382
|
+
expiresAt: payload.exp
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// src/reputation/proof/x402.ts
|
|
1387
|
+
function createX402ReceiptProof(receipt) {
|
|
1388
|
+
return {
|
|
1389
|
+
proofType: "x402-receipt",
|
|
1390
|
+
proofPurpose: "commercial-tx",
|
|
1391
|
+
proofObject: receipt,
|
|
1392
|
+
version: 1,
|
|
1393
|
+
issuedAt: Math.floor(Date.now() / 1e3)
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
function createX402OfferProof(offer) {
|
|
1397
|
+
return {
|
|
1398
|
+
proofType: "x402-offer",
|
|
1399
|
+
proofPurpose: "commercial-tx",
|
|
1400
|
+
proofObject: offer,
|
|
1401
|
+
version: 1,
|
|
1402
|
+
issuedAt: Math.floor(Date.now() / 1e3)
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// src/reputation/proof/evidence-pointer.ts
|
|
1407
|
+
function createEvidencePointerProof(url) {
|
|
1408
|
+
if (!url || typeof url !== "string") {
|
|
1409
|
+
throw new OmaTrustError("INVALID_INPUT", "url must be a non-empty string", { url });
|
|
1410
|
+
}
|
|
1411
|
+
return {
|
|
1412
|
+
proofType: "evidence-pointer",
|
|
1413
|
+
proofPurpose: "shared-control",
|
|
1414
|
+
proofObject: { url },
|
|
1415
|
+
version: 1,
|
|
1416
|
+
issuedAt: Math.floor(Date.now() / 1e3)
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
exports.buildDelegatedAttestationTypedData = buildDelegatedAttestationTypedData;
|
|
1421
|
+
exports.buildDnsTxtRecord = buildDnsTxtRecord;
|
|
1422
|
+
exports.buildEip712Domain = buildEip712Domain;
|
|
1423
|
+
exports.calculateAverageUserReviewRating = calculateAverageUserReviewRating;
|
|
1424
|
+
exports.calculateTransferAmount = calculateTransferAmount;
|
|
1425
|
+
exports.calculateTransferAmountFromAddresses = calculateTransferAmountFromAddresses;
|
|
1426
|
+
exports.callControllerWitness = callControllerWitness;
|
|
1427
|
+
exports.constructSeed = constructSeed;
|
|
1428
|
+
exports.createEvidencePointerProof = createEvidencePointerProof;
|
|
1429
|
+
exports.createPopEip712Proof = createPopEip712Proof;
|
|
1430
|
+
exports.createPopJwsProof = createPopJwsProof;
|
|
1431
|
+
exports.createTxEncodedValueProof = createTxEncodedValueProof;
|
|
1432
|
+
exports.createTxInteractionProof = createTxInteractionProof;
|
|
1433
|
+
exports.createX402OfferProof = createX402OfferProof;
|
|
1434
|
+
exports.createX402ReceiptProof = createX402ReceiptProof;
|
|
1435
|
+
exports.decodeAttestationData = decodeAttestationData;
|
|
1436
|
+
exports.deduplicateReviews = deduplicateReviews;
|
|
1437
|
+
exports.encodeAttestationData = encodeAttestationData;
|
|
1438
|
+
exports.extractAddressesFromDidDocument = extractAddressesFromDidDocument;
|
|
1439
|
+
exports.extractExpirationTime = extractExpirationTime;
|
|
1440
|
+
exports.fetchDidDocument = fetchDidDocument;
|
|
1441
|
+
exports.formatSchemaUid = formatSchemaUid;
|
|
1442
|
+
exports.formatTransferAmount = formatTransferAmount;
|
|
1443
|
+
exports.getAttestation = getAttestation;
|
|
1444
|
+
exports.getAttestationsForDid = getAttestationsForDid;
|
|
1445
|
+
exports.getChainConstants = getChainConstants;
|
|
1446
|
+
exports.getExplorerAddressUrl = getExplorerAddressUrl;
|
|
1447
|
+
exports.getExplorerTxUrl = getExplorerTxUrl;
|
|
1448
|
+
exports.getLatestAttestations = getLatestAttestations;
|
|
1449
|
+
exports.getMajorVersion = getMajorVersion;
|
|
1450
|
+
exports.getOmaTrustProofEip712Types = getOmaTrustProofEip712Types;
|
|
1451
|
+
exports.getSchemaDetails = getSchemaDetails;
|
|
1452
|
+
exports.getSupportedChainIds = getSupportedChainIds;
|
|
1453
|
+
exports.hashSeed = hashSeed;
|
|
1454
|
+
exports.isChainSupported = isChainSupported;
|
|
1455
|
+
exports.listAttestations = listAttestations;
|
|
1456
|
+
exports.normalizeSchema = normalizeSchema;
|
|
1457
|
+
exports.parseDnsTxtRecord = parseDnsTxtRecord;
|
|
1458
|
+
exports.prepareDelegatedAttestation = prepareDelegatedAttestation;
|
|
1459
|
+
exports.schemaToString = schemaToString;
|
|
1460
|
+
exports.splitSignature = splitSignature;
|
|
1461
|
+
exports.submitAttestation = submitAttestation;
|
|
1462
|
+
exports.submitDelegatedAttestation = submitDelegatedAttestation;
|
|
1463
|
+
exports.verifyAttestation = verifyAttestation;
|
|
1464
|
+
exports.verifyDidDocumentControllerDid = verifyDidDocumentControllerDid;
|
|
1465
|
+
exports.verifyDnsTxtControllerDid = verifyDnsTxtControllerDid;
|
|
1466
|
+
exports.verifyEip712Signature = verifyEip712Signature;
|
|
1467
|
+
exports.verifyProof = verifyProof;
|
|
1468
|
+
exports.verifySchemaExists = verifySchemaExists;
|
|
1469
|
+
//# sourceMappingURL=index.cjs.map
|
|
1470
|
+
//# sourceMappingURL=index.cjs.map
|