@thurinlabs/identity-kit 1.3.7 → 2.0.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.
@@ -0,0 +1,3377 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/core/fingerprint.ts
2
+ var _viem = require('viem');
3
+ var FINGERPRINT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
4
+ function normalizeFingerprint(input) {
5
+ const hex = input.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
6
+ return FINGERPRINT_HEX.test(hex) ? hex : null;
7
+ }
8
+ function sameFingerprint(a, b) {
9
+ const x = a ? normalizeFingerprint(a) : null;
10
+ return x !== null && x === (b ? normalizeFingerprint(b) : null);
11
+ }
12
+ function fingerprintToBytes(fingerprint) {
13
+ const hex = normalizeFingerprint(fingerprint);
14
+ if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
15
+ return `0x${hex}`;
16
+ }
17
+ function bytesToFingerprint(bytes) {
18
+ return bytes.replace(/^0x/i, "").toLowerCase();
19
+ }
20
+ function fingerprintHash(fingerprint) {
21
+ return _viem.keccak256.call(void 0, fingerprintToBytes(fingerprint));
22
+ }
23
+ function keyIdOf(fingerprint) {
24
+ const hex = normalizeFingerprint(fingerprint);
25
+ if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
26
+ return `0x${keyIdHex(hex)}`;
27
+ }
28
+ function keyIdHex(normalizedFingerprint) {
29
+ return normalizedFingerprint.length === 64 ? normalizedFingerprint.slice(0, 16) : normalizedFingerprint.slice(-16);
30
+ }
31
+ function keyIdToBytes(keyId) {
32
+ const hex = keyId.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
33
+ return /^[0-9a-f]{16}$/.test(hex) ? `0x${hex}` : null;
34
+ }
35
+
36
+ // src/core/proofs.ts
37
+ var get = (url, init = {}) => fetch(url, { referrerPolicy: "no-referrer", ...init });
38
+ var PROVIDERS = [
39
+ {
40
+ provider: "github",
41
+ label: "GitHub",
42
+ pattern: /^https:\/\/gist\.github\.com\/([^/]+)\/([a-f0-9]+)$/i,
43
+ parse: (m) => ({ user: m[1], gistId: m[2] })
44
+ },
45
+ {
46
+ // A repository's description: the form an organisation can use, since gists belong to user
47
+ // accounts only. Same shape as the Codeberg proof.
48
+ provider: "github",
49
+ label: "GitHub",
50
+ pattern: /^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)$/i,
51
+ parse: (m) => ({ user: m[1], repo: m[2] })
52
+ },
53
+ {
54
+ provider: "dns",
55
+ label: "DNS",
56
+ // A hostname only; underscores allowed for names like _thurin.example.com.
57
+ pattern: /^dns:([a-z0-9._-]+)\?type=TXT$/i,
58
+ parse: (m) => ({ domain: m[1] })
59
+ },
60
+ {
61
+ provider: "farcaster",
62
+ label: "Farcaster",
63
+ pattern: /^https:\/\/farcaster\.xyz\/([^/]+)\/(0x[a-f0-9]+)$/i,
64
+ parse: (m) => ({ user: m[1], castHash: m[2] })
65
+ },
66
+ {
67
+ provider: "codeberg",
68
+ label: "Codeberg",
69
+ pattern: /^https:\/\/codeberg\.org\/([^/]+)\/([^/]+)$/i,
70
+ parse: (m) => ({ user: m[1], repo: m[2] })
71
+ },
72
+ {
73
+ provider: "mastodon",
74
+ label: "Mastodon",
75
+ // A bare hostname (optional port), so "mastodon.social@evil.com" can't send the lookup elsewhere.
76
+ pattern: /^https:\/\/([a-z0-9.-]+(?::\d+)?)\/@([^/@?#\s]+)$/i,
77
+ parse: (m) => ({ instance: m[1], user: m[2] })
78
+ }
79
+ ];
80
+ function identifyProof(notation) {
81
+ if (notation.name !== "proof@thurin.id") return null;
82
+ for (const { provider, label, pattern, parse } of PROVIDERS) {
83
+ const m = notation.value.match(pattern);
84
+ if (m) {
85
+ return { provider, label, url: notation.value, ...parse(m) };
86
+ }
87
+ }
88
+ return { provider: "unknown", label: "Unknown", url: notation.value };
89
+ }
90
+ function displayUrl(proof) {
91
+ if (proof.provider === "dns") return proof.domain;
92
+ if (proof.provider === "github") return proof.user;
93
+ if (proof.provider === "farcaster") return `@${proof.user}`;
94
+ if (proof.provider === "codeberg") return proof.user;
95
+ if (proof.provider === "mastodon") return `@${proof.user}@${proof.instance}`;
96
+ return proof.url;
97
+ }
98
+ function proofHref(proof) {
99
+ if (proof.provider === "dns") return `https://${proof.domain}`;
100
+ if (proof.provider === "github") return `https://github.com/${proof.user}`;
101
+ if (proof.provider === "farcaster") return `https://farcaster.xyz/${proof.user}`;
102
+ if (proof.provider === "codeberg") return `https://codeberg.org/${proof.user}`;
103
+ if (proof.provider === "mastodon") return `https://${proof.instance}/@${proof.user}`;
104
+ if (proof.url.startsWith("http")) return proof.url;
105
+ return null;
106
+ }
107
+ function proofSecondaryHref(proof) {
108
+ if (proof.provider === "github") return proof.url;
109
+ if (proof.provider === "farcaster") return proof.url;
110
+ if (proof.provider === "codeberg") return proof.url;
111
+ if (proof.provider === "mastodon") return proof.url;
112
+ return null;
113
+ }
114
+ var FPR_TOKEN = "OPENPGP4FPR:";
115
+ function containsFingerprint(text, fingerprint) {
116
+ return text.toUpperCase().includes(FPR_TOKEN + fingerprint.toUpperCase());
117
+ }
118
+ function containsFingerprintUrl(text, fingerprint) {
119
+ return text.toUpperCase().includes(fingerprint.toUpperCase());
120
+ }
121
+ async function verifyGitHub(proof, fingerprint) {
122
+ if (proof.repo) return verifyGitHubRepo(proof, fingerprint);
123
+ try {
124
+ const resp = await get(`https://api.github.com/gists/${proof.gistId}`);
125
+ if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
126
+ const data = await resp.json();
127
+ if (_optionalChain([data, 'access', _ => _.owner, 'optionalAccess', _2 => _2.login, 'optionalAccess', _3 => _3.toLowerCase, 'call', _4 => _4()]) !== proof.user.toLowerCase()) {
128
+ return { verified: false, reason: "Gist owner does not match claimed user" };
129
+ }
130
+ for (const file of Object.values(data.files || {})) {
131
+ if (file.content && containsFingerprint(file.content, fingerprint)) {
132
+ return { verified: true };
133
+ }
134
+ }
135
+ return { verified: false, reason: "Fingerprint token not found in gist" };
136
+ } catch (err) {
137
+ return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
138
+ }
139
+ }
140
+ async function verifyGitHubRepo(proof, fingerprint) {
141
+ try {
142
+ const resp = await get(
143
+ `https://api.github.com/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
144
+ );
145
+ if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
146
+ const data = await resp.json();
147
+ if (_optionalChain([data, 'access', _5 => _5.owner, 'optionalAccess', _6 => _6.login, 'optionalAccess', _7 => _7.toLowerCase, 'call', _8 => _8()]) !== proof.user.toLowerCase()) {
148
+ return { verified: false, reason: "Repository owner does not match claimed account" };
149
+ }
150
+ if (data.description && containsFingerprint(data.description, fingerprint)) {
151
+ return { verified: true };
152
+ }
153
+ return { verified: false, reason: "Fingerprint token not found in repository description" };
154
+ } catch (err) {
155
+ return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
156
+ }
157
+ }
158
+ async function verifyDNS(proof, fingerprint) {
159
+ try {
160
+ const resp = await get(
161
+ `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(proof.domain)}&type=TXT`,
162
+ { headers: { Accept: "application/dns-json" } }
163
+ );
164
+ if (!resp.ok) return { verified: false, reason: `DNS query returned ${resp.status}` };
165
+ const data = await resp.json();
166
+ for (const answer of data.Answer || []) {
167
+ if (answer.type !== 16) continue;
168
+ if (answer.data && containsFingerprint(answer.data, fingerprint)) {
169
+ return { verified: true };
170
+ }
171
+ }
172
+ return { verified: false, reason: "Fingerprint token not found in TXT records" };
173
+ } catch (err) {
174
+ return { verified: false, reason: `DNS fetch failed: ${err.message}` };
175
+ }
176
+ }
177
+ var FARCASTER_HUB = "https://haatz.quilibrium.com";
178
+ var NEYNAR_HUB = "https://hub-api.neynar.com";
179
+ function farcasterSource(opts) {
180
+ if (opts.farcasterHub) return { base: opts.farcasterHub.replace(/\/+$/, ""), headers: {} };
181
+ if (opts.neynarApiKey) return { base: NEYNAR_HUB, headers: { "x-api-key": opts.neynarApiKey } };
182
+ return { base: FARCASTER_HUB, headers: {} };
183
+ }
184
+ async function verifyFarcaster(proof, fingerprint, opts = {}) {
185
+ const { base, headers } = farcasterSource(opts);
186
+ const host = base.replace(/^https?:\/\//, "");
187
+ try {
188
+ const nameResp = await get(`${base}/v1/userNameProofByName?name=${encodeURIComponent(proof.user)}`, { headers });
189
+ if (nameResp.status === 404) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
190
+ if (!nameResp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${nameResp.status}` };
191
+ const fid = (await nameResp.json()).fid;
192
+ if (!fid) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
193
+ let pageToken = "";
194
+ for (let page = 0; page < 5; page++) {
195
+ const url = `${base}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ""}`;
196
+ const resp = await get(url, { headers });
197
+ if (!resp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${resp.status}` };
198
+ const data = await resp.json();
199
+ for (const msg of data.messages || []) {
200
+ if (msg.hash && msg.hash.startsWith(proof.castHash)) {
201
+ const text = _optionalChain([msg, 'access', _9 => _9.data, 'optionalAccess', _10 => _10.castAddBody, 'optionalAccess', _11 => _11.text]) || "";
202
+ if (containsFingerprint(text, fingerprint)) {
203
+ return { verified: true };
204
+ }
205
+ }
206
+ }
207
+ if (!data.nextPageToken) break;
208
+ pageToken = data.nextPageToken;
209
+ }
210
+ return { verified: false, reason: "Cast not found" };
211
+ } catch (err) {
212
+ return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) didn't answer (${err.message})` };
213
+ }
214
+ }
215
+ async function verifyCodeberg(proof, fingerprint) {
216
+ try {
217
+ const resp = await get(
218
+ `https://codeberg.org/api/v1/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
219
+ );
220
+ if (!resp.ok) return { verified: false, reason: `Codeberg API returned ${resp.status}` };
221
+ const data = await resp.json();
222
+ if (_optionalChain([data, 'access', _12 => _12.owner, 'optionalAccess', _13 => _13.login, 'optionalAccess', _14 => _14.toLowerCase, 'call', _15 => _15()]) !== proof.user.toLowerCase()) {
223
+ return { verified: false, reason: "Repository owner does not match claimed account" };
224
+ }
225
+ if (data.description && containsFingerprint(data.description, fingerprint)) {
226
+ return { verified: true };
227
+ }
228
+ return { verified: false, reason: "Fingerprint token not found in repo description" };
229
+ } catch (err) {
230
+ return { verified: false, reason: `Codeberg fetch failed: ${err.message}` };
231
+ }
232
+ }
233
+ async function verifyMastodon(proof, fingerprint) {
234
+ try {
235
+ if (!proof.instance || !/^[a-z0-9.-]+(?::\d+)?$/i.test(proof.instance)) {
236
+ return { verified: false, reason: "Invalid Mastodon instance host" };
237
+ }
238
+ const resp = await get(
239
+ `https://${proof.instance}/api/v1/accounts/lookup?acct=${encodeURIComponent(proof.user)}`
240
+ );
241
+ if (!resp.ok) return { verified: false, reason: `Mastodon API returned ${resp.status}` };
242
+ const data = await resp.json();
243
+ const keyId = keyIdHex(fingerprint.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase());
244
+ for (const field of data.fields || []) {
245
+ const text = field.value.replace(/<[^>]*>/g, "");
246
+ if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
247
+ return { verified: true };
248
+ }
249
+ }
250
+ if (data.note) {
251
+ const text = data.note.replace(/<[^>]*>/g, "");
252
+ if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
253
+ return { verified: true };
254
+ }
255
+ }
256
+ return { verified: false, reason: "Fingerprint not found in profile fields or bio" };
257
+ } catch (err) {
258
+ return { verified: false, reason: `Mastodon fetch failed: ${err.message}` };
259
+ }
260
+ }
261
+ var verifiers = {
262
+ github: verifyGitHub,
263
+ dns: verifyDNS,
264
+ farcaster: verifyFarcaster,
265
+ codeberg: verifyCodeberg,
266
+ mastodon: verifyMastodon
267
+ };
268
+ async function verifyProof(proof, fingerprint, opts) {
269
+ const fn = verifiers[proof.provider];
270
+ if (!fn) return { verified: false, reason: "Unknown provider" };
271
+ return fn(proof, fingerprint, typeof opts === "string" ? { neynarApiKey: opts } : _nullishCoalesce(opts, () => ( {})));
272
+ }
273
+
274
+ // src/core/pgp.ts
275
+ function pgpConfig(openpgp) {
276
+ const rejectCurves = new Set([..._nullishCoalesce(openpgp.config.rejectCurves, () => ( []))].filter((c) => c !== "secp256k1"));
277
+ return { ...openpgp.config, rejectCurves };
278
+ }
279
+ function newestSignature(sigs = []) {
280
+ return [...sigs].sort((x, y) => (_nullishCoalesce(_optionalChain([y, 'access', _16 => _16.created, 'optionalAccess', _17 => _17.getTime, 'optionalCall', _18 => _18()]), () => ( 0))) - (_nullishCoalesce(_optionalChain([x, 'access', _19 => _19.created, 'optionalAccess', _20 => _20.getTime, 'optionalCall', _21 => _21()]), () => ( 0))))[0];
281
+ }
282
+ function algorithmName(keyOrSubkey) {
283
+ try {
284
+ const info = keyOrSubkey.getAlgorithmInfo();
285
+ const curve = (info.curve || "").toLowerCase();
286
+ if (curve.startsWith("ed25519") || info.algorithm === "ed25519" || info.algorithm === "eddsaLegacy") return "Ed25519";
287
+ if (curve.startsWith("curve25519") || info.algorithm === "x25519") return "Cv25519";
288
+ if (curve.startsWith("ed448")) return "Ed448";
289
+ if (curve.startsWith("curve448") || info.algorithm === "x448") return "X448";
290
+ if (curve.startsWith("nistp") || curve.startsWith("p")) return `NIST P-${curve.replace(/\D/g, "")}`;
291
+ if (curve.startsWith("secp")) return curve;
292
+ if (curve.startsWith("brainpool")) return curve;
293
+ if (info.algorithm.startsWith("rsa")) return info.bits ? `RSA ${info.bits}` : "RSA";
294
+ if (info.algorithm.startsWith("dsa")) return info.bits ? `DSA ${info.bits}` : "DSA";
295
+ if (info.algorithm.startsWith("elgamal")) return info.bits ? `ElGamal ${info.bits}` : "ElGamal";
296
+ return info.curve || info.algorithm;
297
+ } catch (e2) {
298
+ return String(_nullishCoalesce(_optionalChain([keyOrSubkey, 'access', _22 => _22.keyPacket, 'optionalAccess', _23 => _23.algorithm]), () => ( "unknown")));
299
+ }
300
+ }
301
+ var TEXT_START = 45;
302
+ function hexToBytes(hex) {
303
+ const h = hex.slice(2);
304
+ const out = new Uint8Array(h.length / 2);
305
+ for (let i = 0; i < out.length; i++) out[i] = parseInt(h.substr(i * 2, 2), 16);
306
+ return out;
307
+ }
308
+ function normalizeInput(input) {
309
+ if (typeof input === "string") {
310
+ const t = input.trim();
311
+ if (/^0x([0-9a-fA-F]{2})*$/.test(t)) return normalizeInput(hexToBytes(t));
312
+ return input;
313
+ }
314
+ if (input.length > 0 && input[0] === TEXT_START) return new TextDecoder().decode(input);
315
+ return input;
316
+ }
317
+ async function readKeyAny(openpgp, input) {
318
+ const v = normalizeInput(input);
319
+ return typeof v === "string" ? openpgp.readKey({ armoredKey: v }) : openpgp.readKey({ binaryKey: v });
320
+ }
321
+ async function readSignatureAny(openpgp, input) {
322
+ const v = normalizeInput(input);
323
+ if (typeof v === "string" && v.includes("-----BEGIN PGP SIGNED MESSAGE-----")) {
324
+ const message = await openpgp.readCleartextMessage({ cleartextMessage: v });
325
+ return { packets: message.signature.packets, text: message.getText(), signature: message.signature };
326
+ }
327
+ const signature = typeof v === "string" ? await openpgp.readSignature({ armoredSignature: v }) : await openpgp.readSignature({ binarySignature: v });
328
+ return { packets: signature.packets, text: null, signature };
329
+ }
330
+ function statementText(address) {
331
+ return `I control the Ethereum address: ${address.toLowerCase()}`;
332
+ }
333
+ async function parsePgpKey(armoredKey) {
334
+ try {
335
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
336
+ const config = pgpConfig(openpgp);
337
+ const key = await readKeyAny(openpgp, armoredKey);
338
+ const fingerprint = key.getFingerprint().toUpperCase();
339
+ const algorithm = algorithmName(key);
340
+ const created = _nullishCoalesce(_optionalChain([key, 'access', _24 => _24.keyPacket, 'access', _25 => _25.created, 'optionalAccess', _26 => _26.toISOString, 'call', _27 => _27()]), () => ( null));
341
+ const expiration = await key.getExpirationTime();
342
+ const expires = expiration && expiration !== Infinity ? new Date(expiration).toISOString() : null;
343
+ const userIDs = [];
344
+ const notations = [];
345
+ const seen = /* @__PURE__ */ new Set();
346
+ for (const user of key.users) {
347
+ const certs = user.selfCertifications;
348
+ if (!certs || certs.length === 0) continue;
349
+ let userVerified = false;
350
+ for (const cert of certs) {
351
+ try {
352
+ await cert.verify(
353
+ key.keyPacket,
354
+ cert.signatureType,
355
+ { userID: user.userID, key: key.keyPacket },
356
+ void 0,
357
+ // date: now
358
+ void 0,
359
+ // detached
360
+ config
361
+ );
362
+ } catch (e3) {
363
+ continue;
364
+ }
365
+ userVerified = true;
366
+ if (cert.rawNotations) {
367
+ for (const n of cert.rawNotations) {
368
+ const name = typeof n.name === "string" ? n.name : new TextDecoder().decode(n.name);
369
+ const value = n.value instanceof Uint8Array ? new TextDecoder().decode(n.value) : typeof n.value === "string" ? n.value : null;
370
+ if (value) {
371
+ if (!seen.has(value)) {
372
+ seen.add(value);
373
+ notations.push({ name, value });
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
379
+ if (userVerified) {
380
+ const uid = _optionalChain([user, 'access', _28 => _28.userID, 'optionalAccess', _29 => _29.userID]);
381
+ if (uid) userIDs.push(uid);
382
+ }
383
+ }
384
+ const now = /* @__PURE__ */ new Date();
385
+ const subkeys = await Promise.all(key.subkeys.map(async (sk) => {
386
+ const flags = _nullishCoalesce(_optionalChain([newestSignature, 'call', _30 => _30(sk.bindingSignatures), 'optionalAccess', _31 => _31.keyFlags, 'optionalAccess', _32 => _32[0]]), () => ( 0));
387
+ const usage = [];
388
+ if (flags & 2) usage.push("sign");
389
+ if (flags & 12) usage.push("encrypt");
390
+ if (flags & 32) usage.push("auth");
391
+ const exp = await sk.getExpirationTime(now, config).catch(() => null);
392
+ const valid = await sk.verify(now, config).then(() => true, () => false);
393
+ return {
394
+ algorithm: algorithmName(sk),
395
+ created: _nullishCoalesce(_optionalChain([sk, 'access', _33 => _33.keyPacket, 'access', _34 => _34.created, 'optionalAccess', _35 => _35.toISOString, 'call', _36 => _36()]), () => ( null)),
396
+ fingerprint: sk.getFingerprint().toUpperCase(),
397
+ usage,
398
+ expires: exp && exp !== Infinity ? new Date(exp).toISOString() : null,
399
+ valid
400
+ };
401
+ }));
402
+ const canEncrypt = await key.getEncryptionKey(void 0, now, void 0, config).then(() => true, () => false);
403
+ return { fingerprint, userIDs, algorithm, created, expires, notations, subkeys, canEncrypt };
404
+ } catch (e4) {
405
+ return null;
406
+ }
407
+ }
408
+ async function verifySignedText(openpgp, publicKey, packets, text) {
409
+ const { LiteralDataPacket } = openpgp;
410
+ const config = pgpConfig(openpgp);
411
+ const now = /* @__PURE__ */ new Date();
412
+ for (const sig of packets) {
413
+ if (!publicKey.getKeys(sig.issuerKeyID).length) continue;
414
+ const signingKey = await publicKey.getSigningKey(sig.issuerKeyID, now, void 0, config);
415
+ const literal = new LiteralDataPacket();
416
+ literal.setText(text.replace(/\r?\n/g, "\r\n"));
417
+ await sig.verify(signingKey.keyPacket, sig.signatureType, literal, now, true, config);
418
+ return true;
419
+ }
420
+ return false;
421
+ }
422
+ async function verifyClearsigned({ armoredKey, clearsigned }) {
423
+ try {
424
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
425
+ const publicKey = await readKeyAny(openpgp, armoredKey);
426
+ const message = await openpgp.readCleartextMessage({ cleartextMessage: clearsigned });
427
+ const text = _nullishCoalesce(message.text, () => ( message.getText()));
428
+ if (await verifySignedText(openpgp, publicKey, message.signature.packets, text)) return { verified: true, text: message.getText() };
429
+ return { verified: false, reason: "Message is not signed by this key", text: message.getText() };
430
+ } catch (err) {
431
+ return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text: "" };
432
+ }
433
+ }
434
+ async function verifyStatementSignature({ key, signature, address }) {
435
+ const text = statementText(address);
436
+ try {
437
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
438
+ const publicKey = await readKeyAny(openpgp, key);
439
+ const { packets } = await readSignatureAny(openpgp, signature);
440
+ if (await verifySignedText(openpgp, publicKey, packets, text)) return { verified: true, text };
441
+ return { verified: false, reason: "Message is not signed by this key", text };
442
+ } catch (err) {
443
+ return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text };
444
+ }
445
+ }
446
+ var REVOCATION_REASON = {
447
+ 1: "replaced by a newer key",
448
+ 2: "compromised",
449
+ 3: "no longer used",
450
+ 32: "a user ID is no longer valid"
451
+ };
452
+ var iso = (d) => d instanceof Date ? d.toISOString() : typeof d === "number" && Number.isFinite(d) ? new Date(d).toISOString() : null;
453
+ function revocationOf(sigs) {
454
+ const sig = [..._nullishCoalesce(sigs, () => ( []))].sort((a, b) => (_nullishCoalesce(_optionalChain([b, 'access', _37 => _37.created, 'optionalAccess', _38 => _38.getTime, 'optionalCall', _39 => _39()]), () => ( 0))) - (_nullishCoalesce(_optionalChain([a, 'access', _40 => _40.created, 'optionalAccess', _41 => _41.getTime, 'optionalCall', _42 => _42()]), () => ( 0))))[0];
455
+ const flag = _nullishCoalesce(_optionalChain([sig, 'optionalAccess', _43 => _43.reasonForRevocationFlag]), () => ( null));
456
+ return {
457
+ at: iso(_optionalChain([sig, 'optionalAccess', _44 => _44.created])),
458
+ compromised: flag === 2,
459
+ revocationReason: _optionalChain([sig, 'optionalAccess', _45 => _45.reasonForRevocationString]) || (flag !== null ? _nullishCoalesce(REVOCATION_REASON[flag], () => ( null)) : null)
460
+ };
461
+ }
462
+ async function keyLife(key, issuer, config) {
463
+ const now = /* @__PURE__ */ new Date();
464
+ const signer = issuer && !key.getKeyID().equals(issuer) ? _nullishCoalesce(key.subkeys.find((s) => s.getKeyID().equals(issuer)), () => ( null)) : key;
465
+ const signingKey = signer ? signer.getFingerprint().toUpperCase() : null;
466
+ const safe = async (f, fallback) => {
467
+ try {
468
+ return await f();
469
+ } catch (e5) {
470
+ return fallback;
471
+ }
472
+ };
473
+ const keyRevoked = await safe(() => key.isRevoked(void 0, void 0, now, config), false);
474
+ const keyExp = await safe(() => key.getExpirationTime(void 0, config), null);
475
+ const isSub = signer && signer !== key;
476
+ const subRevoked = isSub ? await safe(() => signer.isRevoked(null, key.keyPacket, now, config), false) : false;
477
+ const subExp = isSub ? await safe(() => signer.getExpirationTime(null, config), null) : null;
478
+ const finite = (d) => d instanceof Date && Number.isFinite(d.getTime()) ? d : null;
479
+ const kExp = finite(keyExp), sExp = finite(subExp);
480
+ let problem = null;
481
+ if (keyRevoked) {
482
+ const r = revocationOf(key.revocationSignatures);
483
+ problem = { kind: r.compromised ? "compromised" : "revoked", at: r.at, revocationReason: r.revocationReason };
484
+ } else if (kExp && kExp <= now) {
485
+ problem = { kind: "expired", at: kExp.toISOString() };
486
+ } else if (subRevoked) {
487
+ const r = revocationOf(signer.revocationSignatures);
488
+ problem = { kind: r.compromised ? "compromised" : "signing-key-revoked", at: r.at, revocationReason: r.revocationReason };
489
+ } else if (sExp && sExp <= now) {
490
+ problem = { kind: "signing-key-expired", at: sExp.toISOString() };
491
+ }
492
+ const earliest = [kExp, sExp].filter(Boolean).sort((a, b) => a - b)[0];
493
+ return { problem, signingKey, expiresAt: earliest ? earliest.toISOString() : null, algorithm: algorithmName(_nullishCoalesce(signer, () => ( key))) };
494
+ }
495
+ async function verifyAttestation({
496
+ pgpPublicKey,
497
+ pgpSignature,
498
+ fingerprint,
499
+ ethAddress
500
+ }) {
501
+ try {
502
+ if (!pgpPublicKey || !pgpSignature) {
503
+ return { verified: false, reason: "Missing PGP data", kind: "bad-signature" };
504
+ }
505
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
506
+ const publicKey = await readKeyAny(openpgp, pgpPublicKey);
507
+ const keyFingerprint = publicKey.getFingerprint().toUpperCase();
508
+ if (keyFingerprint !== fingerprint.toUpperCase()) {
509
+ return { verified: false, reason: "Key fingerprint mismatch", kind: "bad-signature" };
510
+ }
511
+ let sig;
512
+ try {
513
+ sig = await readSignatureAny(openpgp, pgpSignature);
514
+ } catch (e6) {
515
+ return { verified: false, reason: "Unreadable signature", kind: "bad-signature" };
516
+ }
517
+ const life = await keyLife(publicKey, _nullishCoalesce(_optionalChain([sig, 'access', _46 => _46.packets, 'access', _47 => _47[0], 'optionalAccess', _48 => _48.issuerKeyID]), () => ( null)), pgpConfig(openpgp));
518
+ const v = sig.text !== null ? await verifyClearsigned({ armoredKey: publicKey.armor(), clearsigned: normalizeInput(pgpSignature) }) : await verifyStatementSignature({ key: publicKey.armor(), signature: pgpSignature, address: ethAddress });
519
+ if (!v.verified) {
520
+ if (life.problem) return { verified: false, reason: v.reason, signingKey: life.signingKey, ...life.problem };
521
+ if (/too weak|reject|unsupported|not supported|unknown (curve|algorithm)/i.test(v.reason || "")) {
522
+ return { verified: false, reason: v.reason, kind: "unsupported", algorithm: life.algorithm, signingKey: life.signingKey };
523
+ }
524
+ return { verified: false, reason: v.reason, kind: "bad-signature", signingKey: life.signingKey };
525
+ }
526
+ if (v.text.trim().toLowerCase() !== statementText(ethAddress).toLowerCase()) {
527
+ return { verified: false, reason: "Signed text is not the statement for this address", kind: "bad-signature", signingKey: life.signingKey };
528
+ }
529
+ return { verified: true, kind: "verified", signingKey: life.signingKey, expiresAt: life.expiresAt };
530
+ } catch (e7) {
531
+ return { verified: false, reason: "Signature verification failed", kind: "bad-signature" };
532
+ }
533
+ }
534
+ var EMAIL_UID = /@/;
535
+ var EMAIL_IN_TEXT = /(?:^|[^\w/.+-])([\w.+-]+@[\w-]+(?:\.[\w-]+)+)/;
536
+ function notationEmails(sig) {
537
+ const out = [];
538
+ for (const n of _nullishCoalesce(_optionalChain([sig, 'optionalAccess', _49 => _49.rawNotations]), () => ( []))) {
539
+ const text = typeof n.value === "string" ? n.value : new TextDecoder().decode(n.value);
540
+ const m = text.match(EMAIL_IN_TEXT);
541
+ if (m) out.push(m[1]);
542
+ }
543
+ return out;
544
+ }
545
+ async function stripEmailUserIDs(armoredKey) {
546
+ try {
547
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
548
+ const key = await readKeyAny(openpgp, armoredKey);
549
+ const kept = [];
550
+ const removed = [];
551
+ const users = key.users.filter((u) => {
552
+ const uid = _optionalChain([u, 'access', _50 => _50.userID, 'optionalAccess', _51 => _51.userID]);
553
+ if (!uid) return false;
554
+ if (EMAIL_UID.test(uid)) {
555
+ removed.push(uid);
556
+ return false;
557
+ }
558
+ kept.push(uid);
559
+ return true;
560
+ });
561
+ if (users.length === 0) return null;
562
+ key.users = users;
563
+ return { armored: key.armor(), binary: key.write(), kept, removed };
564
+ } catch (e8) {
565
+ return null;
566
+ }
567
+ }
568
+ async function hasEmailUserID(armoredKey) {
569
+ try {
570
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
571
+ const key = await readKeyAny(openpgp, armoredKey);
572
+ return key.users.some((u) => EMAIL_UID.test(_nullishCoalesce(_optionalChain([u, 'access', _52 => _52.userID, 'optionalAccess', _53 => _53.userID]), () => ( ""))));
573
+ } catch (e9) {
574
+ return false;
575
+ }
576
+ }
577
+ async function leanKey(input, { includeEmail = false } = {}) {
578
+ try {
579
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
580
+ const key = await readKeyAny(openpgp, input);
581
+ const kept = [];
582
+ const removed = [];
583
+ const newest = (sigs = []) => sigs.length ? [[...sigs].sort((x, y) => (_nullishCoalesce(_optionalChain([y, 'access', _54 => _54.created, 'optionalAccess', _55 => _55.getTime, 'optionalCall', _56 => _56()]), () => ( 0))) - (_nullishCoalesce(_optionalChain([x, 'access', _57 => _57.created, 'optionalAccess', _58 => _58.getTime, 'optionalCall', _59 => _59()]), () => ( 0))))[0]] : [];
584
+ for (const u of key.users) {
585
+ u.selfCertifications = _optionalChain([u, 'access', _60 => _60.revocationSignatures, 'optionalAccess', _61 => _61.length]) ? [] : newest(u.selfCertifications);
586
+ u.otherCertifications = [];
587
+ }
588
+ const users = key.users.filter((u) => {
589
+ const uid = _optionalChain([u, 'access', _62 => _62.userID, 'optionalAccess', _63 => _63.userID]);
590
+ if (!uid) return false;
591
+ if (!includeEmail && (EMAIL_UID.test(uid) || u.selfCertifications.some((c) => notationEmails(c).length))) {
592
+ removed.push(uid);
593
+ return false;
594
+ }
595
+ kept.push(uid);
596
+ return true;
597
+ });
598
+ if (users.length === 0) return null;
599
+ key.users = users;
600
+ key.directSignatures = newest(key.directSignatures);
601
+ const keyNotationEmails = includeEmail ? [] : key.directSignatures.flatMap(notationEmails);
602
+ for (const sk of key.subkeys) sk.bindingSignatures = newest(sk.bindingSignatures);
603
+ return { binary: key.write(), kept, removed, keyNotationEmails };
604
+ } catch (e10) {
605
+ return null;
606
+ }
607
+ }
608
+ async function claimSignature({ signature, key, address }) {
609
+ try {
610
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
611
+ const sig = await readSignatureAny(openpgp, signature);
612
+ if (sig.text === null) {
613
+ const v = normalizeInput(signature);
614
+ const raw = typeof v === "string" ? (await openpgp.unarmor(v)).data : v;
615
+ return { signature: raw, messageVersion: 1 };
616
+ }
617
+ const packet = sig.signature.write();
618
+ if ((await verifyStatementSignature({ key, signature: packet, address })).verified) return { signature: packet, messageVersion: 1 };
619
+ return { signature: normalizeInput(signature), messageVersion: 0 };
620
+ } catch (e11) {
621
+ return null;
622
+ }
623
+ }
624
+ async function signatureEmail(signature) {
625
+ try {
626
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
627
+ const { packets } = await readSignatureAny(openpgp, signature);
628
+ for (const p of packets) {
629
+ const uid = _nullishCoalesce(p.signersUserID, () => ( null));
630
+ if (uid && uid.includes("@")) return uid;
631
+ }
632
+ return null;
633
+ } catch (e12) {
634
+ return null;
635
+ }
636
+ }
637
+ async function payloadText(input, kind) {
638
+ try {
639
+ const v = normalizeInput(input);
640
+ if (typeof v === "string") return v;
641
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
642
+ if (kind === "key") return (await openpgp.readKey({ binaryKey: v })).armor();
643
+ return (await openpgp.readSignature({ binarySignature: v })).armor();
644
+ } catch (e13) {
645
+ return null;
646
+ }
647
+ }
648
+ var AUTH_FLAG = 32;
649
+ function sshString(bytes) {
650
+ const out = new Uint8Array(4 + bytes.length);
651
+ new DataView(out.buffer).setUint32(0, bytes.length);
652
+ out.set(bytes, 4);
653
+ return out;
654
+ }
655
+ function sshMpint(bytes) {
656
+ let i = 0;
657
+ while (i < bytes.length - 1 && bytes[i] === 0) i++;
658
+ const b = bytes.subarray(i);
659
+ return sshString(b[0] & 128 ? new Uint8Array([0, ...b]) : b);
660
+ }
661
+ function sshBlob(p) {
662
+ const text = (s) => new TextEncoder().encode(s);
663
+ const join = (...parts) => {
664
+ const out = new Uint8Array(parts.reduce((n, x) => n + x.length, 0));
665
+ let o = 0;
666
+ for (const x of parts) {
667
+ out.set(x, o);
668
+ o += x.length;
669
+ }
670
+ return out;
671
+ };
672
+ const { publicParams: pp } = p;
673
+ switch (p.algorithm) {
674
+ case 1:
675
+ case 3:
676
+ return { type: "ssh-rsa", blob: join(sshString(text("ssh-rsa")), sshMpint(pp.e), sshMpint(pp.n)) };
677
+ case 22:
678
+ if (_optionalChain([pp, 'access', _64 => _64.oid, 'optionalAccess', _65 => _65.getName, 'optionalCall', _66 => _66()]) !== "ed25519Legacy" || _optionalChain([pp, 'access', _67 => _67.Q, 'optionalAccess', _68 => _68.length]) !== 33) return null;
679
+ return { type: "ssh-ed25519", blob: join(sshString(text("ssh-ed25519")), sshString(pp.Q.subarray(1))) };
680
+ case 27:
681
+ return { type: "ssh-ed25519", blob: join(sshString(text("ssh-ed25519")), sshString(pp.A)) };
682
+ case 19: {
683
+ const curve = { nistP256: "nistp256", nistP384: "nistp384", nistP521: "nistp521" }[_optionalChain([pp, 'access', _69 => _69.oid, 'optionalAccess', _70 => _70.getName, 'optionalCall', _71 => _71()])];
684
+ if (!curve) return null;
685
+ const type = `ecdsa-sha2-${curve}`;
686
+ return { type, blob: join(sshString(text(type)), sshString(text(curve)), sshString(pp.Q)) };
687
+ }
688
+ default:
689
+ return null;
690
+ }
691
+ }
692
+ function base64(bytes) {
693
+ let s = "";
694
+ for (const b of bytes) s += String.fromCharCode(b);
695
+ return btoa(s);
696
+ }
697
+ async function sshKeys(input) {
698
+ try {
699
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
700
+ const config = pgpConfig(openpgp);
701
+ const key = await readKeyAny(openpgp, input);
702
+ const now = /* @__PURE__ */ new Date();
703
+ try {
704
+ await key.verifyPrimaryKey(now, void 0, config);
705
+ } catch (e14) {
706
+ return [];
707
+ }
708
+ const candidates = [];
709
+ const direct = newestSignature(key.directSignatures);
710
+ const primarySig = _optionalChain([direct, 'optionalAccess', _72 => _72.keyFlags]) ? direct : (await key.getPrimaryUser(now, void 0, config)).selfCertification;
711
+ candidates.push({ packet: key.keyPacket, flags: _nullishCoalesce(_optionalChain([primarySig, 'optionalAccess', _73 => _73.keyFlags, 'optionalAccess', _74 => _74[0]]), () => ( 0)) });
712
+ for (const sk of key.subkeys) {
713
+ try {
714
+ await sk.verify(now, config);
715
+ } catch (e15) {
716
+ continue;
717
+ }
718
+ candidates.push({ packet: sk.keyPacket, flags: _nullishCoalesce(_optionalChain([newestSignature, 'call', _75 => _75(sk.bindingSignatures), 'optionalAccess', _76 => _76.keyFlags, 'optionalAccess', _77 => _77[0]]), () => ( 0)) });
719
+ }
720
+ const out = [];
721
+ for (const { packet, flags } of candidates) {
722
+ if (!(flags & AUTH_FLAG)) continue;
723
+ const ssh = sshBlob(packet);
724
+ if (!ssh) continue;
725
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", ssh.blob));
726
+ out.push({
727
+ line: `${ssh.type} ${base64(ssh.blob)}`,
728
+ sha256: `SHA256:${base64(digest).replace(/=+$/, "")}`,
729
+ fingerprint: packet.getFingerprint().toUpperCase()
730
+ });
731
+ }
732
+ return out;
733
+ } catch (e16) {
734
+ return [];
735
+ }
736
+ }
737
+
738
+ // src/core/authorization.ts
739
+
740
+ var EIP712_NAME = "Thurin PGPRegistry";
741
+ var EIP712_VERSION = "3";
742
+ function registryDomain(chainId, verifyingContract) {
743
+ return { name: EIP712_NAME, version: EIP712_VERSION, chainId, verifyingContract };
744
+ }
745
+ var AUTHORIZATION_TYPES = {
746
+ Attest: [
747
+ { name: "owner", type: "address" },
748
+ { name: "fingerprint", type: "bytes" },
749
+ { name: "signature", type: "bytes" },
750
+ { name: "key", type: "bytes" },
751
+ { name: "nonce", type: "uint256" },
752
+ { name: "deadline", type: "uint256" }
753
+ ],
754
+ Reattest: [
755
+ { name: "owner", type: "address" },
756
+ { name: "revokeIndex", type: "uint256" },
757
+ { name: "fingerprint", type: "bytes" },
758
+ { name: "signature", type: "bytes" },
759
+ { name: "key", type: "bytes" },
760
+ { name: "keepRecords", type: "bool" },
761
+ { name: "nonce", type: "uint256" },
762
+ { name: "deadline", type: "uint256" }
763
+ ],
764
+ UpdateKey: [
765
+ { name: "owner", type: "address" },
766
+ { name: "index", type: "uint256" },
767
+ { name: "key", type: "bytes" },
768
+ { name: "nonce", type: "uint256" },
769
+ { name: "deadline", type: "uint256" }
770
+ ],
771
+ Revoke: [
772
+ { name: "owner", type: "address" },
773
+ { name: "index", type: "uint256" },
774
+ { name: "reason", type: "string" },
775
+ { name: "nonce", type: "uint256" },
776
+ { name: "deadline", type: "uint256" }
777
+ ],
778
+ SetRecord: [
779
+ { name: "owner", type: "address" },
780
+ { name: "index", type: "uint256" },
781
+ { name: "kind", type: "string" },
782
+ { name: "value", type: "string" },
783
+ { name: "nonce", type: "uint256" },
784
+ { name: "deadline", type: "uint256" }
785
+ ],
786
+ MarkCompromised: [
787
+ { name: "owner", type: "address" },
788
+ { name: "index", type: "uint256" },
789
+ { name: "nonce", type: "uint256" },
790
+ { name: "deadline", type: "uint256" }
791
+ ]
792
+ };
793
+ var REVOKE_REASONS = ["", "compromised", "retired", "superseded", "other"];
794
+ var OWNER_REVOKE_REASONS = ["", "compromised", "retired", "other"];
795
+ function payloadBytes(v) {
796
+ return /^0x([0-9a-fA-F]{2})*$/.test(v) ? v : _viem.stringToHex.call(void 0, v);
797
+ }
798
+ function attestTypedData(chainId, registry, a) {
799
+ return {
800
+ domain: registryDomain(chainId, registry),
801
+ types: { Attest: AUTHORIZATION_TYPES.Attest },
802
+ primaryType: "Attest",
803
+ message: {
804
+ owner: a.owner,
805
+ fingerprint: fingerprintToBytes(a.fingerprint),
806
+ signature: payloadBytes(a.signature),
807
+ key: payloadBytes(a.key),
808
+ nonce: a.nonce,
809
+ deadline: a.deadline
810
+ }
811
+ };
812
+ }
813
+ function reattestTypedData(chainId, registry, a) {
814
+ return {
815
+ domain: registryDomain(chainId, registry),
816
+ types: { Reattest: AUTHORIZATION_TYPES.Reattest },
817
+ primaryType: "Reattest",
818
+ message: {
819
+ owner: a.owner,
820
+ revokeIndex: a.revokeIndex,
821
+ fingerprint: fingerprintToBytes(a.fingerprint),
822
+ signature: payloadBytes(a.signature),
823
+ key: payloadBytes(a.key),
824
+ keepRecords: a.keepRecords,
825
+ nonce: a.nonce,
826
+ deadline: a.deadline
827
+ }
828
+ };
829
+ }
830
+ function updateKeyTypedData(chainId, registry, a) {
831
+ return {
832
+ domain: registryDomain(chainId, registry),
833
+ types: { UpdateKey: AUTHORIZATION_TYPES.UpdateKey },
834
+ primaryType: "UpdateKey",
835
+ message: { owner: a.owner, index: a.index, key: payloadBytes(a.key), nonce: a.nonce, deadline: a.deadline }
836
+ };
837
+ }
838
+ function revokeTypedData(chainId, registry, a) {
839
+ return {
840
+ domain: registryDomain(chainId, registry),
841
+ types: { Revoke: AUTHORIZATION_TYPES.Revoke },
842
+ primaryType: "Revoke",
843
+ message: { owner: a.owner, index: a.index, reason: _nullishCoalesce(a.reason, () => ( "")), nonce: a.nonce, deadline: a.deadline }
844
+ };
845
+ }
846
+ function setRecordTypedData(chainId, registry, a) {
847
+ return {
848
+ domain: registryDomain(chainId, registry),
849
+ types: { SetRecord: AUTHORIZATION_TYPES.SetRecord },
850
+ primaryType: "SetRecord",
851
+ message: { owner: a.owner, index: a.index, kind: a.kind, value: a.value, nonce: a.nonce, deadline: a.deadline }
852
+ };
853
+ }
854
+ function markCompromisedTypedData(chainId, registry, a) {
855
+ return {
856
+ domain: registryDomain(chainId, registry),
857
+ types: { MarkCompromised: AUTHORIZATION_TYPES.MarkCompromised },
858
+ primaryType: "MarkCompromised",
859
+ message: { owner: a.owner, index: a.index, nonce: a.nonce, deadline: a.deadline }
860
+ };
861
+ }
862
+ function authorizationDigest(typedData) {
863
+ return _viem.hashTypedData.call(void 0, typedData);
864
+ }
865
+ function recordKind(name) {
866
+ return _viem.keccak256.call(void 0, _viem.stringToHex.call(void 0, name));
867
+ }
868
+
869
+ // src/core/contract.ts
870
+ var _chains = require('viem/chains');
871
+
872
+ // src/core/abi.ts
873
+ var REGISTRY_ABI = [
874
+ {
875
+ "type": "function",
876
+ "name": "ATTEST_TYPEHASH",
877
+ "inputs": [],
878
+ "outputs": [
879
+ {
880
+ "name": "",
881
+ "type": "bytes32",
882
+ "internalType": "bytes32"
883
+ }
884
+ ],
885
+ "stateMutability": "view"
886
+ },
887
+ {
888
+ "type": "function",
889
+ "name": "DOMAIN_SEPARATOR",
890
+ "inputs": [],
891
+ "outputs": [
892
+ {
893
+ "name": "",
894
+ "type": "bytes32",
895
+ "internalType": "bytes32"
896
+ }
897
+ ],
898
+ "stateMutability": "view"
899
+ },
900
+ {
901
+ "type": "function",
902
+ "name": "EIP712_NAME",
903
+ "inputs": [],
904
+ "outputs": [
905
+ {
906
+ "name": "",
907
+ "type": "string",
908
+ "internalType": "string"
909
+ }
910
+ ],
911
+ "stateMutability": "view"
912
+ },
913
+ {
914
+ "type": "function",
915
+ "name": "EIP712_VERSION",
916
+ "inputs": [],
917
+ "outputs": [
918
+ {
919
+ "name": "",
920
+ "type": "string",
921
+ "internalType": "string"
922
+ }
923
+ ],
924
+ "stateMutability": "view"
925
+ },
926
+ {
927
+ "type": "function",
928
+ "name": "EPOCH",
929
+ "inputs": [],
930
+ "outputs": [
931
+ {
932
+ "name": "",
933
+ "type": "uint256",
934
+ "internalType": "uint256"
935
+ }
936
+ ],
937
+ "stateMutability": "view"
938
+ },
939
+ {
940
+ "type": "function",
941
+ "name": "MARK_COMPROMISED_TYPEHASH",
942
+ "inputs": [],
943
+ "outputs": [
944
+ {
945
+ "name": "",
946
+ "type": "bytes32",
947
+ "internalType": "bytes32"
948
+ }
949
+ ],
950
+ "stateMutability": "view"
951
+ },
952
+ {
953
+ "type": "function",
954
+ "name": "MAX_CLAIMS_PER_OWNER",
955
+ "inputs": [],
956
+ "outputs": [
957
+ {
958
+ "name": "",
959
+ "type": "uint256",
960
+ "internalType": "uint256"
961
+ }
962
+ ],
963
+ "stateMutability": "view"
964
+ },
965
+ {
966
+ "type": "function",
967
+ "name": "MAX_KEY_BYTES",
968
+ "inputs": [],
969
+ "outputs": [
970
+ {
971
+ "name": "",
972
+ "type": "uint256",
973
+ "internalType": "uint256"
974
+ }
975
+ ],
976
+ "stateMutability": "view"
977
+ },
978
+ {
979
+ "type": "function",
980
+ "name": "MAX_KIND_BYTES",
981
+ "inputs": [],
982
+ "outputs": [
983
+ {
984
+ "name": "",
985
+ "type": "uint256",
986
+ "internalType": "uint256"
987
+ }
988
+ ],
989
+ "stateMutability": "view"
990
+ },
991
+ {
992
+ "type": "function",
993
+ "name": "MAX_PAYLOAD_BYTES",
994
+ "inputs": [],
995
+ "outputs": [
996
+ {
997
+ "name": "",
998
+ "type": "uint256",
999
+ "internalType": "uint256"
1000
+ }
1001
+ ],
1002
+ "stateMutability": "view"
1003
+ },
1004
+ {
1005
+ "type": "function",
1006
+ "name": "MAX_RECORD_BYTES",
1007
+ "inputs": [],
1008
+ "outputs": [
1009
+ {
1010
+ "name": "",
1011
+ "type": "uint256",
1012
+ "internalType": "uint256"
1013
+ }
1014
+ ],
1015
+ "stateMutability": "view"
1016
+ },
1017
+ {
1018
+ "type": "function",
1019
+ "name": "MAX_SIGNATURE_BYTES",
1020
+ "inputs": [],
1021
+ "outputs": [
1022
+ {
1023
+ "name": "",
1024
+ "type": "uint256",
1025
+ "internalType": "uint256"
1026
+ }
1027
+ ],
1028
+ "stateMutability": "view"
1029
+ },
1030
+ {
1031
+ "type": "function",
1032
+ "name": "MESSAGE_CLEARSIGNED",
1033
+ "inputs": [],
1034
+ "outputs": [
1035
+ {
1036
+ "name": "",
1037
+ "type": "uint8",
1038
+ "internalType": "uint8"
1039
+ }
1040
+ ],
1041
+ "stateMutability": "view"
1042
+ },
1043
+ {
1044
+ "type": "function",
1045
+ "name": "MESSAGE_DETACHED",
1046
+ "inputs": [],
1047
+ "outputs": [
1048
+ {
1049
+ "name": "",
1050
+ "type": "uint8",
1051
+ "internalType": "uint8"
1052
+ }
1053
+ ],
1054
+ "stateMutability": "view"
1055
+ },
1056
+ {
1057
+ "type": "function",
1058
+ "name": "REATTEST_TYPEHASH",
1059
+ "inputs": [],
1060
+ "outputs": [
1061
+ {
1062
+ "name": "",
1063
+ "type": "bytes32",
1064
+ "internalType": "bytes32"
1065
+ }
1066
+ ],
1067
+ "stateMutability": "view"
1068
+ },
1069
+ {
1070
+ "type": "function",
1071
+ "name": "REVOKE_TYPEHASH",
1072
+ "inputs": [],
1073
+ "outputs": [
1074
+ {
1075
+ "name": "",
1076
+ "type": "bytes32",
1077
+ "internalType": "bytes32"
1078
+ }
1079
+ ],
1080
+ "stateMutability": "view"
1081
+ },
1082
+ {
1083
+ "type": "function",
1084
+ "name": "SET_RECORD_TYPEHASH",
1085
+ "inputs": [],
1086
+ "outputs": [
1087
+ {
1088
+ "name": "",
1089
+ "type": "bytes32",
1090
+ "internalType": "bytes32"
1091
+ }
1092
+ ],
1093
+ "stateMutability": "view"
1094
+ },
1095
+ {
1096
+ "type": "function",
1097
+ "name": "UPDATE_KEY_TYPEHASH",
1098
+ "inputs": [],
1099
+ "outputs": [
1100
+ {
1101
+ "name": "",
1102
+ "type": "bytes32",
1103
+ "internalType": "bytes32"
1104
+ }
1105
+ ],
1106
+ "stateMutability": "view"
1107
+ },
1108
+ {
1109
+ "type": "function",
1110
+ "name": "VERSION",
1111
+ "inputs": [],
1112
+ "outputs": [
1113
+ {
1114
+ "name": "",
1115
+ "type": "uint8",
1116
+ "internalType": "uint8"
1117
+ }
1118
+ ],
1119
+ "stateMutability": "view"
1120
+ },
1121
+ {
1122
+ "type": "function",
1123
+ "name": "armorToBytes",
1124
+ "inputs": [
1125
+ {
1126
+ "name": "armored",
1127
+ "type": "string",
1128
+ "internalType": "string"
1129
+ }
1130
+ ],
1131
+ "outputs": [
1132
+ {
1133
+ "name": "",
1134
+ "type": "bytes",
1135
+ "internalType": "bytes"
1136
+ }
1137
+ ],
1138
+ "stateMutability": "pure"
1139
+ },
1140
+ {
1141
+ "type": "function",
1142
+ "name": "armoredKey",
1143
+ "inputs": [
1144
+ {
1145
+ "name": "owner",
1146
+ "type": "address",
1147
+ "internalType": "address"
1148
+ },
1149
+ {
1150
+ "name": "index",
1151
+ "type": "uint256",
1152
+ "internalType": "uint256"
1153
+ }
1154
+ ],
1155
+ "outputs": [
1156
+ {
1157
+ "name": "",
1158
+ "type": "string",
1159
+ "internalType": "string"
1160
+ }
1161
+ ],
1162
+ "stateMutability": "view"
1163
+ },
1164
+ {
1165
+ "type": "function",
1166
+ "name": "attest",
1167
+ "inputs": [
1168
+ {
1169
+ "name": "fingerprint",
1170
+ "type": "bytes",
1171
+ "internalType": "bytes"
1172
+ },
1173
+ {
1174
+ "name": "signature",
1175
+ "type": "bytes",
1176
+ "internalType": "bytes"
1177
+ },
1178
+ {
1179
+ "name": "key",
1180
+ "type": "bytes",
1181
+ "internalType": "bytes"
1182
+ }
1183
+ ],
1184
+ "outputs": [
1185
+ {
1186
+ "name": "index",
1187
+ "type": "uint256",
1188
+ "internalType": "uint256"
1189
+ }
1190
+ ],
1191
+ "stateMutability": "nonpayable"
1192
+ },
1193
+ {
1194
+ "type": "function",
1195
+ "name": "attestFor",
1196
+ "inputs": [
1197
+ {
1198
+ "name": "owner",
1199
+ "type": "address",
1200
+ "internalType": "address"
1201
+ },
1202
+ {
1203
+ "name": "fingerprint",
1204
+ "type": "bytes",
1205
+ "internalType": "bytes"
1206
+ },
1207
+ {
1208
+ "name": "signature",
1209
+ "type": "bytes",
1210
+ "internalType": "bytes"
1211
+ },
1212
+ {
1213
+ "name": "key",
1214
+ "type": "bytes",
1215
+ "internalType": "bytes"
1216
+ },
1217
+ {
1218
+ "name": "deadline",
1219
+ "type": "uint256",
1220
+ "internalType": "uint256"
1221
+ },
1222
+ {
1223
+ "name": "permission",
1224
+ "type": "bytes",
1225
+ "internalType": "bytes"
1226
+ }
1227
+ ],
1228
+ "outputs": [
1229
+ {
1230
+ "name": "index",
1231
+ "type": "uint256",
1232
+ "internalType": "uint256"
1233
+ }
1234
+ ],
1235
+ "stateMutability": "nonpayable"
1236
+ },
1237
+ {
1238
+ "type": "function",
1239
+ "name": "cancelAuthorization",
1240
+ "inputs": [],
1241
+ "outputs": [],
1242
+ "stateMutability": "nonpayable"
1243
+ },
1244
+ {
1245
+ "type": "function",
1246
+ "name": "claim",
1247
+ "inputs": [
1248
+ {
1249
+ "name": "owner",
1250
+ "type": "address",
1251
+ "internalType": "address"
1252
+ },
1253
+ {
1254
+ "name": "index",
1255
+ "type": "uint256",
1256
+ "internalType": "uint256"
1257
+ }
1258
+ ],
1259
+ "outputs": [
1260
+ {
1261
+ "name": "details",
1262
+ "type": "tuple",
1263
+ "internalType": "struct PGPRegistry.ClaimView",
1264
+ "components": [
1265
+ {
1266
+ "name": "index",
1267
+ "type": "uint256",
1268
+ "internalType": "uint256"
1269
+ },
1270
+ {
1271
+ "name": "fingerprint",
1272
+ "type": "bytes",
1273
+ "internalType": "bytes"
1274
+ },
1275
+ {
1276
+ "name": "createdAt",
1277
+ "type": "uint64",
1278
+ "internalType": "uint64"
1279
+ },
1280
+ {
1281
+ "name": "revokedAt",
1282
+ "type": "uint64",
1283
+ "internalType": "uint64"
1284
+ },
1285
+ {
1286
+ "name": "state",
1287
+ "type": "string",
1288
+ "internalType": "string"
1289
+ },
1290
+ {
1291
+ "name": "replacedBy",
1292
+ "type": "uint256",
1293
+ "internalType": "uint256"
1294
+ },
1295
+ {
1296
+ "name": "revokeReason",
1297
+ "type": "string",
1298
+ "internalType": "string"
1299
+ },
1300
+ {
1301
+ "name": "messageVersion",
1302
+ "type": "uint8",
1303
+ "internalType": "uint8"
1304
+ }
1305
+ ]
1306
+ },
1307
+ {
1308
+ "name": "key",
1309
+ "type": "string",
1310
+ "internalType": "string"
1311
+ },
1312
+ {
1313
+ "name": "statement",
1314
+ "type": "string",
1315
+ "internalType": "string"
1316
+ },
1317
+ {
1318
+ "name": "recordKinds",
1319
+ "type": "string[]",
1320
+ "internalType": "string[]"
1321
+ }
1322
+ ],
1323
+ "stateMutability": "view"
1324
+ },
1325
+ {
1326
+ "type": "function",
1327
+ "name": "claimCount",
1328
+ "inputs": [
1329
+ {
1330
+ "name": "owner",
1331
+ "type": "address",
1332
+ "internalType": "address"
1333
+ }
1334
+ ],
1335
+ "outputs": [
1336
+ {
1337
+ "name": "",
1338
+ "type": "uint256",
1339
+ "internalType": "uint256"
1340
+ }
1341
+ ],
1342
+ "stateMutability": "view"
1343
+ },
1344
+ {
1345
+ "type": "function",
1346
+ "name": "claimsOf",
1347
+ "inputs": [
1348
+ {
1349
+ "name": "owner",
1350
+ "type": "address",
1351
+ "internalType": "address"
1352
+ }
1353
+ ],
1354
+ "outputs": [
1355
+ {
1356
+ "name": "out",
1357
+ "type": "tuple[]",
1358
+ "internalType": "struct PGPRegistry.ClaimView[]",
1359
+ "components": [
1360
+ {
1361
+ "name": "index",
1362
+ "type": "uint256",
1363
+ "internalType": "uint256"
1364
+ },
1365
+ {
1366
+ "name": "fingerprint",
1367
+ "type": "bytes",
1368
+ "internalType": "bytes"
1369
+ },
1370
+ {
1371
+ "name": "createdAt",
1372
+ "type": "uint64",
1373
+ "internalType": "uint64"
1374
+ },
1375
+ {
1376
+ "name": "revokedAt",
1377
+ "type": "uint64",
1378
+ "internalType": "uint64"
1379
+ },
1380
+ {
1381
+ "name": "state",
1382
+ "type": "string",
1383
+ "internalType": "string"
1384
+ },
1385
+ {
1386
+ "name": "replacedBy",
1387
+ "type": "uint256",
1388
+ "internalType": "uint256"
1389
+ },
1390
+ {
1391
+ "name": "revokeReason",
1392
+ "type": "string",
1393
+ "internalType": "string"
1394
+ },
1395
+ {
1396
+ "name": "messageVersion",
1397
+ "type": "uint8",
1398
+ "internalType": "uint8"
1399
+ }
1400
+ ]
1401
+ }
1402
+ ],
1403
+ "stateMutability": "view"
1404
+ },
1405
+ {
1406
+ "type": "function",
1407
+ "name": "claimsOfRange",
1408
+ "inputs": [
1409
+ {
1410
+ "name": "owner",
1411
+ "type": "address",
1412
+ "internalType": "address"
1413
+ },
1414
+ {
1415
+ "name": "start",
1416
+ "type": "uint256",
1417
+ "internalType": "uint256"
1418
+ },
1419
+ {
1420
+ "name": "count",
1421
+ "type": "uint256",
1422
+ "internalType": "uint256"
1423
+ }
1424
+ ],
1425
+ "outputs": [
1426
+ {
1427
+ "name": "out",
1428
+ "type": "tuple[]",
1429
+ "internalType": "struct PGPRegistry.ClaimView[]",
1430
+ "components": [
1431
+ {
1432
+ "name": "index",
1433
+ "type": "uint256",
1434
+ "internalType": "uint256"
1435
+ },
1436
+ {
1437
+ "name": "fingerprint",
1438
+ "type": "bytes",
1439
+ "internalType": "bytes"
1440
+ },
1441
+ {
1442
+ "name": "createdAt",
1443
+ "type": "uint64",
1444
+ "internalType": "uint64"
1445
+ },
1446
+ {
1447
+ "name": "revokedAt",
1448
+ "type": "uint64",
1449
+ "internalType": "uint64"
1450
+ },
1451
+ {
1452
+ "name": "state",
1453
+ "type": "string",
1454
+ "internalType": "string"
1455
+ },
1456
+ {
1457
+ "name": "replacedBy",
1458
+ "type": "uint256",
1459
+ "internalType": "uint256"
1460
+ },
1461
+ {
1462
+ "name": "revokeReason",
1463
+ "type": "string",
1464
+ "internalType": "string"
1465
+ },
1466
+ {
1467
+ "name": "messageVersion",
1468
+ "type": "uint8",
1469
+ "internalType": "uint8"
1470
+ }
1471
+ ]
1472
+ }
1473
+ ],
1474
+ "stateMutability": "view"
1475
+ },
1476
+ {
1477
+ "type": "function",
1478
+ "name": "clearsigned",
1479
+ "inputs": [
1480
+ {
1481
+ "name": "owner",
1482
+ "type": "address",
1483
+ "internalType": "address"
1484
+ },
1485
+ {
1486
+ "name": "index",
1487
+ "type": "uint256",
1488
+ "internalType": "uint256"
1489
+ }
1490
+ ],
1491
+ "outputs": [
1492
+ {
1493
+ "name": "",
1494
+ "type": "string",
1495
+ "internalType": "string"
1496
+ }
1497
+ ],
1498
+ "stateMutability": "view"
1499
+ },
1500
+ {
1501
+ "type": "function",
1502
+ "name": "current",
1503
+ "inputs": [
1504
+ {
1505
+ "name": "owner",
1506
+ "type": "address",
1507
+ "internalType": "address"
1508
+ }
1509
+ ],
1510
+ "outputs": [
1511
+ {
1512
+ "name": "found",
1513
+ "type": "bool",
1514
+ "internalType": "bool"
1515
+ },
1516
+ {
1517
+ "name": "index",
1518
+ "type": "uint256",
1519
+ "internalType": "uint256"
1520
+ },
1521
+ {
1522
+ "name": "details",
1523
+ "type": "tuple",
1524
+ "internalType": "struct PGPRegistry.ClaimView",
1525
+ "components": [
1526
+ {
1527
+ "name": "index",
1528
+ "type": "uint256",
1529
+ "internalType": "uint256"
1530
+ },
1531
+ {
1532
+ "name": "fingerprint",
1533
+ "type": "bytes",
1534
+ "internalType": "bytes"
1535
+ },
1536
+ {
1537
+ "name": "createdAt",
1538
+ "type": "uint64",
1539
+ "internalType": "uint64"
1540
+ },
1541
+ {
1542
+ "name": "revokedAt",
1543
+ "type": "uint64",
1544
+ "internalType": "uint64"
1545
+ },
1546
+ {
1547
+ "name": "state",
1548
+ "type": "string",
1549
+ "internalType": "string"
1550
+ },
1551
+ {
1552
+ "name": "replacedBy",
1553
+ "type": "uint256",
1554
+ "internalType": "uint256"
1555
+ },
1556
+ {
1557
+ "name": "revokeReason",
1558
+ "type": "string",
1559
+ "internalType": "string"
1560
+ },
1561
+ {
1562
+ "name": "messageVersion",
1563
+ "type": "uint8",
1564
+ "internalType": "uint8"
1565
+ }
1566
+ ]
1567
+ }
1568
+ ],
1569
+ "stateMutability": "view"
1570
+ },
1571
+ {
1572
+ "type": "function",
1573
+ "name": "eip712Domain",
1574
+ "inputs": [],
1575
+ "outputs": [
1576
+ {
1577
+ "name": "fields",
1578
+ "type": "bytes1",
1579
+ "internalType": "bytes1"
1580
+ },
1581
+ {
1582
+ "name": "name",
1583
+ "type": "string",
1584
+ "internalType": "string"
1585
+ },
1586
+ {
1587
+ "name": "version",
1588
+ "type": "string",
1589
+ "internalType": "string"
1590
+ },
1591
+ {
1592
+ "name": "chainId",
1593
+ "type": "uint256",
1594
+ "internalType": "uint256"
1595
+ },
1596
+ {
1597
+ "name": "verifyingContract",
1598
+ "type": "address",
1599
+ "internalType": "address"
1600
+ },
1601
+ {
1602
+ "name": "salt",
1603
+ "type": "bytes32",
1604
+ "internalType": "bytes32"
1605
+ },
1606
+ {
1607
+ "name": "extensions",
1608
+ "type": "uint256[]",
1609
+ "internalType": "uint256[]"
1610
+ }
1611
+ ],
1612
+ "stateMutability": "view"
1613
+ },
1614
+ {
1615
+ "type": "function",
1616
+ "name": "fingerprintsForKeyId",
1617
+ "inputs": [
1618
+ {
1619
+ "name": "keyId",
1620
+ "type": "bytes8",
1621
+ "internalType": "bytes8"
1622
+ }
1623
+ ],
1624
+ "outputs": [
1625
+ {
1626
+ "name": "",
1627
+ "type": "bytes[]",
1628
+ "internalType": "bytes[]"
1629
+ }
1630
+ ],
1631
+ "stateMutability": "view"
1632
+ },
1633
+ {
1634
+ "type": "function",
1635
+ "name": "fingerprintsForKeyIdCount",
1636
+ "inputs": [
1637
+ {
1638
+ "name": "keyId",
1639
+ "type": "bytes8",
1640
+ "internalType": "bytes8"
1641
+ }
1642
+ ],
1643
+ "outputs": [
1644
+ {
1645
+ "name": "",
1646
+ "type": "uint256",
1647
+ "internalType": "uint256"
1648
+ }
1649
+ ],
1650
+ "stateMutability": "view"
1651
+ },
1652
+ {
1653
+ "type": "function",
1654
+ "name": "fingerprintsForKeyIdRange",
1655
+ "inputs": [
1656
+ {
1657
+ "name": "keyId",
1658
+ "type": "bytes8",
1659
+ "internalType": "bytes8"
1660
+ },
1661
+ {
1662
+ "name": "start",
1663
+ "type": "uint256",
1664
+ "internalType": "uint256"
1665
+ },
1666
+ {
1667
+ "name": "count",
1668
+ "type": "uint256",
1669
+ "internalType": "uint256"
1670
+ }
1671
+ ],
1672
+ "outputs": [
1673
+ {
1674
+ "name": "out",
1675
+ "type": "bytes[]",
1676
+ "internalType": "bytes[]"
1677
+ }
1678
+ ],
1679
+ "stateMutability": "view"
1680
+ },
1681
+ {
1682
+ "type": "function",
1683
+ "name": "keyBytes",
1684
+ "inputs": [
1685
+ {
1686
+ "name": "owner",
1687
+ "type": "address",
1688
+ "internalType": "address"
1689
+ },
1690
+ {
1691
+ "name": "index",
1692
+ "type": "uint256",
1693
+ "internalType": "uint256"
1694
+ }
1695
+ ],
1696
+ "outputs": [
1697
+ {
1698
+ "name": "key",
1699
+ "type": "bytes",
1700
+ "internalType": "bytes"
1701
+ }
1702
+ ],
1703
+ "stateMutability": "view"
1704
+ },
1705
+ {
1706
+ "type": "function",
1707
+ "name": "keyStatus",
1708
+ "inputs": [
1709
+ {
1710
+ "name": "owner",
1711
+ "type": "address",
1712
+ "internalType": "address"
1713
+ },
1714
+ {
1715
+ "name": "fingerprint",
1716
+ "type": "bytes",
1717
+ "internalType": "bytes"
1718
+ }
1719
+ ],
1720
+ "outputs": [
1721
+ {
1722
+ "name": "",
1723
+ "type": "string",
1724
+ "internalType": "string"
1725
+ }
1726
+ ],
1727
+ "stateMutability": "view"
1728
+ },
1729
+ {
1730
+ "type": "function",
1731
+ "name": "markCompromisedFor",
1732
+ "inputs": [
1733
+ {
1734
+ "name": "owner",
1735
+ "type": "address",
1736
+ "internalType": "address"
1737
+ },
1738
+ {
1739
+ "name": "index",
1740
+ "type": "uint256",
1741
+ "internalType": "uint256"
1742
+ },
1743
+ {
1744
+ "name": "deadline",
1745
+ "type": "uint256",
1746
+ "internalType": "uint256"
1747
+ },
1748
+ {
1749
+ "name": "permission",
1750
+ "type": "bytes",
1751
+ "internalType": "bytes"
1752
+ }
1753
+ ],
1754
+ "outputs": [],
1755
+ "stateMutability": "nonpayable"
1756
+ },
1757
+ {
1758
+ "type": "function",
1759
+ "name": "multicall",
1760
+ "inputs": [
1761
+ {
1762
+ "name": "calls",
1763
+ "type": "bytes[]",
1764
+ "internalType": "bytes[]"
1765
+ }
1766
+ ],
1767
+ "outputs": [
1768
+ {
1769
+ "name": "results",
1770
+ "type": "bytes[]",
1771
+ "internalType": "bytes[]"
1772
+ }
1773
+ ],
1774
+ "stateMutability": "nonpayable"
1775
+ },
1776
+ {
1777
+ "type": "function",
1778
+ "name": "nonces",
1779
+ "inputs": [
1780
+ {
1781
+ "name": "owner",
1782
+ "type": "address",
1783
+ "internalType": "address"
1784
+ }
1785
+ ],
1786
+ "outputs": [
1787
+ {
1788
+ "name": "",
1789
+ "type": "uint256",
1790
+ "internalType": "uint256"
1791
+ }
1792
+ ],
1793
+ "stateMutability": "view"
1794
+ },
1795
+ {
1796
+ "type": "function",
1797
+ "name": "ownersOf",
1798
+ "inputs": [
1799
+ {
1800
+ "name": "fingerprint",
1801
+ "type": "bytes",
1802
+ "internalType": "bytes"
1803
+ }
1804
+ ],
1805
+ "outputs": [
1806
+ {
1807
+ "name": "",
1808
+ "type": "address[]",
1809
+ "internalType": "address[]"
1810
+ }
1811
+ ],
1812
+ "stateMutability": "view"
1813
+ },
1814
+ {
1815
+ "type": "function",
1816
+ "name": "ownersOfCount",
1817
+ "inputs": [
1818
+ {
1819
+ "name": "fingerprint",
1820
+ "type": "bytes",
1821
+ "internalType": "bytes"
1822
+ }
1823
+ ],
1824
+ "outputs": [
1825
+ {
1826
+ "name": "",
1827
+ "type": "uint256",
1828
+ "internalType": "uint256"
1829
+ }
1830
+ ],
1831
+ "stateMutability": "view"
1832
+ },
1833
+ {
1834
+ "type": "function",
1835
+ "name": "ownersOfRange",
1836
+ "inputs": [
1837
+ {
1838
+ "name": "fingerprint",
1839
+ "type": "bytes",
1840
+ "internalType": "bytes"
1841
+ },
1842
+ {
1843
+ "name": "start",
1844
+ "type": "uint256",
1845
+ "internalType": "uint256"
1846
+ },
1847
+ {
1848
+ "name": "count",
1849
+ "type": "uint256",
1850
+ "internalType": "uint256"
1851
+ }
1852
+ ],
1853
+ "outputs": [
1854
+ {
1855
+ "name": "out",
1856
+ "type": "address[]",
1857
+ "internalType": "address[]"
1858
+ }
1859
+ ],
1860
+ "stateMutability": "view"
1861
+ },
1862
+ {
1863
+ "type": "function",
1864
+ "name": "reattest",
1865
+ "inputs": [
1866
+ {
1867
+ "name": "revokeIndex",
1868
+ "type": "uint256",
1869
+ "internalType": "uint256"
1870
+ },
1871
+ {
1872
+ "name": "fingerprint",
1873
+ "type": "bytes",
1874
+ "internalType": "bytes"
1875
+ },
1876
+ {
1877
+ "name": "signature",
1878
+ "type": "bytes",
1879
+ "internalType": "bytes"
1880
+ },
1881
+ {
1882
+ "name": "key",
1883
+ "type": "bytes",
1884
+ "internalType": "bytes"
1885
+ },
1886
+ {
1887
+ "name": "keepRecords",
1888
+ "type": "bool",
1889
+ "internalType": "bool"
1890
+ }
1891
+ ],
1892
+ "outputs": [
1893
+ {
1894
+ "name": "index",
1895
+ "type": "uint256",
1896
+ "internalType": "uint256"
1897
+ }
1898
+ ],
1899
+ "stateMutability": "nonpayable"
1900
+ },
1901
+ {
1902
+ "type": "function",
1903
+ "name": "reattestFor",
1904
+ "inputs": [
1905
+ {
1906
+ "name": "owner",
1907
+ "type": "address",
1908
+ "internalType": "address"
1909
+ },
1910
+ {
1911
+ "name": "revokeIndex",
1912
+ "type": "uint256",
1913
+ "internalType": "uint256"
1914
+ },
1915
+ {
1916
+ "name": "fingerprint",
1917
+ "type": "bytes",
1918
+ "internalType": "bytes"
1919
+ },
1920
+ {
1921
+ "name": "signature",
1922
+ "type": "bytes",
1923
+ "internalType": "bytes"
1924
+ },
1925
+ {
1926
+ "name": "key",
1927
+ "type": "bytes",
1928
+ "internalType": "bytes"
1929
+ },
1930
+ {
1931
+ "name": "keepRecords",
1932
+ "type": "bool",
1933
+ "internalType": "bool"
1934
+ },
1935
+ {
1936
+ "name": "deadline",
1937
+ "type": "uint256",
1938
+ "internalType": "uint256"
1939
+ },
1940
+ {
1941
+ "name": "permission",
1942
+ "type": "bytes",
1943
+ "internalType": "bytes"
1944
+ }
1945
+ ],
1946
+ "outputs": [
1947
+ {
1948
+ "name": "index",
1949
+ "type": "uint256",
1950
+ "internalType": "uint256"
1951
+ }
1952
+ ],
1953
+ "stateMutability": "nonpayable"
1954
+ },
1955
+ {
1956
+ "type": "function",
1957
+ "name": "recordText",
1958
+ "inputs": [
1959
+ {
1960
+ "name": "owner",
1961
+ "type": "address",
1962
+ "internalType": "address"
1963
+ },
1964
+ {
1965
+ "name": "index",
1966
+ "type": "uint256",
1967
+ "internalType": "uint256"
1968
+ },
1969
+ {
1970
+ "name": "kind",
1971
+ "type": "string",
1972
+ "internalType": "string"
1973
+ }
1974
+ ],
1975
+ "outputs": [
1976
+ {
1977
+ "name": "",
1978
+ "type": "string",
1979
+ "internalType": "string"
1980
+ }
1981
+ ],
1982
+ "stateMutability": "view"
1983
+ },
1984
+ {
1985
+ "type": "function",
1986
+ "name": "recordsOf",
1987
+ "inputs": [
1988
+ {
1989
+ "name": "owner",
1990
+ "type": "address",
1991
+ "internalType": "address"
1992
+ },
1993
+ {
1994
+ "name": "index",
1995
+ "type": "uint256",
1996
+ "internalType": "uint256"
1997
+ }
1998
+ ],
1999
+ "outputs": [
2000
+ {
2001
+ "name": "kinds",
2002
+ "type": "string[]",
2003
+ "internalType": "string[]"
2004
+ },
2005
+ {
2006
+ "name": "values",
2007
+ "type": "string[]",
2008
+ "internalType": "string[]"
2009
+ }
2010
+ ],
2011
+ "stateMutability": "view"
2012
+ },
2013
+ {
2014
+ "type": "function",
2015
+ "name": "revoke",
2016
+ "inputs": [
2017
+ {
2018
+ "name": "index",
2019
+ "type": "uint256",
2020
+ "internalType": "uint256"
2021
+ },
2022
+ {
2023
+ "name": "reason",
2024
+ "type": "string",
2025
+ "internalType": "string"
2026
+ }
2027
+ ],
2028
+ "outputs": [],
2029
+ "stateMutability": "nonpayable"
2030
+ },
2031
+ {
2032
+ "type": "function",
2033
+ "name": "revokeFor",
2034
+ "inputs": [
2035
+ {
2036
+ "name": "owner",
2037
+ "type": "address",
2038
+ "internalType": "address"
2039
+ },
2040
+ {
2041
+ "name": "index",
2042
+ "type": "uint256",
2043
+ "internalType": "uint256"
2044
+ },
2045
+ {
2046
+ "name": "reason",
2047
+ "type": "string",
2048
+ "internalType": "string"
2049
+ },
2050
+ {
2051
+ "name": "deadline",
2052
+ "type": "uint256",
2053
+ "internalType": "uint256"
2054
+ },
2055
+ {
2056
+ "name": "permission",
2057
+ "type": "bytes",
2058
+ "internalType": "bytes"
2059
+ }
2060
+ ],
2061
+ "outputs": [],
2062
+ "stateMutability": "nonpayable"
2063
+ },
2064
+ {
2065
+ "type": "function",
2066
+ "name": "setRecord",
2067
+ "inputs": [
2068
+ {
2069
+ "name": "index",
2070
+ "type": "uint256",
2071
+ "internalType": "uint256"
2072
+ },
2073
+ {
2074
+ "name": "kind",
2075
+ "type": "string",
2076
+ "internalType": "string"
2077
+ },
2078
+ {
2079
+ "name": "value",
2080
+ "type": "string",
2081
+ "internalType": "string"
2082
+ }
2083
+ ],
2084
+ "outputs": [],
2085
+ "stateMutability": "nonpayable"
2086
+ },
2087
+ {
2088
+ "type": "function",
2089
+ "name": "setRecordFor",
2090
+ "inputs": [
2091
+ {
2092
+ "name": "owner",
2093
+ "type": "address",
2094
+ "internalType": "address"
2095
+ },
2096
+ {
2097
+ "name": "index",
2098
+ "type": "uint256",
2099
+ "internalType": "uint256"
2100
+ },
2101
+ {
2102
+ "name": "kind",
2103
+ "type": "string",
2104
+ "internalType": "string"
2105
+ },
2106
+ {
2107
+ "name": "value",
2108
+ "type": "string",
2109
+ "internalType": "string"
2110
+ },
2111
+ {
2112
+ "name": "deadline",
2113
+ "type": "uint256",
2114
+ "internalType": "uint256"
2115
+ },
2116
+ {
2117
+ "name": "permission",
2118
+ "type": "bytes",
2119
+ "internalType": "bytes"
2120
+ }
2121
+ ],
2122
+ "outputs": [],
2123
+ "stateMutability": "nonpayable"
2124
+ },
2125
+ {
2126
+ "type": "function",
2127
+ "name": "signatureBytes",
2128
+ "inputs": [
2129
+ {
2130
+ "name": "owner",
2131
+ "type": "address",
2132
+ "internalType": "address"
2133
+ },
2134
+ {
2135
+ "name": "index",
2136
+ "type": "uint256",
2137
+ "internalType": "uint256"
2138
+ }
2139
+ ],
2140
+ "outputs": [
2141
+ {
2142
+ "name": "signature",
2143
+ "type": "bytes",
2144
+ "internalType": "bytes"
2145
+ }
2146
+ ],
2147
+ "stateMutability": "view"
2148
+ },
2149
+ {
2150
+ "type": "function",
2151
+ "name": "statementFor",
2152
+ "inputs": [
2153
+ {
2154
+ "name": "owner",
2155
+ "type": "address",
2156
+ "internalType": "address"
2157
+ }
2158
+ ],
2159
+ "outputs": [
2160
+ {
2161
+ "name": "",
2162
+ "type": "string",
2163
+ "internalType": "string"
2164
+ }
2165
+ ],
2166
+ "stateMutability": "pure"
2167
+ },
2168
+ {
2169
+ "type": "function",
2170
+ "name": "summary",
2171
+ "inputs": [
2172
+ {
2173
+ "name": "owner",
2174
+ "type": "address",
2175
+ "internalType": "address"
2176
+ }
2177
+ ],
2178
+ "outputs": [
2179
+ {
2180
+ "name": "total",
2181
+ "type": "uint256",
2182
+ "internalType": "uint256"
2183
+ },
2184
+ {
2185
+ "name": "active",
2186
+ "type": "uint256",
2187
+ "internalType": "uint256"
2188
+ },
2189
+ {
2190
+ "name": "hasCurrent",
2191
+ "type": "bool",
2192
+ "internalType": "bool"
2193
+ },
2194
+ {
2195
+ "name": "currentIndex",
2196
+ "type": "uint256",
2197
+ "internalType": "uint256"
2198
+ }
2199
+ ],
2200
+ "stateMutability": "view"
2201
+ },
2202
+ {
2203
+ "type": "function",
2204
+ "name": "updateKey",
2205
+ "inputs": [
2206
+ {
2207
+ "name": "index",
2208
+ "type": "uint256",
2209
+ "internalType": "uint256"
2210
+ },
2211
+ {
2212
+ "name": "key",
2213
+ "type": "bytes",
2214
+ "internalType": "bytes"
2215
+ }
2216
+ ],
2217
+ "outputs": [],
2218
+ "stateMutability": "nonpayable"
2219
+ },
2220
+ {
2221
+ "type": "function",
2222
+ "name": "updateKeyFor",
2223
+ "inputs": [
2224
+ {
2225
+ "name": "owner",
2226
+ "type": "address",
2227
+ "internalType": "address"
2228
+ },
2229
+ {
2230
+ "name": "index",
2231
+ "type": "uint256",
2232
+ "internalType": "uint256"
2233
+ },
2234
+ {
2235
+ "name": "key",
2236
+ "type": "bytes",
2237
+ "internalType": "bytes"
2238
+ },
2239
+ {
2240
+ "name": "deadline",
2241
+ "type": "uint256",
2242
+ "internalType": "uint256"
2243
+ },
2244
+ {
2245
+ "name": "permission",
2246
+ "type": "bytes",
2247
+ "internalType": "bytes"
2248
+ }
2249
+ ],
2250
+ "outputs": [],
2251
+ "stateMutability": "nonpayable"
2252
+ },
2253
+ {
2254
+ "type": "event",
2255
+ "name": "Attested",
2256
+ "inputs": [
2257
+ {
2258
+ "name": "owner",
2259
+ "type": "address",
2260
+ "indexed": true,
2261
+ "internalType": "address"
2262
+ },
2263
+ {
2264
+ "name": "fingerprintHash",
2265
+ "type": "bytes32",
2266
+ "indexed": true,
2267
+ "internalType": "bytes32"
2268
+ },
2269
+ {
2270
+ "name": "index",
2271
+ "type": "uint256",
2272
+ "indexed": true,
2273
+ "internalType": "uint256"
2274
+ },
2275
+ {
2276
+ "name": "fingerprint",
2277
+ "type": "bytes",
2278
+ "indexed": false,
2279
+ "internalType": "bytes"
2280
+ },
2281
+ {
2282
+ "name": "payload",
2283
+ "type": "address",
2284
+ "indexed": false,
2285
+ "internalType": "address"
2286
+ },
2287
+ {
2288
+ "name": "messageVersion",
2289
+ "type": "uint8",
2290
+ "indexed": false,
2291
+ "internalType": "uint8"
2292
+ },
2293
+ {
2294
+ "name": "submitter",
2295
+ "type": "address",
2296
+ "indexed": false,
2297
+ "internalType": "address"
2298
+ }
2299
+ ],
2300
+ "anonymous": false
2301
+ },
2302
+ {
2303
+ "type": "event",
2304
+ "name": "KeyUpdated",
2305
+ "inputs": [
2306
+ {
2307
+ "name": "owner",
2308
+ "type": "address",
2309
+ "indexed": true,
2310
+ "internalType": "address"
2311
+ },
2312
+ {
2313
+ "name": "fingerprintHash",
2314
+ "type": "bytes32",
2315
+ "indexed": true,
2316
+ "internalType": "bytes32"
2317
+ },
2318
+ {
2319
+ "name": "index",
2320
+ "type": "uint256",
2321
+ "indexed": true,
2322
+ "internalType": "uint256"
2323
+ },
2324
+ {
2325
+ "name": "oldPayload",
2326
+ "type": "address",
2327
+ "indexed": false,
2328
+ "internalType": "address"
2329
+ },
2330
+ {
2331
+ "name": "newPayload",
2332
+ "type": "address",
2333
+ "indexed": false,
2334
+ "internalType": "address"
2335
+ },
2336
+ {
2337
+ "name": "submitter",
2338
+ "type": "address",
2339
+ "indexed": false,
2340
+ "internalType": "address"
2341
+ }
2342
+ ],
2343
+ "anonymous": false
2344
+ },
2345
+ {
2346
+ "type": "event",
2347
+ "name": "NonceUsed",
2348
+ "inputs": [
2349
+ {
2350
+ "name": "owner",
2351
+ "type": "address",
2352
+ "indexed": true,
2353
+ "internalType": "address"
2354
+ },
2355
+ {
2356
+ "name": "nonce",
2357
+ "type": "uint256",
2358
+ "indexed": false,
2359
+ "internalType": "uint256"
2360
+ }
2361
+ ],
2362
+ "anonymous": false
2363
+ },
2364
+ {
2365
+ "type": "event",
2366
+ "name": "RecordSet",
2367
+ "inputs": [
2368
+ {
2369
+ "name": "owner",
2370
+ "type": "address",
2371
+ "indexed": true,
2372
+ "internalType": "address"
2373
+ },
2374
+ {
2375
+ "name": "index",
2376
+ "type": "uint256",
2377
+ "indexed": true,
2378
+ "internalType": "uint256"
2379
+ },
2380
+ {
2381
+ "name": "kindHash",
2382
+ "type": "bytes32",
2383
+ "indexed": true,
2384
+ "internalType": "bytes32"
2385
+ },
2386
+ {
2387
+ "name": "kind",
2388
+ "type": "string",
2389
+ "indexed": false,
2390
+ "internalType": "string"
2391
+ },
2392
+ {
2393
+ "name": "value",
2394
+ "type": "string",
2395
+ "indexed": false,
2396
+ "internalType": "string"
2397
+ },
2398
+ {
2399
+ "name": "submitter",
2400
+ "type": "address",
2401
+ "indexed": false,
2402
+ "internalType": "address"
2403
+ }
2404
+ ],
2405
+ "anonymous": false
2406
+ },
2407
+ {
2408
+ "type": "event",
2409
+ "name": "RecordsMoved",
2410
+ "inputs": [
2411
+ {
2412
+ "name": "owner",
2413
+ "type": "address",
2414
+ "indexed": true,
2415
+ "internalType": "address"
2416
+ },
2417
+ {
2418
+ "name": "fromIndex",
2419
+ "type": "uint256",
2420
+ "indexed": true,
2421
+ "internalType": "uint256"
2422
+ },
2423
+ {
2424
+ "name": "toIndex",
2425
+ "type": "uint256",
2426
+ "indexed": true,
2427
+ "internalType": "uint256"
2428
+ }
2429
+ ],
2430
+ "anonymous": false
2431
+ },
2432
+ {
2433
+ "type": "event",
2434
+ "name": "Revoked",
2435
+ "inputs": [
2436
+ {
2437
+ "name": "owner",
2438
+ "type": "address",
2439
+ "indexed": true,
2440
+ "internalType": "address"
2441
+ },
2442
+ {
2443
+ "name": "fingerprintHash",
2444
+ "type": "bytes32",
2445
+ "indexed": true,
2446
+ "internalType": "bytes32"
2447
+ },
2448
+ {
2449
+ "name": "index",
2450
+ "type": "uint256",
2451
+ "indexed": true,
2452
+ "internalType": "uint256"
2453
+ },
2454
+ {
2455
+ "name": "reason",
2456
+ "type": "string",
2457
+ "indexed": false,
2458
+ "internalType": "string"
2459
+ },
2460
+ {
2461
+ "name": "replacedBy",
2462
+ "type": "uint256",
2463
+ "indexed": false,
2464
+ "internalType": "uint256"
2465
+ },
2466
+ {
2467
+ "name": "submitter",
2468
+ "type": "address",
2469
+ "indexed": false,
2470
+ "internalType": "address"
2471
+ }
2472
+ ],
2473
+ "anonymous": false
2474
+ },
2475
+ {
2476
+ "type": "error",
2477
+ "name": "AlreadyRevoked",
2478
+ "inputs": [
2479
+ {
2480
+ "name": "index",
2481
+ "type": "uint256",
2482
+ "internalType": "uint256"
2483
+ }
2484
+ ]
2485
+ },
2486
+ {
2487
+ "type": "error",
2488
+ "name": "BadArmorChecksum",
2489
+ "inputs": []
2490
+ },
2491
+ {
2492
+ "type": "error",
2493
+ "name": "BadBase64",
2494
+ "inputs": []
2495
+ },
2496
+ {
2497
+ "type": "error",
2498
+ "name": "ClaimActive",
2499
+ "inputs": [
2500
+ {
2501
+ "name": "index",
2502
+ "type": "uint256",
2503
+ "internalType": "uint256"
2504
+ }
2505
+ ]
2506
+ },
2507
+ {
2508
+ "type": "error",
2509
+ "name": "DeploymentFailed",
2510
+ "inputs": []
2511
+ },
2512
+ {
2513
+ "type": "error",
2514
+ "name": "DuplicateActiveFingerprint",
2515
+ "inputs": [
2516
+ {
2517
+ "name": "fingerprint",
2518
+ "type": "bytes",
2519
+ "internalType": "bytes"
2520
+ }
2521
+ ]
2522
+ },
2523
+ {
2524
+ "type": "error",
2525
+ "name": "EmptyKey",
2526
+ "inputs": []
2527
+ },
2528
+ {
2529
+ "type": "error",
2530
+ "name": "EmptySignature",
2531
+ "inputs": []
2532
+ },
2533
+ {
2534
+ "type": "error",
2535
+ "name": "IndexOutOfBounds",
2536
+ "inputs": [
2537
+ {
2538
+ "name": "index",
2539
+ "type": "uint256",
2540
+ "internalType": "uint256"
2541
+ },
2542
+ {
2543
+ "name": "count",
2544
+ "type": "uint256",
2545
+ "internalType": "uint256"
2546
+ }
2547
+ ]
2548
+ },
2549
+ {
2550
+ "type": "error",
2551
+ "name": "InvalidFingerprint",
2552
+ "inputs": [
2553
+ {
2554
+ "name": "fingerprint",
2555
+ "type": "bytes",
2556
+ "internalType": "bytes"
2557
+ }
2558
+ ]
2559
+ },
2560
+ {
2561
+ "type": "error",
2562
+ "name": "InvalidFingerprintLength",
2563
+ "inputs": [
2564
+ {
2565
+ "name": "length",
2566
+ "type": "uint256",
2567
+ "internalType": "uint256"
2568
+ }
2569
+ ]
2570
+ },
2571
+ {
2572
+ "type": "error",
2573
+ "name": "InvalidKindName",
2574
+ "inputs": [
2575
+ {
2576
+ "name": "kind",
2577
+ "type": "string",
2578
+ "internalType": "string"
2579
+ }
2580
+ ]
2581
+ },
2582
+ {
2583
+ "type": "error",
2584
+ "name": "InvalidPermission",
2585
+ "inputs": []
2586
+ },
2587
+ {
2588
+ "type": "error",
2589
+ "name": "InvalidPointer",
2590
+ "inputs": []
2591
+ },
2592
+ {
2593
+ "type": "error",
2594
+ "name": "KeyCompromised",
2595
+ "inputs": [
2596
+ {
2597
+ "name": "fingerprint",
2598
+ "type": "bytes",
2599
+ "internalType": "bytes"
2600
+ }
2601
+ ]
2602
+ },
2603
+ {
2604
+ "type": "error",
2605
+ "name": "KeyStillActive",
2606
+ "inputs": [
2607
+ {
2608
+ "name": "fingerprint",
2609
+ "type": "bytes",
2610
+ "internalType": "bytes"
2611
+ }
2612
+ ]
2613
+ },
2614
+ {
2615
+ "type": "error",
2616
+ "name": "KeyTooLarge",
2617
+ "inputs": [
2618
+ {
2619
+ "name": "size",
2620
+ "type": "uint256",
2621
+ "internalType": "uint256"
2622
+ },
2623
+ {
2624
+ "name": "max",
2625
+ "type": "uint256",
2626
+ "internalType": "uint256"
2627
+ }
2628
+ ]
2629
+ },
2630
+ {
2631
+ "type": "error",
2632
+ "name": "NotAKey",
2633
+ "inputs": [
2634
+ {
2635
+ "name": "firstByte",
2636
+ "type": "bytes1",
2637
+ "internalType": "bytes1"
2638
+ }
2639
+ ]
2640
+ },
2641
+ {
2642
+ "type": "error",
2643
+ "name": "NotASignature",
2644
+ "inputs": [
2645
+ {
2646
+ "name": "firstByte",
2647
+ "type": "bytes1",
2648
+ "internalType": "bytes1"
2649
+ }
2650
+ ]
2651
+ },
2652
+ {
2653
+ "type": "error",
2654
+ "name": "NotArmored",
2655
+ "inputs": []
2656
+ },
2657
+ {
2658
+ "type": "error",
2659
+ "name": "PayloadTooLarge",
2660
+ "inputs": [
2661
+ {
2662
+ "name": "size",
2663
+ "type": "uint256",
2664
+ "internalType": "uint256"
2665
+ },
2666
+ {
2667
+ "name": "max",
2668
+ "type": "uint256",
2669
+ "internalType": "uint256"
2670
+ }
2671
+ ]
2672
+ },
2673
+ {
2674
+ "type": "error",
2675
+ "name": "PermissionExpired",
2676
+ "inputs": [
2677
+ {
2678
+ "name": "deadline",
2679
+ "type": "uint256",
2680
+ "internalType": "uint256"
2681
+ }
2682
+ ]
2683
+ },
2684
+ {
2685
+ "type": "error",
2686
+ "name": "RecordTooLarge",
2687
+ "inputs": [
2688
+ {
2689
+ "name": "size",
2690
+ "type": "uint256",
2691
+ "internalType": "uint256"
2692
+ },
2693
+ {
2694
+ "name": "max",
2695
+ "type": "uint256",
2696
+ "internalType": "uint256"
2697
+ }
2698
+ ]
2699
+ },
2700
+ {
2701
+ "type": "error",
2702
+ "name": "SignatureTooLarge",
2703
+ "inputs": [
2704
+ {
2705
+ "name": "size",
2706
+ "type": "uint256",
2707
+ "internalType": "uint256"
2708
+ },
2709
+ {
2710
+ "name": "max",
2711
+ "type": "uint256",
2712
+ "internalType": "uint256"
2713
+ }
2714
+ ]
2715
+ },
2716
+ {
2717
+ "type": "error",
2718
+ "name": "SupersededIsSetByReattest",
2719
+ "inputs": []
2720
+ },
2721
+ {
2722
+ "type": "error",
2723
+ "name": "TooManyClaims",
2724
+ "inputs": []
2725
+ },
2726
+ {
2727
+ "type": "error",
2728
+ "name": "UnknownRevokeReason",
2729
+ "inputs": [
2730
+ {
2731
+ "name": "reason",
2732
+ "type": "string",
2733
+ "internalType": "string"
2734
+ }
2735
+ ]
2736
+ },
2737
+ {
2738
+ "type": "error",
2739
+ "name": "UnsupportedHeader",
2740
+ "inputs": []
2741
+ }
2742
+ ];
2743
+
2744
+ // src/core/contract.ts
2745
+ var REGISTRY_ADDRESS = "0xFa6956c11163517249f8A67F5560a4406B519451";
2746
+ var NETWORKS = {
2747
+ mainnet: {
2748
+ chainId: 1,
2749
+ address: REGISTRY_ADDRESS,
2750
+ explorerUrl: "https://etherscan.io",
2751
+ defaultRpcUrl: "https://ethereum.publicnode.com"
2752
+ // resolves on more networks than ethereum-rpc.publicnode.com
2753
+ },
2754
+ sepolia: {
2755
+ chainId: 11155111,
2756
+ address: REGISTRY_ADDRESS,
2757
+ explorerUrl: "https://sepolia.etherscan.io",
2758
+ defaultRpcUrl: "https://ethereum-sepolia.publicnode.com"
2759
+ },
2760
+ /** A local anvil instance (`anvil`, chain id 31337). Deploy with pgp-registry's Deploy script. */
2761
+ local: {
2762
+ chainId: 31337,
2763
+ address: REGISTRY_ADDRESS,
2764
+ explorerUrl: "",
2765
+ defaultRpcUrl: "http://127.0.0.1:8545"
2766
+ }
2767
+ };
2768
+ function isNetworkName(value) {
2769
+ return value === "mainnet" || value === "sepolia" || value === "local";
2770
+ }
2771
+ function getRegistry(network = "mainnet", addressOverride) {
2772
+ const r = NETWORKS[network];
2773
+ if (!r) throw new Error(`Unknown network: ${network}`);
2774
+ if (addressOverride && /^0x[0-9a-fA-F]{40}$/.test(addressOverride)) {
2775
+ return { ...r, address: addressOverride };
2776
+ }
2777
+ return r;
2778
+ }
2779
+ function chainFor(network = "mainnet") {
2780
+ if (network === "sepolia") return _chains.sepolia;
2781
+ if (network === "local") return _chains.foundry;
2782
+ return _chains.mainnet;
2783
+ }
2784
+
2785
+ // src/core/claims.ts
2786
+ var CLAIM_LIMIT = 50;
2787
+ async function readClaims(client, owner, { registry = REGISTRY_ADDRESS, limit = CLAIM_LIMIT } = {}) {
2788
+ const read = (functionName, args) => client.readContract({ address: registry, abi: REGISTRY_ABI, functionName, args });
2789
+ const rows = await read("claimsOf", [owner]);
2790
+ const first = Math.max(0, rows.length - limit);
2791
+ return Promise.all(rows.map(async (row, index) => {
2792
+ const revokedAt = Number(row.revokedAt);
2793
+ const claim = {
2794
+ index,
2795
+ fingerprint: bytesToFingerprint(row.fingerprint),
2796
+ createdAt: Number(row.createdAt),
2797
+ revoked: revokedAt !== 0,
2798
+ revokedAt: revokedAt || null,
2799
+ state: row.state,
2800
+ replacedBy: row.state === "replaced" ? Number(row.replacedBy) : null,
2801
+ revokeReason: row.revokeReason,
2802
+ messageVersion: Number(row.messageVersion),
2803
+ pgpPublicKey: null,
2804
+ pgpSignature: null,
2805
+ verification: null
2806
+ };
2807
+ if (index < first) return claim;
2808
+ const bytes = (fn) => read(fn, [owner, BigInt(index)]).catch(() => null);
2809
+ const [keyHex, sigHex] = await Promise.all([bytes("keyBytes"), bytes("signatureBytes")]);
2810
+ claim.pgpPublicKey = keyHex ? await payloadText(keyHex, "key") : null;
2811
+ claim.pgpSignature = sigHex ? await payloadText(sigHex, "signature") : null;
2812
+ claim.verification = await verifyAttestation({
2813
+ pgpPublicKey: claim.pgpPublicKey,
2814
+ pgpSignature: claim.pgpSignature,
2815
+ fingerprint: claim.fingerprint,
2816
+ ethAddress: owner
2817
+ });
2818
+ return claim;
2819
+ }));
2820
+ }
2821
+ function keyStanding(claims) {
2822
+ const active = claims.filter((c) => !c.revoked);
2823
+ const verified = active.filter((c) => _optionalChain([c, 'access', _78 => _78.verification, 'optionalAccess', _79 => _79.verified]));
2824
+ if (verified.length) return { kind: "verified", claim: verified[verified.length - 1] };
2825
+ if (active.length) return { kind: "not-counted", claim: active[active.length - 1] };
2826
+ if (claims.length) return { kind: "inactive", claim: claims[claims.length - 1] };
2827
+ return { kind: "none", claim: null };
2828
+ }
2829
+ async function findOwners(client, key, { registry = REGISTRY_ADDRESS } = {}) {
2830
+ const read = (functionName, args) => client.readContract({ address: registry, abi: REGISTRY_ABI, functionName, args });
2831
+ let fingerprints;
2832
+ if ("keyId" in key) {
2833
+ const keyId = keyIdToBytes(key.keyId);
2834
+ if (!keyId) return [];
2835
+ fingerprints = (await read("fingerprintsForKeyId", [keyId])).map(bytesToFingerprint);
2836
+ } else {
2837
+ const fp = normalizeFingerprint(key.fingerprint);
2838
+ if (!fp) return [];
2839
+ fingerprints = [fp];
2840
+ }
2841
+ const found = [];
2842
+ for (const fingerprint of fingerprints) {
2843
+ for (const owner of await read("ownersOf", [fingerprintToBytes(fingerprint)])) {
2844
+ if (!found.some((f) => f.owner.toLowerCase() === owner.toLowerCase() && f.fingerprint === fingerprint)) found.push({ owner, fingerprint });
2845
+ }
2846
+ }
2847
+ return found;
2848
+ }
2849
+
2850
+ // src/core/claimStatus.ts
2851
+ var CLAIM_CHECK_LABEL = {
2852
+ verified: "pgp verified",
2853
+ expired: "key expired",
2854
+ "signing-key-expired": "signing key expired",
2855
+ revoked: "key revoked",
2856
+ compromised: "key compromised",
2857
+ "signing-key-revoked": "signing key revoked",
2858
+ unsupported: "not supported",
2859
+ "bad-signature": "doesn't verify"
2860
+ };
2861
+ function formatClaimDate(isoOrSeconds) {
2862
+ if (isoOrSeconds === null || isoOrSeconds === void 0) return "an unknown date";
2863
+ const d = typeof isoOrSeconds === "number" ? new Date(isoOrSeconds * 1e3) : new Date(isoOrSeconds);
2864
+ if (Number.isNaN(d.getTime())) return "an unknown date";
2865
+ return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
2866
+ }
2867
+ function shortFingerprint(fpr) {
2868
+ if (!fpr) return "unknown";
2869
+ const f = fpr.toUpperCase();
2870
+ return `${f.slice(0, 4)} ${f.slice(4, 8)} \u2026`;
2871
+ }
2872
+ function claimCheckText(v, formatDate = formatClaimDate) {
2873
+ const kind = _nullishCoalesce(v.kind, () => ( (v.verified ? "verified" : "bad-signature")));
2874
+ const at = formatDate(v.at);
2875
+ const t = (sentence, fix = null) => ({ kind, label: CLAIM_CHECK_LABEL[kind], sentence, fix });
2876
+ switch (kind) {
2877
+ case "verified":
2878
+ return t("The key signed a line naming this address, and the signature checks out.");
2879
+ case "expired":
2880
+ return t(
2881
+ `The key on this claim expired on ${at}, so the claim no longer counts.`,
2882
+ "Extend the key, then Update key. No new signature needed."
2883
+ );
2884
+ case "signing-key-expired":
2885
+ return t(
2886
+ `The subkey that signed this claim (${shortFingerprint(v.signingKey)}) expired on ${at}.`,
2887
+ "Extend that subkey, then Update key."
2888
+ );
2889
+ case "revoked":
2890
+ return t(
2891
+ `Its owner revoked this PGP key on ${at}${v.revocationReason ? ` (reason: ${v.revocationReason})` : ""}. The claim no longer counts.`,
2892
+ "Revoke this claim and attest your new key."
2893
+ );
2894
+ case "compromised":
2895
+ return t(
2896
+ `Its owner revoked this PGP key on ${at} as compromised. Don't trust anything it signed after that date.`,
2897
+ "Revoke this claim and attest a new key."
2898
+ );
2899
+ case "signing-key-revoked":
2900
+ return t(
2901
+ `The subkey that signed this claim was revoked on ${at}.`,
2902
+ "Replace the claim with a signature from a current key."
2903
+ );
2904
+ case "unsupported":
2905
+ return t(`This key uses an algorithm Thurin doesn't check${v.algorithm ? ` (${v.algorithm})` : ""}. The claim is on-chain, but no lookup will vouch for it.`);
2906
+ case "bad-signature":
2907
+ default:
2908
+ return t("The signature stored with this claim doesn't match its key, so nothing from this claim is shown.");
2909
+ }
2910
+ }
2911
+ function keyProblemText(v, formatDate = formatClaimDate) {
2912
+ const at = formatDate(v.at);
2913
+ const t = (kind, sentence, fix) => ({ kind, label: CLAIM_CHECK_LABEL[kind], sentence, fix });
2914
+ switch (v.kind) {
2915
+ case "expired":
2916
+ return t("expired", `This key expired on ${at}, so a claim with it wouldn't count.`, "Extend it (gpg --quick-set-expire), then export it again.");
2917
+ case "signing-key-expired":
2918
+ return t("signing-key-expired", `The subkey that signed (${shortFingerprint(v.signingKey)}) expired on ${at}.`, "Extend that subkey, or sign with a current one.");
2919
+ case "revoked":
2920
+ return t("revoked", `This key was revoked on ${at}${v.revocationReason ? ` (reason: ${v.revocationReason})` : ""}.`, "Use a current key.");
2921
+ case "compromised":
2922
+ return t("compromised", `This key was revoked as compromised on ${at}.`, "Use a new key.");
2923
+ case "signing-key-revoked":
2924
+ return t("signing-key-revoked", `The subkey that signed was revoked on ${at}.`, "Sign with a current subkey.");
2925
+ case "unsupported":
2926
+ return t("unsupported", `This key uses an algorithm Thurin doesn't check${v.algorithm ? ` (${v.algorithm})` : ""}, so no lookup would vouch for the claim.`, "Use an Ed25519 or RSA key.");
2927
+ default:
2928
+ return null;
2929
+ }
2930
+ }
2931
+ function expiresSoon(v, now = Date.now(), withinDays = 30) {
2932
+ if (!_optionalChain([v, 'optionalAccess', _80 => _80.verified]) || !v.expiresAt) return null;
2933
+ const ms = new Date(v.expiresAt).getTime() - now;
2934
+ if (!Number.isFinite(ms) || ms < 0 || ms > withinDays * 864e5) return null;
2935
+ return { days: Math.floor(ms / 864e5), at: v.expiresAt };
2936
+ }
2937
+ function expiresSoonText(soon, formatDate = formatClaimDate) {
2938
+ const when = soon.days === 0 ? "today" : soon.days === 1 ? "in 1 day" : `in ${soon.days} days`;
2939
+ return `Key expires ${when} (${formatDate(soon.at)}).`;
2940
+ }
2941
+ function claimFates(claims) {
2942
+ const out = /* @__PURE__ */ new Map();
2943
+ for (const c of claims) {
2944
+ if (c.state === "active" || !c.revokedAt) out.set(c.index, { state: "active" });
2945
+ else if (c.state === "replaced" && c.replacedBy !== null) out.set(c.index, { state: "replaced", at: c.revokedAt, by: c.replacedBy, ...c.revokeReason === "compromised" ? { compromised: true } : {} });
2946
+ else out.set(c.index, { state: "revoked", at: c.revokedAt, ...c.revokeReason ? { reason: c.revokeReason } : {} });
2947
+ }
2948
+ return out;
2949
+ }
2950
+ function claimFateText(fate, formatDate = (s) => formatClaimDate(s)) {
2951
+ if (fate.state === "revoked") return `Revoked by its owner on ${formatDate(fate.at)}${fate.reason ? ` (${fate.reason})` : ""}.`;
2952
+ if (fate.state === "replaced") return `Replaced by claim #${fate.by} on ${formatDate(fate.at)}.${fate.compromised ? " Its key was marked compromised." : ""}`;
2953
+ return null;
2954
+ }
2955
+
2956
+ // src/core/encrypt.ts
2957
+ async function encryptionKeyFor(claims, { now = Date.now(), recentDays = 7 } = {}) {
2958
+ const standing = keyStanding(claims);
2959
+ if (standing.kind === "none" || standing.kind === "inactive") return { ok: false, reason: "no-claim", claim: standing.claim };
2960
+ if (standing.kind === "not-counted" || !standing.claim.pgpPublicKey) return { ok: false, reason: "unverified", claim: standing.claim };
2961
+ const claim = standing.claim;
2962
+ try {
2963
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
2964
+ const config = pgpConfig(openpgp);
2965
+ const key = await readKeyAny(openpgp, claim.pgpPublicKey);
2966
+ const date = new Date(now);
2967
+ const enc = await key.getEncryptionKey(void 0, date, void 0, config).catch(() => null);
2968
+ if (!enc) {
2969
+ let lastExpiry = null;
2970
+ for (const sk of key.subkeys) {
2971
+ const flags = _nullishCoalesce(_optionalChain([newestSignature, 'call', _81 => _81(sk.bindingSignatures), 'optionalAccess', _82 => _82.keyFlags, 'optionalAccess', _83 => _83[0]]), () => ( 0));
2972
+ if (!(flags & 12) || await sk.isRevoked(void 0, void 0, date, config).catch(() => false)) continue;
2973
+ const exp2 = await sk.getExpirationTime(date, config).catch(() => null);
2974
+ if (typeof exp2 === "number" || exp2 instanceof Date) {
2975
+ const t = new Date(exp2).getTime();
2976
+ if (t <= now && (lastExpiry === null || t > lastExpiry)) lastExpiry = t;
2977
+ }
2978
+ }
2979
+ return lastExpiry !== null ? { ok: false, reason: "encryption-subkey-expired", claim, expired: new Date(lastExpiry).toISOString() } : { ok: false, reason: "no-encryption-subkey", claim };
2980
+ }
2981
+ const exp = await enc.getExpirationTime(date, config).catch(() => null);
2982
+ return {
2983
+ ok: true,
2984
+ claim,
2985
+ key: claim.pgpPublicKey,
2986
+ subkey: {
2987
+ fingerprint: enc.getFingerprint().toUpperCase(),
2988
+ algorithm: algorithmName(enc),
2989
+ expires: exp && exp !== Infinity ? new Date(exp).toISOString() : null
2990
+ },
2991
+ addedAt: claim.createdAt,
2992
+ changedRecently: now / 1e3 - claim.createdAt < recentDays * 86400
2993
+ };
2994
+ } catch (e17) {
2995
+ return { ok: false, reason: "unverified", claim };
2996
+ }
2997
+ }
2998
+ function encryptRefusalText(r, formatDate = formatClaimDate) {
2999
+ switch (r.reason) {
3000
+ case "no-claim":
3001
+ return "No claim counts for this identity, so there is no key to encrypt to.";
3002
+ case "unverified":
3003
+ return "Its newest claim doesn't verify, so its key can't be trusted with a message.";
3004
+ case "no-encryption-subkey":
3005
+ return "Its key has no encryption subkey, so it can sign but not receive.";
3006
+ case "encryption-subkey-expired":
3007
+ return `Its encryption subkey expired on ${formatDate(r.expired)}.`;
3008
+ }
3009
+ }
3010
+ function keyChangedText(k, now = Date.now()) {
3011
+ if (!k.changedRecently) return null;
3012
+ const days = Math.floor((now / 1e3 - k.addedAt) / 86400);
3013
+ const when = days <= 0 ? "today" : days === 1 ? "yesterday" : `${days} days ago`;
3014
+ return `This key was added ${when}. If you were expecting a different one, check with them another way.`;
3015
+ }
3016
+ async function encryptTo(key, data, { filename, armor = typeof data === "string", hideRecipient = true, date = /* @__PURE__ */ new Date() } = {}) {
3017
+ const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
3018
+ const config = pgpConfig(openpgp);
3019
+ const encryptionKeys = await readKeyAny(openpgp, key);
3020
+ const message = typeof data === "string" ? await openpgp.createMessage({ text: data, date }) : await openpgp.createMessage({ binary: data, filename, format: "binary", date });
3021
+ const opts = { message, encryptionKeys, config, wildcard: hideRecipient, date };
3022
+ return armor ? await openpgp.encrypt({ ...opts, format: "armored" }) : await openpgp.encrypt({ ...opts, format: "binary" });
3023
+ }
3024
+
3025
+ // src/core/contractErrors.ts
3026
+ var CONTRACT_ERROR_TEXT = {
3027
+ AlreadyRevoked: "That claim has already ended, or is already marked compromised.",
3028
+ BadArmorChecksum: "The armor's checksum doesn't match its data: the paste was changed or cut short.",
3029
+ BadBase64: "The armored data isn't valid base64.",
3030
+ ClaimActive: 'That claim is still active: revoke it with the reason "compromised" instead.',
3031
+ DeploymentFailed: "The key and signature couldn't be stored: together they're over the contract's size limit.",
3032
+ DuplicateActiveFingerprint: "This address already has an active claim for this key: update it or replace it instead.",
3033
+ EmptyKey: "No key was given.",
3034
+ EmptySignature: "No signature was given.",
3035
+ IndexOutOfBounds: "This address has no claim at that index.",
3036
+ InvalidFingerprint: "That 32-byte fingerprint ends in 12 zero bytes, which no real v6 key has.",
3037
+ InvalidFingerprintLength: "The fingerprint isn't 20 bytes (v4 key) or 32 bytes (v6 key).",
3038
+ InvalidKindName: "The record name is empty, longer than 31 bytes, or uses something other than a-z, 0-9, '-', and '.'.",
3039
+ InvalidPermission: "The permission isn't the owner's signature over exactly this change and their current nonce: it was already used, cancelled, or superseded by a newer one.",
3040
+ InvalidPointer: "There is no stored data at that address.",
3041
+ KeyCompromised: "This address marked the key compromised: it can never claim it again.",
3042
+ KeyStillActive: 'This address still has an active claim for the key: revoke that claim as "compromised" instead.',
3043
+ KeyTooLarge: "The key is over 16,384 bytes: export it without photos or old signatures.",
3044
+ NotAKey: "The key doesn't start with an OpenPGP public-key packet: give gpg's export of the public key.",
3045
+ NotASignature: "The signature is neither an OpenPGP signature nor a clearsigned message.",
3046
+ NotArmored: "The paste has no -----BEGIN PGP and -----END PGP lines.",
3047
+ PayloadTooLarge: "The signature and key together are over 24,000 bytes.",
3048
+ PermissionExpired: "The permission is past its deadline: ask the owner for a new one.",
3049
+ RecordTooLarge: "The record value is over 1,024 bytes.",
3050
+ SignatureTooLarge: "The signature is over 8,192 bytes.",
3051
+ SupersededIsSetByReattest: '"superseded" is set only by replacing a claim: pick another reason.',
3052
+ TooManyClaims: "This address has reached the limit of 65,535 claims.",
3053
+ UnknownRevokeReason: 'The revoke reason must be empty, "compromised", "retired", or "other".',
3054
+ UnsupportedHeader: "Armor header lines (like Comment:) can't be told from the data, as when line breaks were lost: remove them."
3055
+ };
3056
+ function contractErrorName(err) {
3057
+ for (let e = err, depth = 0; e && depth < 8; e = e.cause, depth++) {
3058
+ const name = _nullishCoalesce(_optionalChain([e, 'access', _84 => _84.data, 'optionalAccess', _85 => _85.errorName]), () => ( e.errorName));
3059
+ if (typeof name === "string" && name) return name;
3060
+ }
3061
+ return null;
3062
+ }
3063
+ function contractErrorText(err) {
3064
+ const name = contractErrorName(err);
3065
+ return name ? _nullishCoalesce(CONTRACT_ERROR_TEXT[name], () => ( `The registry refused it (${name}).`)) : null;
3066
+ }
3067
+
3068
+ // src/core/records.ts
3069
+ var MAX_RECORD_BYTES = 1024;
3070
+ var MAX_KIND_BYTES = 31;
3071
+ function kindName(input) {
3072
+ return input.includes(".") ? input : `thurin.${input}`;
3073
+ }
3074
+ function checkKindName(input) {
3075
+ if (!/^[a-z0-9.-]+$/.test(input)) throw new Error(`Record name "${input}": use a-z, 0-9, "-" and "." only`);
3076
+ const name = kindName(input);
3077
+ if (name.length > MAX_KIND_BYTES) throw new Error(`Record name "${name}" is ${name.length} bytes; the registry allows ${MAX_KIND_BYTES}`);
3078
+ return name;
3079
+ }
3080
+ function checkRecordValue(value) {
3081
+ const bytes = new TextEncoder().encode(value).length;
3082
+ if (bytes > MAX_RECORD_BYTES) throw new Error(`Record is ${bytes} bytes; the registry accepts up to ${MAX_RECORD_BYTES}`);
3083
+ return value;
3084
+ }
3085
+ var KNOWN_KINDS = [
3086
+ "thurin.railgun",
3087
+ "thurin.security",
3088
+ "thurin.successor",
3089
+ "thurin.affiliation",
3090
+ "thurin.canary",
3091
+ "thurin.releases",
3092
+ "thurin.private",
3093
+ "thurin.disclosure"
3094
+ ];
3095
+ function parseReleases(text) {
3096
+ const p = JSON.parse(text);
3097
+ if (_optionalChain([p, 'optionalAccess', _86 => _86.v]) !== 1 || !Array.isArray(p.releases)) throw new Error("Not a v1 thurin.releases record");
3098
+ return p;
3099
+ }
3100
+ function addRelease(existing, entry) {
3101
+ if (!/^[0-9a-f]{64}$/i.test(entry.sha256)) throw new Error("sha256 must be 64 hex characters");
3102
+ const releases = [entry, ...(_nullishCoalesce(_optionalChain([existing, 'optionalAccess', _87 => _87.releases]), () => ( []))).filter((r) => r.name !== entry.name)];
3103
+ const dropped = [];
3104
+ const record = { v: 1, releases };
3105
+ while (new TextEncoder().encode(JSON.stringify(record)).length > MAX_RECORD_BYTES && releases.length > 1) dropped.push(releases.pop());
3106
+ return { record, dropped };
3107
+ }
3108
+ function renderReleases(p) {
3109
+ return p.releases.map((r) => `${r.name.padEnd(22)} ${r.date} sha256 ${r.sha256}${r.url ? ` ${r.url}` : ""}`).join("\n");
3110
+ }
3111
+ var RAILGUN_ADDRESS = /^0zk1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{60,200}$/;
3112
+ var ISO_DATE = /\b(\d{4}-\d{2}-\d{2})\b/;
3113
+ async function parseRecord(kind, text, opts = {}) {
3114
+ const bytes = new TextEncoder().encode(text).length;
3115
+ const base = { kind, text, bytes };
3116
+ const invalid = (reason, data = { type: "text" }) => ({ ...base, valid: false, reason, data });
3117
+ const t = text.trim();
3118
+ switch (kind) {
3119
+ case "thurin.railgun": {
3120
+ if (!RAILGUN_ADDRESS.test(t)) return invalid("Not a 0zk address");
3121
+ return { ...base, valid: true, data: { type: "railgun", address: t } };
3122
+ }
3123
+ case "thurin.security": {
3124
+ if (!t) return invalid("Empty");
3125
+ const url = /^https?:\/\/\S+$/.test(t) ? t : null;
3126
+ return { ...base, valid: true, data: { type: "security", contact: t, url } };
3127
+ }
3128
+ case "thurin.successor": {
3129
+ const fp = normalizeFingerprint(t);
3130
+ if (!fp) return invalid("Not a PGP fingerprint");
3131
+ return { ...base, valid: true, data: { type: "successor", fingerprint: fp } };
3132
+ }
3133
+ case "thurin.affiliation": {
3134
+ try {
3135
+ const j = JSON.parse(t);
3136
+ if (_optionalChain([j, 'optionalAccess', _88 => _88.v]) !== 1 || typeof j.with !== "string" || !j.with) return invalid('Not a v1 affiliation: expected {"v":1,"with":"<address or name>"}');
3137
+ return { ...base, valid: true, data: { type: "affiliation", with: j.with, role: typeof j.role === "string" ? j.role : null } };
3138
+ } catch (e18) {
3139
+ return invalid("Not JSON");
3140
+ }
3141
+ }
3142
+ case "thurin.canary": {
3143
+ const date = _optionalChain([t, 'access', _89 => _89.match, 'call', _90 => _90(ISO_DATE), 'optionalAccess', _91 => _91[1]]);
3144
+ if (!date) return invalid("No date in the statement");
3145
+ const clearsigned = t.startsWith("-----BEGIN PGP SIGNED MESSAGE-----");
3146
+ let statement = clearsigned ? (_nullishCoalesce(t.split(/\r?\n\r?\n/)[1], () => ( ""))).split("-----BEGIN PGP SIGNATURE-----")[0].trim() : t;
3147
+ let verified = null, reason;
3148
+ if (clearsigned && opts.armoredKey) {
3149
+ const v = await verifyClearsigned({ armoredKey: opts.armoredKey, clearsigned: t });
3150
+ verified = v.verified;
3151
+ reason = v.reason;
3152
+ if (v.text) statement = v.text.trim();
3153
+ }
3154
+ return { ...base, valid: true, data: { type: "canary", date, statement, clearsigned, verified, ...reason ? { reason } : {} } };
3155
+ }
3156
+ case "thurin.private":
3157
+ case "thurin.disclosure": {
3158
+ if (!t.startsWith("-----BEGIN PGP MESSAGE-----")) return invalid("Not a PGP message");
3159
+ let recipients = null;
3160
+ try {
3161
+ const { readMessage } = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
3162
+ recipients = (await readMessage({ armoredMessage: t })).getEncryptionKeyIDs().length;
3163
+ } catch (e19) {
3164
+ return invalid("PGP message does not parse");
3165
+ }
3166
+ return { ...base, valid: true, data: { type: "encrypted", recipients } };
3167
+ }
3168
+ case "thurin.releases": {
3169
+ try {
3170
+ return { ...base, valid: true, data: { type: "releases", releases: parseReleases(t).releases } };
3171
+ } catch (e20) {
3172
+ return invalid('Not a v1 release list: expected {"v":1,"releases":[\u2026]}');
3173
+ }
3174
+ }
3175
+ default:
3176
+ return { ...base, valid: true, data: { type: "text" } };
3177
+ }
3178
+ }
3179
+ function pickRecords(names, values, kinds = KNOWN_KINDS) {
3180
+ const all = names.map((kind, i) => ({ kind, text: _nullishCoalesce(values[i], () => ( "")) })).filter((r) => r.text);
3181
+ if (!kinds) return all;
3182
+ return kinds.flatMap((k) => all.filter((r) => r.kind === k));
3183
+ }
3184
+ function pageRecords(names, values) {
3185
+ const all = pickRecords(names, values, null);
3186
+ const ours = KNOWN_KINDS.flatMap((k) => all.filter((r) => r.kind === k));
3187
+ return [...ours, ...all.filter((r) => !KNOWN_KINDS.includes(r.kind))];
3188
+ }
3189
+ async function fetchRecords(client, registry, abi, owner, index, kinds = KNOWN_KINDS, opts = {}) {
3190
+ const [names, values] = await client.readContract({ address: registry, abi, functionName: "recordsOf", args: [owner, BigInt(index)] });
3191
+ const out = [];
3192
+ for (const r of pickRecords(names, values, kinds)) out.push(await parseRecord(r.kind, r.text, opts));
3193
+ return out;
3194
+ }
3195
+
3196
+ // src/core/avatar.ts
3197
+ var IPFS_GATEWAYS = ["https://ipfs.filebase.io/ipfs/", "https://gateway.pinata.cloud/ipfs/"];
3198
+ var IPFS_GATEWAY = IPFS_GATEWAYS[0];
3199
+ var ARWEAVE_GATEWAY = "https://arweave.net/";
3200
+ function avatarUrl(uri) {
3201
+ if (typeof uri !== "string") return null;
3202
+ const u = uri.trim();
3203
+ if (/^data:image\/(png|jpe?g|gif|webp|avif|svg\+xml)[;,]/i.test(u)) return u;
3204
+ let m = u.match(/^ipfs:\/\/(?:ipfs\/)?([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
3205
+ if (m) return IPFS_GATEWAY + m[1];
3206
+ m = u.match(/^ar:\/\/([a-zA-Z0-9_-]{43}(?:\/[^?#\s]*)?)$/);
3207
+ if (m) return ARWEAVE_GATEWAY + m[1];
3208
+ if (/^https:\/\/euc\.li\/[^?#\s]+$/.test(u)) return u;
3209
+ m = u.match(/^https:\/\/[^/]+\/ipfs\/([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
3210
+ if (m) return IPFS_GATEWAY + m[1];
3211
+ return null;
3212
+ }
3213
+ function avatarFallbacks(url) {
3214
+ if (!url || !url.startsWith(IPFS_GATEWAYS[0])) return [];
3215
+ const path = url.slice(IPFS_GATEWAYS[0].length);
3216
+ return IPFS_GATEWAYS.slice(1).map((g) => g + path);
3217
+ }
3218
+ function parseNftAvatar(raw) {
3219
+ const m = typeof raw === "string" && raw.trim().match(/^eip155:(\d+)\/(erc721|erc1155):(0x[0-9a-fA-F]{40})\/(\d+)$/i);
3220
+ return m ? { chainId: Number(m[1]), standard: m[2].toLowerCase(), contract: m[3], tokenId: BigInt(m[4]) } : null;
3221
+ }
3222
+ var NFT_AVATAR_ABI = [
3223
+ { type: "function", name: "tokenURI", stateMutability: "view", inputs: [{ type: "uint256" }], outputs: [{ type: "string" }] },
3224
+ { type: "function", name: "uri", stateMutability: "view", inputs: [{ type: "uint256" }], outputs: [{ type: "string" }] }
3225
+ ];
3226
+ async function nftAvatarImage(tokenUri, tokenId, fetchFn = fetch) {
3227
+ const uri = tokenUri.replace("{id}", tokenId.toString(16).padStart(64, "0"));
3228
+ let meta;
3229
+ const inline = uri.match(/^data:application\/json(;base64)?,([\s\S]*)$/);
3230
+ if (inline) meta = JSON.parse(inline[1] ? atob(inline[2]) : decodeURIComponent(inline[2]));
3231
+ else {
3232
+ const url = uri.startsWith("data:") ? null : avatarUrl(uri);
3233
+ if (!url) return null;
3234
+ for (const candidate of [url, ...avatarFallbacks(url)]) {
3235
+ try {
3236
+ const res = await fetchFn(candidate);
3237
+ if (res.ok) {
3238
+ meta = await res.json();
3239
+ break;
3240
+ }
3241
+ } catch (e21) {
3242
+ }
3243
+ }
3244
+ if (!meta) return null;
3245
+ }
3246
+ return avatarUrl(_nullishCoalesce(_optionalChain([meta, 'optionalAccess', _92 => _92.image]), () => ( _optionalChain([meta, 'optionalAccess', _93 => _93.image_url]))));
3247
+ }
3248
+
3249
+ // src/core/ensHint.ts
3250
+ var _ens = require('viem/ens');
3251
+ var ENS_HINT_KEY = "id.thurin";
3252
+ function ensHintFor(record, verifiedFingerprint) {
3253
+ const raw = _nullishCoalesce(_optionalChain([record, 'optionalAccess', _94 => _94.trim, 'call', _95 => _95()]), () => ( ""));
3254
+ const expected = verifiedFingerprint ? normalizeFingerprint(verifiedFingerprint) : null;
3255
+ if (!raw) return { key: ENS_HINT_KEY, state: "unset", record: null, fingerprint: null, expected };
3256
+ const fingerprint = normalizeFingerprint(raw);
3257
+ if (!fingerprint) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint: null, expected, reason: "The record is not a PGP fingerprint" };
3258
+ if (!expected) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The address has no verified claim" };
3259
+ if (fingerprint === expected) return { key: ENS_HINT_KEY, state: "match", record: raw, fingerprint, expected };
3260
+ return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The record names a key this address has not claimed" };
3261
+ }
3262
+ function ensHintValue(fingerprint) {
3263
+ const fp = normalizeFingerprint(fingerprint);
3264
+ if (!fp) throw new Error(`Not a fingerprint: ${fingerprint}`);
3265
+ return fp.toUpperCase();
3266
+ }
3267
+ var ENS_TEXT_RESOLVER_ABI = [
3268
+ { type: "function", name: "text", stateMutability: "view", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }], outputs: [{ name: "", type: "string" }] },
3269
+ { type: "function", name: "setText", stateMutability: "nonpayable", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }, { name: "value", type: "string" }], outputs: [] }
3270
+ ];
3271
+ function ensHintWrite(name, fingerprint) {
3272
+ const normalized = _ens.normalize.call(void 0, name);
3273
+ return {
3274
+ abi: ENS_TEXT_RESOLVER_ABI,
3275
+ functionName: "setText",
3276
+ args: [_ens.namehash.call(void 0, normalized), ENS_HINT_KEY, ensHintValue(fingerprint)],
3277
+ name: normalized
3278
+ };
3279
+ }
3280
+ async function fetchEnsHint(client, name, verifiedFingerprint) {
3281
+ const record = await client.getEnsText({ name: _ens.normalize.call(void 0, name), key: ENS_HINT_KEY });
3282
+ return ensHintFor(record, verifiedFingerprint);
3283
+ }
3284
+
3285
+
3286
+
3287
+
3288
+
3289
+
3290
+
3291
+
3292
+
3293
+
3294
+
3295
+
3296
+
3297
+
3298
+
3299
+
3300
+
3301
+
3302
+
3303
+
3304
+
3305
+
3306
+
3307
+
3308
+
3309
+
3310
+
3311
+
3312
+
3313
+
3314
+
3315
+
3316
+
3317
+
3318
+
3319
+
3320
+
3321
+
3322
+
3323
+
3324
+
3325
+
3326
+
3327
+
3328
+
3329
+
3330
+
3331
+
3332
+
3333
+
3334
+
3335
+
3336
+
3337
+
3338
+
3339
+
3340
+
3341
+
3342
+
3343
+
3344
+
3345
+
3346
+
3347
+
3348
+
3349
+
3350
+
3351
+
3352
+
3353
+
3354
+
3355
+
3356
+
3357
+
3358
+
3359
+
3360
+
3361
+
3362
+
3363
+
3364
+
3365
+
3366
+
3367
+
3368
+
3369
+
3370
+
3371
+
3372
+
3373
+
3374
+
3375
+
3376
+
3377
+ exports.normalizeFingerprint = normalizeFingerprint; exports.sameFingerprint = sameFingerprint; exports.fingerprintToBytes = fingerprintToBytes; exports.bytesToFingerprint = bytesToFingerprint; exports.fingerprintHash = fingerprintHash; exports.keyIdOf = keyIdOf; exports.keyIdToBytes = keyIdToBytes; exports.identifyProof = identifyProof; exports.displayUrl = displayUrl; exports.proofHref = proofHref; exports.proofSecondaryHref = proofSecondaryHref; exports.FARCASTER_HUB = FARCASTER_HUB; exports.verifyProof = verifyProof; exports.statementText = statementText; exports.parsePgpKey = parsePgpKey; exports.verifyClearsigned = verifyClearsigned; exports.verifyStatementSignature = verifyStatementSignature; exports.verifyAttestation = verifyAttestation; exports.stripEmailUserIDs = stripEmailUserIDs; exports.hasEmailUserID = hasEmailUserID; exports.leanKey = leanKey; exports.claimSignature = claimSignature; exports.signatureEmail = signatureEmail; exports.payloadText = payloadText; exports.sshKeys = sshKeys; exports.EIP712_NAME = EIP712_NAME; exports.EIP712_VERSION = EIP712_VERSION; exports.registryDomain = registryDomain; exports.AUTHORIZATION_TYPES = AUTHORIZATION_TYPES; exports.REVOKE_REASONS = REVOKE_REASONS; exports.OWNER_REVOKE_REASONS = OWNER_REVOKE_REASONS; exports.attestTypedData = attestTypedData; exports.reattestTypedData = reattestTypedData; exports.updateKeyTypedData = updateKeyTypedData; exports.revokeTypedData = revokeTypedData; exports.setRecordTypedData = setRecordTypedData; exports.markCompromisedTypedData = markCompromisedTypedData; exports.authorizationDigest = authorizationDigest; exports.recordKind = recordKind; exports.REGISTRY_ABI = REGISTRY_ABI; exports.REGISTRY_ADDRESS = REGISTRY_ADDRESS; exports.NETWORKS = NETWORKS; exports.isNetworkName = isNetworkName; exports.getRegistry = getRegistry; exports.chainFor = chainFor; exports.CLAIM_LIMIT = CLAIM_LIMIT; exports.readClaims = readClaims; exports.keyStanding = keyStanding; exports.findOwners = findOwners; exports.CLAIM_CHECK_LABEL = CLAIM_CHECK_LABEL; exports.formatClaimDate = formatClaimDate; exports.claimCheckText = claimCheckText; exports.keyProblemText = keyProblemText; exports.expiresSoon = expiresSoon; exports.expiresSoonText = expiresSoonText; exports.claimFates = claimFates; exports.claimFateText = claimFateText; exports.encryptionKeyFor = encryptionKeyFor; exports.encryptRefusalText = encryptRefusalText; exports.keyChangedText = keyChangedText; exports.encryptTo = encryptTo; exports.CONTRACT_ERROR_TEXT = CONTRACT_ERROR_TEXT; exports.contractErrorName = contractErrorName; exports.contractErrorText = contractErrorText; exports.MAX_RECORD_BYTES = MAX_RECORD_BYTES; exports.MAX_KIND_BYTES = MAX_KIND_BYTES; exports.kindName = kindName; exports.checkKindName = checkKindName; exports.checkRecordValue = checkRecordValue; exports.KNOWN_KINDS = KNOWN_KINDS; exports.parseReleases = parseReleases; exports.addRelease = addRelease; exports.renderReleases = renderReleases; exports.parseRecord = parseRecord; exports.pickRecords = pickRecords; exports.pageRecords = pageRecords; exports.fetchRecords = fetchRecords; exports.IPFS_GATEWAYS = IPFS_GATEWAYS; exports.IPFS_GATEWAY = IPFS_GATEWAY; exports.ARWEAVE_GATEWAY = ARWEAVE_GATEWAY; exports.avatarUrl = avatarUrl; exports.avatarFallbacks = avatarFallbacks; exports.parseNftAvatar = parseNftAvatar; exports.NFT_AVATAR_ABI = NFT_AVATAR_ABI; exports.nftAvatarImage = nftAvatarImage; exports.ENS_HINT_KEY = ENS_HINT_KEY; exports.ensHintFor = ensHintFor; exports.ensHintValue = ensHintValue; exports.ENS_TEXT_RESOLVER_ABI = ENS_TEXT_RESOLVER_ABI; exports.ensHintWrite = ensHintWrite; exports.fetchEnsHint = fetchEnsHint;