@thurinlabs/identity-kit 1.4.0 → 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.
- package/README.md +44 -384
- package/dist/{chunk-ZAQ2EPF5.js → chunk-AAJWERE2.js} +2455 -1343
- package/dist/{chunk-M2OJDB7I.cjs → chunk-DJGCYNJN.cjs} +2460 -1348
- package/dist/core.cjs +44 -3
- package/dist/core.d.cts +1563 -903
- package/dist/core.d.ts +1563 -903
- package/dist/core.js +65 -24
- package/dist/index.cjs +25 -459
- package/dist/index.d.cts +1 -83
- package/dist/index.d.ts +1 -83
- package/dist/index.js +65 -499
- package/package.json +5 -22
- package/dist/chunk-M2OJDB7I.cjs.map +0 -1
- package/dist/chunk-ZAQ2EPF5.js.map +0 -1
- package/dist/core.cjs.map +0 -1
- package/dist/core.js.map +0 -1
- package/dist/embed.global.js +0 -32213
- package/dist/embed.global.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.css +0 -185
- package/dist/index.css.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -1,237 +1,1162 @@
|
|
|
1
|
-
// src/core/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
// src/core/fingerprint.ts
|
|
2
|
+
import { keccak256 } from "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 keccak256(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 = [
|
|
29
39
|
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
"name": "",
|
|
36
|
-
"type": "string",
|
|
37
|
-
"internalType": "string"
|
|
38
|
-
}
|
|
39
|
-
],
|
|
40
|
-
"stateMutability": "view"
|
|
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] })
|
|
41
44
|
},
|
|
42
45
|
{
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"type": "string",
|
|
50
|
-
"internalType": "string"
|
|
51
|
-
}
|
|
52
|
-
],
|
|
53
|
-
"stateMutability": "view"
|
|
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] })
|
|
54
52
|
},
|
|
55
53
|
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"name": "",
|
|
62
|
-
"type": "uint256",
|
|
63
|
-
"internalType": "uint256"
|
|
64
|
-
}
|
|
65
|
-
],
|
|
66
|
-
"stateMutability": "view"
|
|
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] })
|
|
67
59
|
},
|
|
68
60
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
"name": "",
|
|
75
|
-
"type": "uint256",
|
|
76
|
-
"internalType": "uint256"
|
|
77
|
-
}
|
|
78
|
-
],
|
|
79
|
-
"stateMutability": "view"
|
|
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] })
|
|
80
65
|
},
|
|
81
66
|
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
-
"name": "",
|
|
88
|
-
"type": "uint256",
|
|
89
|
-
"internalType": "uint256"
|
|
90
|
-
}
|
|
91
|
-
],
|
|
92
|
-
"stateMutability": "view"
|
|
67
|
+
provider: "codeberg",
|
|
68
|
+
label: "Codeberg",
|
|
69
|
+
pattern: /^https:\/\/codeberg\.org\/([^/]+)\/([^/]+)$/i,
|
|
70
|
+
parse: (m) => ({ user: m[1], repo: m[2] })
|
|
93
71
|
},
|
|
94
72
|
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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 (data.owner?.login?.toLowerCase() !== 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 };
|
|
103
133
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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 (data.owner?.login?.toLowerCase() !== 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 };
|
|
116
170
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
"name": "",
|
|
153
|
-
"type": "bytes32",
|
|
154
|
-
"internalType": "bytes32"
|
|
155
|
-
}
|
|
156
|
-
],
|
|
157
|
-
"stateMutability": "view"
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
"type": "function",
|
|
161
|
-
"name": "VERSION",
|
|
162
|
-
"inputs": [],
|
|
163
|
-
"outputs": [
|
|
164
|
-
{
|
|
165
|
-
"name": "",
|
|
166
|
-
"type": "uint8",
|
|
167
|
-
"internalType": "uint8"
|
|
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 = msg.data?.castAddBody?.text || "";
|
|
202
|
+
if (containsFingerprint(text, fingerprint)) {
|
|
203
|
+
return { verified: true };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
168
206
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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 (data.owner?.login?.toLowerCase() !== 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 };
|
|
180
248
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"internalType": "address[]"
|
|
249
|
+
}
|
|
250
|
+
if (data.note) {
|
|
251
|
+
const text = data.note.replace(/<[^>]*>/g, "");
|
|
252
|
+
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
253
|
+
return { verified: true };
|
|
187
254
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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 } : opts ?? {});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/core/pgp.ts
|
|
275
|
+
function pgpConfig(openpgp) {
|
|
276
|
+
const rejectCurves = new Set([...openpgp.config.rejectCurves ?? []].filter((c) => c !== "secp256k1"));
|
|
277
|
+
return { ...openpgp.config, rejectCurves };
|
|
278
|
+
}
|
|
279
|
+
function newestSignature(sigs = []) {
|
|
280
|
+
return [...sigs].sort((x, y) => (y.created?.getTime?.() ?? 0) - (x.created?.getTime?.() ?? 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 {
|
|
298
|
+
return String(keyOrSubkey.keyPacket?.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 import("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 = key.keyPacket.created?.toISOString() ?? 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 {
|
|
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
|
+
}
|
|
199
378
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"name": "",
|
|
204
|
-
"type": "uint256",
|
|
205
|
-
"internalType": "uint256"
|
|
379
|
+
if (userVerified) {
|
|
380
|
+
const uid = user.userID?.userID;
|
|
381
|
+
if (uid) userIDs.push(uid);
|
|
206
382
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
383
|
+
}
|
|
384
|
+
const now = /* @__PURE__ */ new Date();
|
|
385
|
+
const subkeys = await Promise.all(key.subkeys.map(async (sk) => {
|
|
386
|
+
const flags = newestSignature(sk.bindingSignatures)?.keyFlags?.[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: sk.keyPacket.created?.toISOString() ?? 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 {
|
|
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 import("openpgp");
|
|
425
|
+
const publicKey = await readKeyAny(openpgp, armoredKey);
|
|
426
|
+
const message = await openpgp.readCleartextMessage({ cleartextMessage: clearsigned });
|
|
427
|
+
const text = 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 import("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 = [...sigs ?? []].sort((a, b) => (b.created?.getTime?.() ?? 0) - (a.created?.getTime?.() ?? 0))[0];
|
|
455
|
+
const flag = sig?.reasonForRevocationFlag ?? null;
|
|
456
|
+
return {
|
|
457
|
+
at: iso(sig?.created),
|
|
458
|
+
compromised: flag === 2,
|
|
459
|
+
revocationReason: sig?.reasonForRevocationString || (flag !== null ? 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) ? 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 {
|
|
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(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 import("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 {
|
|
515
|
+
return { verified: false, reason: "Unreadable signature", kind: "bad-signature" };
|
|
516
|
+
}
|
|
517
|
+
const life = await keyLife(publicKey, sig.packets[0]?.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 {
|
|
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 sig?.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 import("openpgp");
|
|
548
|
+
const key = await readKeyAny(openpgp, armoredKey);
|
|
549
|
+
const kept = [];
|
|
550
|
+
const removed = [];
|
|
551
|
+
const users = key.users.filter((u) => {
|
|
552
|
+
const uid = u.userID?.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 {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async function hasEmailUserID(armoredKey) {
|
|
569
|
+
try {
|
|
570
|
+
const openpgp = await import("openpgp");
|
|
571
|
+
const key = await readKeyAny(openpgp, armoredKey);
|
|
572
|
+
return key.users.some((u) => EMAIL_UID.test(u.userID?.userID ?? ""));
|
|
573
|
+
} catch {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
async function leanKey(input, { includeEmail = false } = {}) {
|
|
578
|
+
try {
|
|
579
|
+
const openpgp = await import("openpgp");
|
|
580
|
+
const key = await readKeyAny(openpgp, input);
|
|
581
|
+
const kept = [];
|
|
582
|
+
const removed = [];
|
|
583
|
+
const newest = (sigs = []) => sigs.length ? [[...sigs].sort((x, y) => (y.created?.getTime?.() ?? 0) - (x.created?.getTime?.() ?? 0))[0]] : [];
|
|
584
|
+
for (const u of key.users) {
|
|
585
|
+
u.selfCertifications = u.revocationSignatures?.length ? [] : newest(u.selfCertifications);
|
|
586
|
+
u.otherCertifications = [];
|
|
587
|
+
}
|
|
588
|
+
const users = key.users.filter((u) => {
|
|
589
|
+
const uid = u.userID?.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 {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async function claimSignature({ signature, key, address }) {
|
|
609
|
+
try {
|
|
610
|
+
const openpgp = await import("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 {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async function signatureEmail(signature) {
|
|
625
|
+
try {
|
|
626
|
+
const openpgp = await import("openpgp");
|
|
627
|
+
const { packets } = await readSignatureAny(openpgp, signature);
|
|
628
|
+
for (const p of packets) {
|
|
629
|
+
const uid = p.signersUserID ?? null;
|
|
630
|
+
if (uid && uid.includes("@")) return uid;
|
|
631
|
+
}
|
|
632
|
+
return null;
|
|
633
|
+
} catch {
|
|
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 import("openpgp");
|
|
642
|
+
if (kind === "key") return (await openpgp.readKey({ binaryKey: v })).armor();
|
|
643
|
+
return (await openpgp.readSignature({ binarySignature: v })).armor();
|
|
644
|
+
} catch {
|
|
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 (pp.oid?.getName?.() !== "ed25519Legacy" || pp.Q?.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" }[pp.oid?.getName?.()];
|
|
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 import("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 {
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
const candidates = [];
|
|
709
|
+
const direct = newestSignature(key.directSignatures);
|
|
710
|
+
const primarySig = direct?.keyFlags ? direct : (await key.getPrimaryUser(now, void 0, config)).selfCertification;
|
|
711
|
+
candidates.push({ packet: key.keyPacket, flags: primarySig?.keyFlags?.[0] ?? 0 });
|
|
712
|
+
for (const sk of key.subkeys) {
|
|
713
|
+
try {
|
|
714
|
+
await sk.verify(now, config);
|
|
715
|
+
} catch {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
candidates.push({ packet: sk.keyPacket, flags: newestSignature(sk.bindingSignatures)?.keyFlags?.[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 {
|
|
734
|
+
return [];
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/core/authorization.ts
|
|
739
|
+
import { hashTypedData, keccak256 as keccak2562, stringToHex } from "viem";
|
|
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 : stringToHex(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: 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 hashTypedData(typedData);
|
|
864
|
+
}
|
|
865
|
+
function recordKind(name) {
|
|
866
|
+
return keccak2562(stringToHex(name));
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/core/contract.ts
|
|
870
|
+
import { mainnet, sepolia, foundry } from "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": "",
|
|
216
881
|
"type": "bytes32",
|
|
217
882
|
"internalType": "bytes32"
|
|
218
|
-
}
|
|
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": [
|
|
219
957
|
{
|
|
220
|
-
"name": "
|
|
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": "",
|
|
221
1024
|
"type": "uint256",
|
|
222
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"
|
|
223
1148
|
},
|
|
224
1149
|
{
|
|
225
|
-
"name": "
|
|
1150
|
+
"name": "index",
|
|
226
1151
|
"type": "uint256",
|
|
227
1152
|
"internalType": "uint256"
|
|
228
1153
|
}
|
|
229
1154
|
],
|
|
230
1155
|
"outputs": [
|
|
231
1156
|
{
|
|
232
|
-
"name": "
|
|
233
|
-
"type": "
|
|
234
|
-
"internalType": "
|
|
1157
|
+
"name": "",
|
|
1158
|
+
"type": "string",
|
|
1159
|
+
"internalType": "string"
|
|
235
1160
|
}
|
|
236
1161
|
],
|
|
237
1162
|
"stateMutability": "view"
|
|
@@ -246,12 +1171,12 @@ var REGISTRY_ABI = [
|
|
|
246
1171
|
"internalType": "bytes"
|
|
247
1172
|
},
|
|
248
1173
|
{
|
|
249
|
-
"name": "
|
|
1174
|
+
"name": "signature",
|
|
250
1175
|
"type": "bytes",
|
|
251
1176
|
"internalType": "bytes"
|
|
252
1177
|
},
|
|
253
1178
|
{
|
|
254
|
-
"name": "
|
|
1179
|
+
"name": "key",
|
|
255
1180
|
"type": "bytes",
|
|
256
1181
|
"internalType": "bytes"
|
|
257
1182
|
}
|
|
@@ -280,38 +1205,126 @@ var REGISTRY_ABI = [
|
|
|
280
1205
|
"internalType": "bytes"
|
|
281
1206
|
},
|
|
282
1207
|
{
|
|
283
|
-
"name": "
|
|
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",
|
|
284
1224
|
"type": "bytes",
|
|
285
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
|
+
]
|
|
286
1306
|
},
|
|
287
1307
|
{
|
|
288
|
-
"name": "
|
|
289
|
-
"type": "
|
|
290
|
-
"internalType": "
|
|
1308
|
+
"name": "key",
|
|
1309
|
+
"type": "string",
|
|
1310
|
+
"internalType": "string"
|
|
291
1311
|
},
|
|
292
1312
|
{
|
|
293
|
-
"name": "
|
|
294
|
-
"type": "
|
|
295
|
-
"internalType": "
|
|
1313
|
+
"name": "statement",
|
|
1314
|
+
"type": "string",
|
|
1315
|
+
"internalType": "string"
|
|
296
1316
|
},
|
|
297
1317
|
{
|
|
298
|
-
"name": "
|
|
299
|
-
"type": "
|
|
300
|
-
"internalType": "
|
|
301
|
-
}
|
|
302
|
-
],
|
|
303
|
-
"outputs": [
|
|
304
|
-
{
|
|
305
|
-
"name": "index",
|
|
306
|
-
"type": "uint256",
|
|
307
|
-
"internalType": "uint256"
|
|
1318
|
+
"name": "recordKinds",
|
|
1319
|
+
"type": "string[]",
|
|
1320
|
+
"internalType": "string[]"
|
|
308
1321
|
}
|
|
309
1322
|
],
|
|
310
|
-
"stateMutability": "
|
|
1323
|
+
"stateMutability": "view"
|
|
311
1324
|
},
|
|
312
1325
|
{
|
|
313
1326
|
"type": "function",
|
|
314
|
-
"name": "
|
|
1327
|
+
"name": "claimCount",
|
|
315
1328
|
"inputs": [
|
|
316
1329
|
{
|
|
317
1330
|
"name": "owner",
|
|
@@ -330,7 +1343,7 @@ var REGISTRY_ABI = [
|
|
|
330
1343
|
},
|
|
331
1344
|
{
|
|
332
1345
|
"type": "function",
|
|
333
|
-
"name": "
|
|
1346
|
+
"name": "claimsOf",
|
|
334
1347
|
"inputs": [
|
|
335
1348
|
{
|
|
336
1349
|
"name": "owner",
|
|
@@ -340,10 +1353,15 @@ var REGISTRY_ABI = [
|
|
|
340
1353
|
],
|
|
341
1354
|
"outputs": [
|
|
342
1355
|
{
|
|
343
|
-
"name": "",
|
|
1356
|
+
"name": "out",
|
|
344
1357
|
"type": "tuple[]",
|
|
345
|
-
"internalType": "struct PGPRegistry.
|
|
1358
|
+
"internalType": "struct PGPRegistry.ClaimView[]",
|
|
346
1359
|
"components": [
|
|
1360
|
+
{
|
|
1361
|
+
"name": "index",
|
|
1362
|
+
"type": "uint256",
|
|
1363
|
+
"internalType": "uint256"
|
|
1364
|
+
},
|
|
347
1365
|
{
|
|
348
1366
|
"name": "fingerprint",
|
|
349
1367
|
"type": "bytes",
|
|
@@ -360,19 +1378,24 @@ var REGISTRY_ABI = [
|
|
|
360
1378
|
"internalType": "uint64"
|
|
361
1379
|
},
|
|
362
1380
|
{
|
|
363
|
-
"name": "
|
|
364
|
-
"type": "
|
|
365
|
-
"internalType": "
|
|
1381
|
+
"name": "state",
|
|
1382
|
+
"type": "string",
|
|
1383
|
+
"internalType": "string"
|
|
366
1384
|
},
|
|
367
1385
|
{
|
|
368
|
-
"name": "
|
|
369
|
-
"type": "
|
|
370
|
-
"internalType": "
|
|
1386
|
+
"name": "replacedBy",
|
|
1387
|
+
"type": "uint256",
|
|
1388
|
+
"internalType": "uint256"
|
|
371
1389
|
},
|
|
372
1390
|
{
|
|
373
|
-
"name": "
|
|
374
|
-
"type": "
|
|
375
|
-
"internalType": "
|
|
1391
|
+
"name": "revokeReason",
|
|
1392
|
+
"type": "string",
|
|
1393
|
+
"internalType": "string"
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
"name": "messageVersion",
|
|
1397
|
+
"type": "uint8",
|
|
1398
|
+
"internalType": "uint8"
|
|
376
1399
|
}
|
|
377
1400
|
]
|
|
378
1401
|
}
|
|
@@ -381,7 +1404,7 @@ var REGISTRY_ABI = [
|
|
|
381
1404
|
},
|
|
382
1405
|
{
|
|
383
1406
|
"type": "function",
|
|
384
|
-
"name": "
|
|
1407
|
+
"name": "claimsOfRange",
|
|
385
1408
|
"inputs": [
|
|
386
1409
|
{
|
|
387
1410
|
"name": "owner",
|
|
@@ -403,8 +1426,13 @@ var REGISTRY_ABI = [
|
|
|
403
1426
|
{
|
|
404
1427
|
"name": "out",
|
|
405
1428
|
"type": "tuple[]",
|
|
406
|
-
"internalType": "struct PGPRegistry.
|
|
1429
|
+
"internalType": "struct PGPRegistry.ClaimView[]",
|
|
407
1430
|
"components": [
|
|
1431
|
+
{
|
|
1432
|
+
"name": "index",
|
|
1433
|
+
"type": "uint256",
|
|
1434
|
+
"internalType": "uint256"
|
|
1435
|
+
},
|
|
408
1436
|
{
|
|
409
1437
|
"name": "fingerprint",
|
|
410
1438
|
"type": "bytes",
|
|
@@ -421,19 +1449,24 @@ var REGISTRY_ABI = [
|
|
|
421
1449
|
"internalType": "uint64"
|
|
422
1450
|
},
|
|
423
1451
|
{
|
|
424
|
-
"name": "
|
|
425
|
-
"type": "
|
|
426
|
-
"internalType": "
|
|
1452
|
+
"name": "state",
|
|
1453
|
+
"type": "string",
|
|
1454
|
+
"internalType": "string"
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
"name": "replacedBy",
|
|
1458
|
+
"type": "uint256",
|
|
1459
|
+
"internalType": "uint256"
|
|
427
1460
|
},
|
|
428
1461
|
{
|
|
429
|
-
"name": "
|
|
430
|
-
"type": "
|
|
431
|
-
"internalType": "
|
|
1462
|
+
"name": "revokeReason",
|
|
1463
|
+
"type": "string",
|
|
1464
|
+
"internalType": "string"
|
|
432
1465
|
},
|
|
433
1466
|
{
|
|
434
|
-
"name": "
|
|
435
|
-
"type": "
|
|
436
|
-
"internalType": "
|
|
1467
|
+
"name": "messageVersion",
|
|
1468
|
+
"type": "uint8",
|
|
1469
|
+
"internalType": "uint8"
|
|
437
1470
|
}
|
|
438
1471
|
]
|
|
439
1472
|
}
|
|
@@ -442,10 +1475,27 @@ var REGISTRY_ABI = [
|
|
|
442
1475
|
},
|
|
443
1476
|
{
|
|
444
1477
|
"type": "function",
|
|
445
|
-
"name": "
|
|
446
|
-
"inputs": [
|
|
447
|
-
|
|
448
|
-
|
|
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"
|
|
449
1499
|
},
|
|
450
1500
|
{
|
|
451
1501
|
"type": "function",
|
|
@@ -469,10 +1519,15 @@ var REGISTRY_ABI = [
|
|
|
469
1519
|
"internalType": "uint256"
|
|
470
1520
|
},
|
|
471
1521
|
{
|
|
472
|
-
"name": "
|
|
1522
|
+
"name": "details",
|
|
473
1523
|
"type": "tuple",
|
|
474
|
-
"internalType": "struct PGPRegistry.
|
|
1524
|
+
"internalType": "struct PGPRegistry.ClaimView",
|
|
475
1525
|
"components": [
|
|
1526
|
+
{
|
|
1527
|
+
"name": "index",
|
|
1528
|
+
"type": "uint256",
|
|
1529
|
+
"internalType": "uint256"
|
|
1530
|
+
},
|
|
476
1531
|
{
|
|
477
1532
|
"name": "fingerprint",
|
|
478
1533
|
"type": "bytes",
|
|
@@ -489,25 +1544,73 @@ var REGISTRY_ABI = [
|
|
|
489
1544
|
"internalType": "uint64"
|
|
490
1545
|
},
|
|
491
1546
|
{
|
|
492
|
-
"name": "
|
|
493
|
-
"type": "
|
|
494
|
-
"internalType": "
|
|
1547
|
+
"name": "state",
|
|
1548
|
+
"type": "string",
|
|
1549
|
+
"internalType": "string"
|
|
495
1550
|
},
|
|
496
1551
|
{
|
|
497
|
-
"name": "
|
|
498
|
-
"type": "
|
|
499
|
-
"internalType": "
|
|
1552
|
+
"name": "replacedBy",
|
|
1553
|
+
"type": "uint256",
|
|
1554
|
+
"internalType": "uint256"
|
|
500
1555
|
},
|
|
501
1556
|
{
|
|
502
|
-
"name": "
|
|
503
|
-
"type": "
|
|
504
|
-
"internalType": "
|
|
1557
|
+
"name": "revokeReason",
|
|
1558
|
+
"type": "string",
|
|
1559
|
+
"internalType": "string"
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
"name": "messageVersion",
|
|
1563
|
+
"type": "uint8",
|
|
1564
|
+
"internalType": "uint8"
|
|
505
1565
|
}
|
|
506
1566
|
]
|
|
507
1567
|
}
|
|
508
1568
|
],
|
|
509
1569
|
"stateMutability": "view"
|
|
510
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
|
+
},
|
|
511
1614
|
{
|
|
512
1615
|
"type": "function",
|
|
513
1616
|
"name": "fingerprintsForKeyId",
|
|
@@ -520,21 +1623,164 @@ var REGISTRY_ABI = [
|
|
|
520
1623
|
],
|
|
521
1624
|
"outputs": [
|
|
522
1625
|
{
|
|
523
|
-
"name": "",
|
|
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",
|
|
524
1770
|
"type": "bytes[]",
|
|
525
1771
|
"internalType": "bytes[]"
|
|
526
1772
|
}
|
|
527
1773
|
],
|
|
528
|
-
"stateMutability": "
|
|
1774
|
+
"stateMutability": "nonpayable"
|
|
529
1775
|
},
|
|
530
1776
|
{
|
|
531
1777
|
"type": "function",
|
|
532
|
-
"name": "
|
|
1778
|
+
"name": "nonces",
|
|
533
1779
|
"inputs": [
|
|
534
1780
|
{
|
|
535
|
-
"name": "
|
|
536
|
-
"type": "
|
|
537
|
-
"internalType": "
|
|
1781
|
+
"name": "owner",
|
|
1782
|
+
"type": "address",
|
|
1783
|
+
"internalType": "address"
|
|
538
1784
|
}
|
|
539
1785
|
],
|
|
540
1786
|
"outputs": [
|
|
@@ -548,133 +1794,67 @@ var REGISTRY_ABI = [
|
|
|
548
1794
|
},
|
|
549
1795
|
{
|
|
550
1796
|
"type": "function",
|
|
551
|
-
"name": "
|
|
1797
|
+
"name": "ownersOf",
|
|
552
1798
|
"inputs": [
|
|
553
1799
|
{
|
|
554
|
-
"name": "
|
|
555
|
-
"type": "
|
|
556
|
-
"internalType": "
|
|
557
|
-
},
|
|
558
|
-
{
|
|
559
|
-
"name": "start",
|
|
560
|
-
"type": "uint256",
|
|
561
|
-
"internalType": "uint256"
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
"name": "count",
|
|
565
|
-
"type": "uint256",
|
|
566
|
-
"internalType": "uint256"
|
|
1800
|
+
"name": "fingerprint",
|
|
1801
|
+
"type": "bytes",
|
|
1802
|
+
"internalType": "bytes"
|
|
567
1803
|
}
|
|
568
1804
|
],
|
|
569
1805
|
"outputs": [
|
|
570
1806
|
{
|
|
571
|
-
"name": "
|
|
572
|
-
"type": "
|
|
573
|
-
"internalType": "
|
|
1807
|
+
"name": "",
|
|
1808
|
+
"type": "address[]",
|
|
1809
|
+
"internalType": "address[]"
|
|
574
1810
|
}
|
|
575
1811
|
],
|
|
576
1812
|
"stateMutability": "view"
|
|
577
1813
|
},
|
|
578
1814
|
{
|
|
579
1815
|
"type": "function",
|
|
580
|
-
"name": "
|
|
1816
|
+
"name": "ownersOfCount",
|
|
581
1817
|
"inputs": [
|
|
582
1818
|
{
|
|
583
|
-
"name": "
|
|
584
|
-
"type": "
|
|
585
|
-
"internalType": "
|
|
586
|
-
},
|
|
587
|
-
{
|
|
588
|
-
"name": "index",
|
|
589
|
-
"type": "uint256",
|
|
590
|
-
"internalType": "uint256"
|
|
1819
|
+
"name": "fingerprint",
|
|
1820
|
+
"type": "bytes",
|
|
1821
|
+
"internalType": "bytes"
|
|
591
1822
|
}
|
|
592
1823
|
],
|
|
593
1824
|
"outputs": [
|
|
594
1825
|
{
|
|
595
1826
|
"name": "",
|
|
596
|
-
"type": "
|
|
597
|
-
"internalType": "
|
|
598
|
-
"components": [
|
|
599
|
-
{
|
|
600
|
-
"name": "fingerprint",
|
|
601
|
-
"type": "bytes",
|
|
602
|
-
"internalType": "bytes"
|
|
603
|
-
},
|
|
604
|
-
{
|
|
605
|
-
"name": "createdAt",
|
|
606
|
-
"type": "uint64",
|
|
607
|
-
"internalType": "uint64"
|
|
608
|
-
},
|
|
609
|
-
{
|
|
610
|
-
"name": "revokedAt",
|
|
611
|
-
"type": "uint64",
|
|
612
|
-
"internalType": "uint64"
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
"name": "messageVersion",
|
|
616
|
-
"type": "uint8",
|
|
617
|
-
"internalType": "uint8"
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
"name": "keyPtr",
|
|
621
|
-
"type": "address",
|
|
622
|
-
"internalType": "address"
|
|
623
|
-
},
|
|
624
|
-
{
|
|
625
|
-
"name": "sigPtr",
|
|
626
|
-
"type": "address",
|
|
627
|
-
"internalType": "address"
|
|
628
|
-
}
|
|
629
|
-
]
|
|
1827
|
+
"type": "uint256",
|
|
1828
|
+
"internalType": "uint256"
|
|
630
1829
|
}
|
|
631
1830
|
],
|
|
632
1831
|
"stateMutability": "view"
|
|
633
1832
|
},
|
|
634
1833
|
{
|
|
635
1834
|
"type": "function",
|
|
636
|
-
"name": "
|
|
1835
|
+
"name": "ownersOfRange",
|
|
637
1836
|
"inputs": [
|
|
638
1837
|
{
|
|
639
|
-
"name": "
|
|
640
|
-
"type": "
|
|
641
|
-
"internalType": "
|
|
1838
|
+
"name": "fingerprint",
|
|
1839
|
+
"type": "bytes",
|
|
1840
|
+
"internalType": "bytes"
|
|
642
1841
|
},
|
|
643
1842
|
{
|
|
644
|
-
"name": "
|
|
1843
|
+
"name": "start",
|
|
645
1844
|
"type": "uint256",
|
|
646
1845
|
"internalType": "uint256"
|
|
647
|
-
}
|
|
648
|
-
],
|
|
649
|
-
"outputs": [
|
|
650
|
-
{
|
|
651
|
-
"name": "pgpSignature",
|
|
652
|
-
"type": "bytes",
|
|
653
|
-
"internalType": "bytes"
|
|
654
1846
|
},
|
|
655
1847
|
{
|
|
656
|
-
"name": "
|
|
657
|
-
"type": "
|
|
658
|
-
"internalType": "
|
|
659
|
-
}
|
|
660
|
-
],
|
|
661
|
-
"stateMutability": "view"
|
|
662
|
-
},
|
|
663
|
-
{
|
|
664
|
-
"type": "function",
|
|
665
|
-
"name": "nonces",
|
|
666
|
-
"inputs": [
|
|
667
|
-
{
|
|
668
|
-
"name": "",
|
|
669
|
-
"type": "address",
|
|
670
|
-
"internalType": "address"
|
|
1848
|
+
"name": "count",
|
|
1849
|
+
"type": "uint256",
|
|
1850
|
+
"internalType": "uint256"
|
|
671
1851
|
}
|
|
672
1852
|
],
|
|
673
1853
|
"outputs": [
|
|
674
1854
|
{
|
|
675
|
-
"name": "",
|
|
676
|
-
"type": "
|
|
677
|
-
"internalType": "
|
|
1855
|
+
"name": "out",
|
|
1856
|
+
"type": "address[]",
|
|
1857
|
+
"internalType": "address[]"
|
|
678
1858
|
}
|
|
679
1859
|
],
|
|
680
1860
|
"stateMutability": "view"
|
|
@@ -694,14 +1874,19 @@ var REGISTRY_ABI = [
|
|
|
694
1874
|
"internalType": "bytes"
|
|
695
1875
|
},
|
|
696
1876
|
{
|
|
697
|
-
"name": "
|
|
1877
|
+
"name": "signature",
|
|
698
1878
|
"type": "bytes",
|
|
699
1879
|
"internalType": "bytes"
|
|
700
1880
|
},
|
|
701
1881
|
{
|
|
702
|
-
"name": "
|
|
1882
|
+
"name": "key",
|
|
703
1883
|
"type": "bytes",
|
|
704
1884
|
"internalType": "bytes"
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
"name": "keepRecords",
|
|
1888
|
+
"type": "bool",
|
|
1889
|
+
"internalType": "bool"
|
|
705
1890
|
}
|
|
706
1891
|
],
|
|
707
1892
|
"outputs": [
|
|
@@ -733,22 +1918,27 @@ var REGISTRY_ABI = [
|
|
|
733
1918
|
"internalType": "bytes"
|
|
734
1919
|
},
|
|
735
1920
|
{
|
|
736
|
-
"name": "
|
|
1921
|
+
"name": "signature",
|
|
737
1922
|
"type": "bytes",
|
|
738
1923
|
"internalType": "bytes"
|
|
739
1924
|
},
|
|
740
1925
|
{
|
|
741
|
-
"name": "
|
|
1926
|
+
"name": "key",
|
|
742
1927
|
"type": "bytes",
|
|
743
1928
|
"internalType": "bytes"
|
|
744
1929
|
},
|
|
1930
|
+
{
|
|
1931
|
+
"name": "keepRecords",
|
|
1932
|
+
"type": "bool",
|
|
1933
|
+
"internalType": "bool"
|
|
1934
|
+
},
|
|
745
1935
|
{
|
|
746
1936
|
"name": "deadline",
|
|
747
1937
|
"type": "uint256",
|
|
748
1938
|
"internalType": "uint256"
|
|
749
1939
|
},
|
|
750
1940
|
{
|
|
751
|
-
"name": "
|
|
1941
|
+
"name": "permission",
|
|
752
1942
|
"type": "bytes",
|
|
753
1943
|
"internalType": "bytes"
|
|
754
1944
|
}
|
|
@@ -764,7 +1954,7 @@ var REGISTRY_ABI = [
|
|
|
764
1954
|
},
|
|
765
1955
|
{
|
|
766
1956
|
"type": "function",
|
|
767
|
-
"name": "
|
|
1957
|
+
"name": "recordText",
|
|
768
1958
|
"inputs": [
|
|
769
1959
|
{
|
|
770
1960
|
"name": "owner",
|
|
@@ -778,15 +1968,44 @@ var REGISTRY_ABI = [
|
|
|
778
1968
|
},
|
|
779
1969
|
{
|
|
780
1970
|
"name": "kind",
|
|
781
|
-
"type": "
|
|
782
|
-
"internalType": "
|
|
1971
|
+
"type": "string",
|
|
1972
|
+
"internalType": "string"
|
|
783
1973
|
}
|
|
784
1974
|
],
|
|
785
1975
|
"outputs": [
|
|
786
1976
|
{
|
|
787
1977
|
"name": "",
|
|
788
|
-
"type": "
|
|
789
|
-
"internalType": "
|
|
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[]"
|
|
790
2009
|
}
|
|
791
2010
|
],
|
|
792
2011
|
"stateMutability": "view"
|
|
@@ -799,6 +2018,11 @@ var REGISTRY_ABI = [
|
|
|
799
2018
|
"name": "index",
|
|
800
2019
|
"type": "uint256",
|
|
801
2020
|
"internalType": "uint256"
|
|
2021
|
+
},
|
|
2022
|
+
{
|
|
2023
|
+
"name": "reason",
|
|
2024
|
+
"type": "string",
|
|
2025
|
+
"internalType": "string"
|
|
802
2026
|
}
|
|
803
2027
|
],
|
|
804
2028
|
"outputs": [],
|
|
@@ -818,13 +2042,18 @@ var REGISTRY_ABI = [
|
|
|
818
2042
|
"type": "uint256",
|
|
819
2043
|
"internalType": "uint256"
|
|
820
2044
|
},
|
|
2045
|
+
{
|
|
2046
|
+
"name": "reason",
|
|
2047
|
+
"type": "string",
|
|
2048
|
+
"internalType": "string"
|
|
2049
|
+
},
|
|
821
2050
|
{
|
|
822
2051
|
"name": "deadline",
|
|
823
2052
|
"type": "uint256",
|
|
824
2053
|
"internalType": "uint256"
|
|
825
2054
|
},
|
|
826
2055
|
{
|
|
827
|
-
"name": "
|
|
2056
|
+
"name": "permission",
|
|
828
2057
|
"type": "bytes",
|
|
829
2058
|
"internalType": "bytes"
|
|
830
2059
|
}
|
|
@@ -843,13 +2072,13 @@ var REGISTRY_ABI = [
|
|
|
843
2072
|
},
|
|
844
2073
|
{
|
|
845
2074
|
"name": "kind",
|
|
846
|
-
"type": "
|
|
847
|
-
"internalType": "
|
|
2075
|
+
"type": "string",
|
|
2076
|
+
"internalType": "string"
|
|
848
2077
|
},
|
|
849
2078
|
{
|
|
850
2079
|
"name": "value",
|
|
851
|
-
"type": "
|
|
852
|
-
"internalType": "
|
|
2080
|
+
"type": "string",
|
|
2081
|
+
"internalType": "string"
|
|
853
2082
|
}
|
|
854
2083
|
],
|
|
855
2084
|
"outputs": [],
|
|
@@ -871,13 +2100,13 @@ var REGISTRY_ABI = [
|
|
|
871
2100
|
},
|
|
872
2101
|
{
|
|
873
2102
|
"name": "kind",
|
|
874
|
-
"type": "
|
|
875
|
-
"internalType": "
|
|
2103
|
+
"type": "string",
|
|
2104
|
+
"internalType": "string"
|
|
876
2105
|
},
|
|
877
2106
|
{
|
|
878
2107
|
"name": "value",
|
|
879
|
-
"type": "
|
|
880
|
-
"internalType": "
|
|
2108
|
+
"type": "string",
|
|
2109
|
+
"internalType": "string"
|
|
881
2110
|
},
|
|
882
2111
|
{
|
|
883
2112
|
"name": "deadline",
|
|
@@ -885,7 +2114,7 @@ var REGISTRY_ABI = [
|
|
|
885
2114
|
"internalType": "uint256"
|
|
886
2115
|
},
|
|
887
2116
|
{
|
|
888
|
-
"name": "
|
|
2117
|
+
"name": "permission",
|
|
889
2118
|
"type": "bytes",
|
|
890
2119
|
"internalType": "bytes"
|
|
891
2120
|
}
|
|
@@ -893,6 +2122,83 @@ var REGISTRY_ABI = [
|
|
|
893
2122
|
"outputs": [],
|
|
894
2123
|
"stateMutability": "nonpayable"
|
|
895
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
|
+
},
|
|
896
2202
|
{
|
|
897
2203
|
"type": "function",
|
|
898
2204
|
"name": "updateKey",
|
|
@@ -903,7 +2209,7 @@ var REGISTRY_ABI = [
|
|
|
903
2209
|
"internalType": "uint256"
|
|
904
2210
|
},
|
|
905
2211
|
{
|
|
906
|
-
"name": "
|
|
2212
|
+
"name": "key",
|
|
907
2213
|
"type": "bytes",
|
|
908
2214
|
"internalType": "bytes"
|
|
909
2215
|
}
|
|
@@ -926,7 +2232,7 @@ var REGISTRY_ABI = [
|
|
|
926
2232
|
"internalType": "uint256"
|
|
927
2233
|
},
|
|
928
2234
|
{
|
|
929
|
-
"name": "
|
|
2235
|
+
"name": "key",
|
|
930
2236
|
"type": "bytes",
|
|
931
2237
|
"internalType": "bytes"
|
|
932
2238
|
},
|
|
@@ -936,7 +2242,7 @@ var REGISTRY_ABI = [
|
|
|
936
2242
|
"internalType": "uint256"
|
|
937
2243
|
},
|
|
938
2244
|
{
|
|
939
|
-
"name": "
|
|
2245
|
+
"name": "permission",
|
|
940
2246
|
"type": "bytes",
|
|
941
2247
|
"internalType": "bytes"
|
|
942
2248
|
}
|
|
@@ -967,28 +2273,22 @@ var REGISTRY_ABI = [
|
|
|
967
2273
|
"internalType": "uint256"
|
|
968
2274
|
},
|
|
969
2275
|
{
|
|
970
|
-
"name": "fingerprint",
|
|
971
|
-
"type": "bytes",
|
|
972
|
-
"indexed": false,
|
|
973
|
-
"internalType": "bytes"
|
|
974
|
-
},
|
|
975
|
-
{
|
|
976
|
-
"name": "messageVersion",
|
|
977
|
-
"type": "uint8",
|
|
2276
|
+
"name": "fingerprint",
|
|
2277
|
+
"type": "bytes",
|
|
978
2278
|
"indexed": false,
|
|
979
|
-
"internalType": "
|
|
2279
|
+
"internalType": "bytes"
|
|
980
2280
|
},
|
|
981
2281
|
{
|
|
982
|
-
"name": "
|
|
2282
|
+
"name": "payload",
|
|
983
2283
|
"type": "address",
|
|
984
2284
|
"indexed": false,
|
|
985
2285
|
"internalType": "address"
|
|
986
2286
|
},
|
|
987
2287
|
{
|
|
988
|
-
"name": "
|
|
989
|
-
"type": "
|
|
2288
|
+
"name": "messageVersion",
|
|
2289
|
+
"type": "uint8",
|
|
990
2290
|
"indexed": false,
|
|
991
|
-
"internalType": "
|
|
2291
|
+
"internalType": "uint8"
|
|
992
2292
|
},
|
|
993
2293
|
{
|
|
994
2294
|
"name": "submitter",
|
|
@@ -1022,13 +2322,13 @@ var REGISTRY_ABI = [
|
|
|
1022
2322
|
"internalType": "uint256"
|
|
1023
2323
|
},
|
|
1024
2324
|
{
|
|
1025
|
-
"name": "
|
|
2325
|
+
"name": "oldPayload",
|
|
1026
2326
|
"type": "address",
|
|
1027
2327
|
"indexed": false,
|
|
1028
2328
|
"internalType": "address"
|
|
1029
2329
|
},
|
|
1030
2330
|
{
|
|
1031
|
-
"name": "
|
|
2331
|
+
"name": "newPayload",
|
|
1032
2332
|
"type": "address",
|
|
1033
2333
|
"indexed": false,
|
|
1034
2334
|
"internalType": "address"
|
|
@@ -1078,11 +2378,23 @@ var REGISTRY_ABI = [
|
|
|
1078
2378
|
"internalType": "uint256"
|
|
1079
2379
|
},
|
|
1080
2380
|
{
|
|
1081
|
-
"name": "
|
|
2381
|
+
"name": "kindHash",
|
|
1082
2382
|
"type": "bytes32",
|
|
1083
2383
|
"indexed": true,
|
|
1084
2384
|
"internalType": "bytes32"
|
|
1085
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
|
+
},
|
|
1086
2398
|
{
|
|
1087
2399
|
"name": "submitter",
|
|
1088
2400
|
"type": "address",
|
|
@@ -1092,6 +2404,31 @@ var REGISTRY_ABI = [
|
|
|
1092
2404
|
],
|
|
1093
2405
|
"anonymous": false
|
|
1094
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
|
+
},
|
|
1095
2432
|
{
|
|
1096
2433
|
"type": "event",
|
|
1097
2434
|
"name": "Revoked",
|
|
@@ -1114,6 +2451,18 @@ var REGISTRY_ABI = [
|
|
|
1114
2451
|
"indexed": true,
|
|
1115
2452
|
"internalType": "uint256"
|
|
1116
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
|
+
},
|
|
1117
2466
|
{
|
|
1118
2467
|
"name": "submitter",
|
|
1119
2468
|
"type": "address",
|
|
@@ -1126,893 +2475,629 @@ var REGISTRY_ABI = [
|
|
|
1126
2475
|
{
|
|
1127
2476
|
"type": "error",
|
|
1128
2477
|
"name": "AlreadyRevoked",
|
|
1129
|
-
"inputs": [
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
{
|
|
1137
|
-
"type": "error",
|
|
1138
|
-
"name": "DeploymentFailed",
|
|
1139
|
-
"inputs": []
|
|
1140
|
-
},
|
|
1141
|
-
{
|
|
1142
|
-
"type": "error",
|
|
1143
|
-
"name": "DuplicateActiveFingerprint",
|
|
1144
|
-
"inputs": []
|
|
1145
|
-
},
|
|
1146
|
-
{
|
|
1147
|
-
"type": "error",
|
|
1148
|
-
"name": "EmptyPublicKey",
|
|
1149
|
-
"inputs": []
|
|
1150
|
-
},
|
|
1151
|
-
{
|
|
1152
|
-
"type": "error",
|
|
1153
|
-
"name": "EmptySignature",
|
|
1154
|
-
"inputs": []
|
|
1155
|
-
},
|
|
1156
|
-
{
|
|
1157
|
-
"type": "error",
|
|
1158
|
-
"name": "IndexOutOfBounds",
|
|
1159
|
-
"inputs": []
|
|
1160
|
-
},
|
|
1161
|
-
{
|
|
1162
|
-
"type": "error",
|
|
1163
|
-
"name": "InvalidAuthorization",
|
|
1164
|
-
"inputs": []
|
|
2478
|
+
"inputs": [
|
|
2479
|
+
{
|
|
2480
|
+
"name": "index",
|
|
2481
|
+
"type": "uint256",
|
|
2482
|
+
"internalType": "uint256"
|
|
2483
|
+
}
|
|
2484
|
+
]
|
|
1165
2485
|
},
|
|
1166
2486
|
{
|
|
1167
2487
|
"type": "error",
|
|
1168
|
-
"name": "
|
|
2488
|
+
"name": "BadArmorChecksum",
|
|
1169
2489
|
"inputs": []
|
|
1170
2490
|
},
|
|
1171
2491
|
{
|
|
1172
2492
|
"type": "error",
|
|
1173
|
-
"name": "
|
|
2493
|
+
"name": "BadBase64",
|
|
1174
2494
|
"inputs": []
|
|
1175
2495
|
},
|
|
1176
2496
|
{
|
|
1177
2497
|
"type": "error",
|
|
1178
|
-
"name": "
|
|
1179
|
-
"inputs": [
|
|
2498
|
+
"name": "ClaimActive",
|
|
2499
|
+
"inputs": [
|
|
2500
|
+
{
|
|
2501
|
+
"name": "index",
|
|
2502
|
+
"type": "uint256",
|
|
2503
|
+
"internalType": "uint256"
|
|
2504
|
+
}
|
|
2505
|
+
]
|
|
1180
2506
|
},
|
|
1181
2507
|
{
|
|
1182
2508
|
"type": "error",
|
|
1183
|
-
"name": "
|
|
2509
|
+
"name": "DeploymentFailed",
|
|
1184
2510
|
"inputs": []
|
|
1185
2511
|
},
|
|
1186
2512
|
{
|
|
1187
2513
|
"type": "error",
|
|
1188
|
-
"name": "
|
|
1189
|
-
"inputs": [
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
import { mainnet, sepolia, foundry } from "viem/chains";
|
|
1195
|
-
var REGISTRY_ADDRESS = "0x9302E02e2869e129aC8516fE5eFFd51EA3082c09";
|
|
1196
|
-
var NETWORKS = {
|
|
1197
|
-
mainnet: {
|
|
1198
|
-
chainId: 1,
|
|
1199
|
-
address: REGISTRY_ADDRESS,
|
|
1200
|
-
deployBlock: 25962908n,
|
|
1201
|
-
// 2026-09-12, tx 0xda4c3a19cae4e71ffb02559a048403f664d5a7967c19d42158c39600ec63bad8
|
|
1202
|
-
explorerUrl: "https://etherscan.io",
|
|
1203
|
-
defaultRpcUrl: "https://ethereum.publicnode.com"
|
|
1204
|
-
// same service as ethereum-rpc.publicnode.com; this hostname resolves on more networks
|
|
1205
|
-
},
|
|
1206
|
-
sepolia: {
|
|
1207
|
-
chainId: 11155111,
|
|
1208
|
-
address: REGISTRY_ADDRESS,
|
|
1209
|
-
deployBlock: 11683667n,
|
|
1210
|
-
// 2026-09-11, tx 0x3e40e8f5b9fbf82a4b8943561bcc9b964b2987de10640ac8ff3a1b4ef0725322
|
|
1211
|
-
explorerUrl: "https://sepolia.etherscan.io",
|
|
1212
|
-
defaultRpcUrl: "https://ethereum-sepolia.publicnode.com"
|
|
1213
|
-
},
|
|
1214
|
-
/** A local anvil instance (`anvil`, chain id 31337). Deploy with pgp-registry's Deploy script. */
|
|
1215
|
-
local: {
|
|
1216
|
-
chainId: 31337,
|
|
1217
|
-
address: REGISTRY_ADDRESS,
|
|
1218
|
-
deployBlock: 0n,
|
|
1219
|
-
explorerUrl: "",
|
|
1220
|
-
defaultRpcUrl: "http://127.0.0.1:8545"
|
|
1221
|
-
}
|
|
1222
|
-
};
|
|
1223
|
-
function isNetworkName(value) {
|
|
1224
|
-
return value === "mainnet" || value === "sepolia" || value === "local";
|
|
1225
|
-
}
|
|
1226
|
-
function getRegistry(network = "mainnet", addressOverride) {
|
|
1227
|
-
const r = NETWORKS[network];
|
|
1228
|
-
if (!r) throw new Error(`Unknown network: ${network}`);
|
|
1229
|
-
if (addressOverride && /^0x[0-9a-fA-F]{40}$/.test(addressOverride)) {
|
|
1230
|
-
return { ...r, address: addressOverride };
|
|
1231
|
-
}
|
|
1232
|
-
return r;
|
|
1233
|
-
}
|
|
1234
|
-
function chainFor(network = "mainnet") {
|
|
1235
|
-
if (network === "sepolia") return sepolia;
|
|
1236
|
-
if (network === "local") return foundry;
|
|
1237
|
-
return mainnet;
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
// src/core/identityError.ts
|
|
1241
|
-
function needsRpcProbe(s) {
|
|
1242
|
-
return s.ensEmpty || s.ensFailed || s.claimsFailed;
|
|
1243
|
-
}
|
|
1244
|
-
function identityErrorKind(s) {
|
|
1245
|
-
if (!needsRpcProbe(s) || s.rpcAnswered === void 0) return null;
|
|
1246
|
-
if (!s.rpcAnswered) return "rpc";
|
|
1247
|
-
if (s.ensEmpty && !s.ensFailed) return "not-found";
|
|
1248
|
-
return "read";
|
|
1249
|
-
}
|
|
1250
|
-
var IDENTITY_ERROR_TEXT = {
|
|
1251
|
-
rpc: "Couldn't reach the RPC, so nothing here is known yet.",
|
|
1252
|
-
"not-found": "No address for this name.",
|
|
1253
|
-
read: "Couldn't read the registry."
|
|
1254
|
-
};
|
|
1255
|
-
|
|
1256
|
-
// src/core/fingerprint.ts
|
|
1257
|
-
import { keccak256 } from "viem";
|
|
1258
|
-
var FINGERPRINT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
1259
|
-
function normalizeFingerprint(input) {
|
|
1260
|
-
const hex = input.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
|
|
1261
|
-
return FINGERPRINT_HEX.test(hex) ? hex : null;
|
|
1262
|
-
}
|
|
1263
|
-
function fingerprintToBytes(fingerprint) {
|
|
1264
|
-
const hex = normalizeFingerprint(fingerprint);
|
|
1265
|
-
if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
|
|
1266
|
-
return `0x${hex}`;
|
|
1267
|
-
}
|
|
1268
|
-
function bytesToFingerprint(bytes) {
|
|
1269
|
-
return bytes.replace(/^0x/i, "").toLowerCase();
|
|
1270
|
-
}
|
|
1271
|
-
function fingerprintHash(fingerprint) {
|
|
1272
|
-
return keccak256(fingerprintToBytes(fingerprint));
|
|
1273
|
-
}
|
|
1274
|
-
function keyIdOf(fingerprint) {
|
|
1275
|
-
const hex = normalizeFingerprint(fingerprint);
|
|
1276
|
-
if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
|
|
1277
|
-
return `0x${keyIdHex(hex)}`;
|
|
1278
|
-
}
|
|
1279
|
-
function keyIdHex(normalizedFingerprint) {
|
|
1280
|
-
return normalizedFingerprint.length === 64 ? normalizedFingerprint.slice(0, 16) : normalizedFingerprint.slice(-16);
|
|
1281
|
-
}
|
|
1282
|
-
function keyIdToBytes(keyId) {
|
|
1283
|
-
const hex = keyId.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
|
|
1284
|
-
return /^[0-9a-f]{16}$/.test(hex) ? `0x${hex}` : null;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
// src/core/pgp.ts
|
|
1288
|
-
function pgpConfig(openpgp) {
|
|
1289
|
-
const rejectCurves = new Set([...openpgp.config.rejectCurves ?? []].filter((c) => c !== "secp256k1"));
|
|
1290
|
-
return { ...openpgp.config, rejectCurves };
|
|
1291
|
-
}
|
|
1292
|
-
function algorithmName(keyOrSubkey) {
|
|
1293
|
-
try {
|
|
1294
|
-
const info = keyOrSubkey.getAlgorithmInfo();
|
|
1295
|
-
const curve = (info.curve || "").toLowerCase();
|
|
1296
|
-
if (curve.startsWith("ed25519") || info.algorithm === "ed25519" || info.algorithm === "eddsaLegacy") return "Ed25519";
|
|
1297
|
-
if (curve.startsWith("curve25519") || info.algorithm === "x25519") return "Cv25519";
|
|
1298
|
-
if (curve.startsWith("ed448")) return "Ed448";
|
|
1299
|
-
if (curve.startsWith("curve448") || info.algorithm === "x448") return "X448";
|
|
1300
|
-
if (curve.startsWith("nistp") || curve.startsWith("p")) return `NIST P-${curve.replace(/\D/g, "")}`;
|
|
1301
|
-
if (curve.startsWith("secp")) return curve;
|
|
1302
|
-
if (curve.startsWith("brainpool")) return curve;
|
|
1303
|
-
if (info.algorithm.startsWith("rsa")) return info.bits ? `RSA ${info.bits}` : "RSA";
|
|
1304
|
-
if (info.algorithm.startsWith("dsa")) return info.bits ? `DSA ${info.bits}` : "DSA";
|
|
1305
|
-
if (info.algorithm.startsWith("elgamal")) return info.bits ? `ElGamal ${info.bits}` : "ElGamal";
|
|
1306
|
-
return info.curve || info.algorithm;
|
|
1307
|
-
} catch {
|
|
1308
|
-
return String(keyOrSubkey.keyPacket?.algorithm ?? "unknown");
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
async function parsePgpKey(armoredKey) {
|
|
1312
|
-
try {
|
|
1313
|
-
const openpgp = await import("openpgp");
|
|
1314
|
-
const { readKey } = openpgp;
|
|
1315
|
-
const config = pgpConfig(openpgp);
|
|
1316
|
-
const key = await readKey({ armoredKey });
|
|
1317
|
-
const fingerprint = key.getFingerprint().toUpperCase();
|
|
1318
|
-
const algorithm = algorithmName(key);
|
|
1319
|
-
const created = key.keyPacket.created?.toISOString() ?? null;
|
|
1320
|
-
const expiration = await key.getExpirationTime();
|
|
1321
|
-
const expires = expiration && expiration !== Infinity ? new Date(expiration).toISOString() : null;
|
|
1322
|
-
const userIDs = [];
|
|
1323
|
-
const notations = [];
|
|
1324
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1325
|
-
for (const user of key.users) {
|
|
1326
|
-
const certs = user.selfCertifications;
|
|
1327
|
-
if (!certs || certs.length === 0) continue;
|
|
1328
|
-
let userVerified = false;
|
|
1329
|
-
for (const cert of certs) {
|
|
1330
|
-
try {
|
|
1331
|
-
await cert.verify(
|
|
1332
|
-
key.keyPacket,
|
|
1333
|
-
cert.signatureType,
|
|
1334
|
-
{ userID: user.userID, key: key.keyPacket },
|
|
1335
|
-
void 0,
|
|
1336
|
-
// date: now
|
|
1337
|
-
void 0,
|
|
1338
|
-
// detached
|
|
1339
|
-
config
|
|
1340
|
-
);
|
|
1341
|
-
} catch {
|
|
1342
|
-
continue;
|
|
1343
|
-
}
|
|
1344
|
-
userVerified = true;
|
|
1345
|
-
if (cert.rawNotations) {
|
|
1346
|
-
for (const n of cert.rawNotations) {
|
|
1347
|
-
const name = typeof n.name === "string" ? n.name : new TextDecoder().decode(n.name);
|
|
1348
|
-
const value = n.value instanceof Uint8Array ? new TextDecoder().decode(n.value) : typeof n.value === "string" ? n.value : null;
|
|
1349
|
-
if (value) {
|
|
1350
|
-
if (!seen.has(value)) {
|
|
1351
|
-
seen.add(value);
|
|
1352
|
-
notations.push({ name, value });
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
2514
|
+
"name": "DuplicateActiveFingerprint",
|
|
2515
|
+
"inputs": [
|
|
2516
|
+
{
|
|
2517
|
+
"name": "fingerprint",
|
|
2518
|
+
"type": "bytes",
|
|
2519
|
+
"internalType": "bytes"
|
|
1357
2520
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
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"
|
|
1361
2546
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1373
|
-
async function verifyClearsigned({ armoredKey, clearsigned }) {
|
|
1374
|
-
try {
|
|
1375
|
-
const openpgp = await import("openpgp");
|
|
1376
|
-
const { readKey, readCleartextMessage, LiteralDataPacket } = openpgp;
|
|
1377
|
-
const config = pgpConfig(openpgp);
|
|
1378
|
-
const publicKey = await readKey({ armoredKey });
|
|
1379
|
-
const message = await readCleartextMessage({ cleartextMessage: clearsigned });
|
|
1380
|
-
const msg = message;
|
|
1381
|
-
const now = /* @__PURE__ */ new Date();
|
|
1382
|
-
for (const sig of msg.signature.packets) {
|
|
1383
|
-
if (!publicKey.getKeys(sig.issuerKeyID).length) continue;
|
|
1384
|
-
const signingKey = await publicKey.getSigningKey(sig.issuerKeyID, now, void 0, config);
|
|
1385
|
-
const literal = new LiteralDataPacket();
|
|
1386
|
-
literal.setText(msg.text ?? message.getText().replace(/\r?\n/g, "\r\n"));
|
|
1387
|
-
await sig.verify(signingKey.keyPacket, sig.signatureType, literal, now, true, config);
|
|
1388
|
-
return { verified: true, text: message.getText() };
|
|
1389
|
-
}
|
|
1390
|
-
return { verified: false, reason: "Message is not signed by this key", text: message.getText() };
|
|
1391
|
-
} catch (err) {
|
|
1392
|
-
return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text: "" };
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
var REVOCATION_REASON = {
|
|
1396
|
-
// 0 = "no reason specified", gpg's default: say nothing rather than "(reason: no reason given)"
|
|
1397
|
-
1: "replaced by a newer key",
|
|
1398
|
-
2: "compromised",
|
|
1399
|
-
3: "no longer used",
|
|
1400
|
-
32: "a user ID is no longer valid"
|
|
1401
|
-
};
|
|
1402
|
-
var iso = (d) => d instanceof Date ? d.toISOString() : typeof d === "number" && Number.isFinite(d) ? new Date(d).toISOString() : null;
|
|
1403
|
-
function revocationOf(sigs) {
|
|
1404
|
-
const sig = [...sigs ?? []].sort((a, b) => (b.created?.getTime?.() ?? 0) - (a.created?.getTime?.() ?? 0))[0];
|
|
1405
|
-
const flag = sig?.reasonForRevocationFlag ?? null;
|
|
1406
|
-
return {
|
|
1407
|
-
at: iso(sig?.created),
|
|
1408
|
-
compromised: flag === 2,
|
|
1409
|
-
revocationReason: sig?.reasonForRevocationString || (flag !== null ? REVOCATION_REASON[flag] ?? null : null)
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
|
-
async function keyLife(openpgp, key, clearsigned, config) {
|
|
1413
|
-
const now = /* @__PURE__ */ new Date();
|
|
1414
|
-
let issuer = null;
|
|
1415
|
-
try {
|
|
1416
|
-
issuer = (await openpgp.readCleartextMessage({ cleartextMessage: clearsigned })).signature.packets[0]?.issuerKeyID ?? null;
|
|
1417
|
-
} catch {
|
|
1418
|
-
}
|
|
1419
|
-
const signer = issuer && !key.getKeyID().equals(issuer) ? key.subkeys.find((s) => s.getKeyID().equals(issuer)) ?? null : key;
|
|
1420
|
-
const signingKey = signer ? signer.getFingerprint().toUpperCase() : null;
|
|
1421
|
-
const safe = async (f, fallback) => {
|
|
1422
|
-
try {
|
|
1423
|
-
return await f();
|
|
1424
|
-
} catch {
|
|
1425
|
-
return fallback;
|
|
1426
|
-
}
|
|
1427
|
-
};
|
|
1428
|
-
const keyRevoked = await safe(() => key.isRevoked(void 0, void 0, now, config), false);
|
|
1429
|
-
const keyExp = await safe(() => key.getExpirationTime(void 0, config), null);
|
|
1430
|
-
const isSub = signer && signer !== key;
|
|
1431
|
-
const subRevoked = isSub ? await safe(() => signer.isRevoked(null, key.keyPacket, now, config), false) : false;
|
|
1432
|
-
const subExp = isSub ? await safe(() => signer.getExpirationTime(null, config), null) : null;
|
|
1433
|
-
const finite = (d) => d instanceof Date && Number.isFinite(d.getTime()) ? d : null;
|
|
1434
|
-
const kExp = finite(keyExp), sExp = finite(subExp);
|
|
1435
|
-
let problem = null;
|
|
1436
|
-
if (keyRevoked) {
|
|
1437
|
-
const r = revocationOf(key.revocationSignatures);
|
|
1438
|
-
problem = { kind: r.compromised ? "compromised" : "revoked", at: r.at, revocationReason: r.revocationReason };
|
|
1439
|
-
} else if (kExp && kExp <= now) {
|
|
1440
|
-
problem = { kind: "expired", at: kExp.toISOString() };
|
|
1441
|
-
} else if (subRevoked) {
|
|
1442
|
-
const r = revocationOf(signer.revocationSignatures);
|
|
1443
|
-
problem = { kind: r.compromised ? "compromised" : "signing-key-revoked", at: r.at, revocationReason: r.revocationReason };
|
|
1444
|
-
} else if (sExp && sExp <= now) {
|
|
1445
|
-
problem = { kind: "signing-key-expired", at: sExp.toISOString() };
|
|
1446
|
-
}
|
|
1447
|
-
const earliest = [kExp, sExp].filter(Boolean).sort((a, b) => a - b)[0];
|
|
1448
|
-
return { problem, signingKey, expiresAt: earliest ? earliest.toISOString() : null, algorithm: algorithmName(signer ?? key) };
|
|
1449
|
-
}
|
|
1450
|
-
async function verifyAttestation({
|
|
1451
|
-
pgpPublicKey,
|
|
1452
|
-
pgpSignature,
|
|
1453
|
-
fingerprint,
|
|
1454
|
-
ethAddress
|
|
1455
|
-
}) {
|
|
1456
|
-
try {
|
|
1457
|
-
if (!pgpPublicKey || !pgpSignature) {
|
|
1458
|
-
return { verified: false, reason: "Missing PGP data", kind: "bad-signature" };
|
|
1459
|
-
}
|
|
1460
|
-
const openpgp = await import("openpgp");
|
|
1461
|
-
const { readKey } = openpgp;
|
|
1462
|
-
const publicKey = await readKey({ armoredKey: pgpPublicKey });
|
|
1463
|
-
const keyFingerprint = publicKey.getFingerprint().toUpperCase();
|
|
1464
|
-
if (keyFingerprint !== fingerprint.toUpperCase()) {
|
|
1465
|
-
return { verified: false, reason: "Key fingerprint mismatch", kind: "bad-signature" };
|
|
1466
|
-
}
|
|
1467
|
-
const life = await keyLife(openpgp, publicKey, pgpSignature, pgpConfig(openpgp));
|
|
1468
|
-
const v = await verifyClearsigned({ armoredKey: pgpPublicKey, clearsigned: pgpSignature });
|
|
1469
|
-
if (!v.verified) {
|
|
1470
|
-
if (life.problem) return { verified: false, reason: v.reason, signingKey: life.signingKey, ...life.problem };
|
|
1471
|
-
if (/too weak|reject|unsupported|not supported|unknown (curve|algorithm)/i.test(v.reason || "")) {
|
|
1472
|
-
return { verified: false, reason: v.reason, kind: "unsupported", algorithm: life.algorithm, signingKey: life.signingKey };
|
|
2547
|
+
]
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
"type": "error",
|
|
2551
|
+
"name": "InvalidFingerprint",
|
|
2552
|
+
"inputs": [
|
|
2553
|
+
{
|
|
2554
|
+
"name": "fingerprint",
|
|
2555
|
+
"type": "bytes",
|
|
2556
|
+
"internalType": "bytes"
|
|
1473
2557
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
1485
|
-
var EMAIL_UID = /@/;
|
|
1486
|
-
async function stripEmailUserIDs(armoredKey) {
|
|
1487
|
-
try {
|
|
1488
|
-
const { readKey } = await import("openpgp");
|
|
1489
|
-
const key = await readKey({ armoredKey });
|
|
1490
|
-
const kept = [];
|
|
1491
|
-
const removed = [];
|
|
1492
|
-
const users = key.users.filter((u) => {
|
|
1493
|
-
const uid = u.userID?.userID;
|
|
1494
|
-
if (!uid) return false;
|
|
1495
|
-
if (EMAIL_UID.test(uid)) {
|
|
1496
|
-
removed.push(uid);
|
|
1497
|
-
return false;
|
|
2558
|
+
]
|
|
2559
|
+
},
|
|
2560
|
+
{
|
|
2561
|
+
"type": "error",
|
|
2562
|
+
"name": "InvalidFingerprintLength",
|
|
2563
|
+
"inputs": [
|
|
2564
|
+
{
|
|
2565
|
+
"name": "length",
|
|
2566
|
+
"type": "uint256",
|
|
2567
|
+
"internalType": "uint256"
|
|
1498
2568
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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
|
+
},
|
|
1552
2684
|
{
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
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
|
+
]
|
|
1557
2699
|
},
|
|
1558
2700
|
{
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
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
|
+
]
|
|
1565
2715
|
},
|
|
1566
2716
|
{
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
// records like _thurin.example.com); rejects slashes, userinfo, and spaces.
|
|
1571
|
-
pattern: /^dns:([a-z0-9._-]+)\?type=TXT$/i,
|
|
1572
|
-
parse: (m) => ({ domain: m[1] })
|
|
2717
|
+
"type": "error",
|
|
2718
|
+
"name": "SupersededIsSetByReattest",
|
|
2719
|
+
"inputs": []
|
|
1573
2720
|
},
|
|
1574
2721
|
{
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
parse: (m) => ({ user: m[1], castHash: m[2] })
|
|
2722
|
+
"type": "error",
|
|
2723
|
+
"name": "TooManyClaims",
|
|
2724
|
+
"inputs": []
|
|
1579
2725
|
},
|
|
1580
2726
|
{
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
2727
|
+
"type": "error",
|
|
2728
|
+
"name": "UnknownRevokeReason",
|
|
2729
|
+
"inputs": [
|
|
2730
|
+
{
|
|
2731
|
+
"name": "reason",
|
|
2732
|
+
"type": "string",
|
|
2733
|
+
"internalType": "string"
|
|
2734
|
+
}
|
|
2735
|
+
]
|
|
1585
2736
|
},
|
|
1586
2737
|
{
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
// like "mastodon.social@evil.com" can't redirect the lookup elsewhere.
|
|
1591
|
-
pattern: /^https:\/\/([a-z0-9.-]+(?::\d+)?)\/@([^/@?#\s]+)$/i,
|
|
1592
|
-
parse: (m) => ({ instance: m[1], user: m[2] })
|
|
2738
|
+
"type": "error",
|
|
2739
|
+
"name": "UnsupportedHeader",
|
|
2740
|
+
"inputs": []
|
|
1593
2741
|
}
|
|
1594
2742
|
];
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
if (proof.url.startsWith("http")) return proof.url;
|
|
1620
|
-
return null;
|
|
1621
|
-
}
|
|
1622
|
-
function proofSecondaryHref(proof) {
|
|
1623
|
-
if (proof.provider === "github") return proof.url;
|
|
1624
|
-
if (proof.provider === "farcaster") return proof.url;
|
|
1625
|
-
if (proof.provider === "codeberg") return proof.url;
|
|
1626
|
-
if (proof.provider === "mastodon") return proof.url;
|
|
1627
|
-
return null;
|
|
1628
|
-
}
|
|
1629
|
-
var FPR_TOKEN = "OPENPGP4FPR:";
|
|
1630
|
-
function containsFingerprint(text, fingerprint) {
|
|
1631
|
-
return text.toUpperCase().includes(FPR_TOKEN + fingerprint.toUpperCase());
|
|
1632
|
-
}
|
|
1633
|
-
function containsFingerprintUrl(text, fingerprint) {
|
|
1634
|
-
return text.toUpperCase().includes(fingerprint.toUpperCase());
|
|
1635
|
-
}
|
|
1636
|
-
async function verifyGitHub(proof, fingerprint) {
|
|
1637
|
-
if (proof.repo) return verifyGitHubRepo(proof, fingerprint);
|
|
1638
|
-
try {
|
|
1639
|
-
const resp = await fetch(`https://api.github.com/gists/${proof.gistId}`);
|
|
1640
|
-
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
1641
|
-
const data = await resp.json();
|
|
1642
|
-
if (data.owner?.login?.toLowerCase() !== proof.user.toLowerCase()) {
|
|
1643
|
-
return { verified: false, reason: "Gist owner does not match claimed user" };
|
|
1644
|
-
}
|
|
1645
|
-
for (const file of Object.values(data.files || {})) {
|
|
1646
|
-
if (file.content && containsFingerprint(file.content, fingerprint)) {
|
|
1647
|
-
return { verified: true };
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
return { verified: false, reason: "Fingerprint token not found in gist" };
|
|
1651
|
-
} catch (err) {
|
|
1652
|
-
return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
async function verifyGitHubRepo(proof, fingerprint) {
|
|
1656
|
-
try {
|
|
1657
|
-
const resp = await fetch(
|
|
1658
|
-
`https://api.github.com/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
|
|
1659
|
-
);
|
|
1660
|
-
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
1661
|
-
const data = await resp.json();
|
|
1662
|
-
if (data.owner?.login?.toLowerCase() !== proof.user.toLowerCase()) {
|
|
1663
|
-
return { verified: false, reason: "Repository owner does not match claimed account" };
|
|
1664
|
-
}
|
|
1665
|
-
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
1666
|
-
return { verified: true };
|
|
1667
|
-
}
|
|
1668
|
-
return { verified: false, reason: "Fingerprint token not found in repository description" };
|
|
1669
|
-
} catch (err) {
|
|
1670
|
-
return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
async function verifyDNS(proof, fingerprint) {
|
|
1674
|
-
try {
|
|
1675
|
-
const resp = await fetch(
|
|
1676
|
-
`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(proof.domain)}&type=TXT`,
|
|
1677
|
-
{ headers: { Accept: "application/dns-json" } }
|
|
1678
|
-
);
|
|
1679
|
-
if (!resp.ok) return { verified: false, reason: `DNS query returned ${resp.status}` };
|
|
1680
|
-
const data = await resp.json();
|
|
1681
|
-
for (const answer of data.Answer || []) {
|
|
1682
|
-
if (answer.type !== 16) continue;
|
|
1683
|
-
if (answer.data && containsFingerprint(answer.data, fingerprint)) {
|
|
1684
|
-
return { verified: true };
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
return { verified: false, reason: "Fingerprint token not found in TXT records" };
|
|
1688
|
-
} catch (err) {
|
|
1689
|
-
return { verified: false, reason: `DNS fetch failed: ${err.message}` };
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
var FARCASTER_HUB = "https://haatz.quilibrium.com";
|
|
1693
|
-
var NEYNAR_HUB = "https://hub-api.neynar.com";
|
|
1694
|
-
function farcasterSource(opts) {
|
|
1695
|
-
if (opts.farcasterHub) return { base: opts.farcasterHub.replace(/\/+$/, ""), headers: {} };
|
|
1696
|
-
if (opts.neynarApiKey) return { base: NEYNAR_HUB, headers: { "x-api-key": opts.neynarApiKey } };
|
|
1697
|
-
return { base: FARCASTER_HUB, headers: {} };
|
|
1698
|
-
}
|
|
1699
|
-
async function verifyFarcaster(proof, fingerprint, opts = {}) {
|
|
1700
|
-
const { base, headers } = farcasterSource(opts);
|
|
1701
|
-
const host = base.replace(/^https?:\/\//, "");
|
|
1702
|
-
try {
|
|
1703
|
-
const nameResp = await fetch(`${base}/v1/userNameProofByName?name=${encodeURIComponent(proof.user)}`, { headers });
|
|
1704
|
-
if (nameResp.status === 404) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
1705
|
-
if (!nameResp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${nameResp.status}` };
|
|
1706
|
-
const fid = (await nameResp.json()).fid;
|
|
1707
|
-
if (!fid) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
1708
|
-
let pageToken = "";
|
|
1709
|
-
for (let page = 0; page < 5; page++) {
|
|
1710
|
-
const url = `${base}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ""}`;
|
|
1711
|
-
const resp = await fetch(url, { headers });
|
|
1712
|
-
if (!resp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${resp.status}` };
|
|
1713
|
-
const data = await resp.json();
|
|
1714
|
-
for (const msg of data.messages || []) {
|
|
1715
|
-
if (msg.hash && msg.hash.startsWith(proof.castHash)) {
|
|
1716
|
-
const text = msg.data?.castAddBody?.text || "";
|
|
1717
|
-
if (containsFingerprint(text, fingerprint)) {
|
|
1718
|
-
return { verified: true };
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
if (!data.nextPageToken) break;
|
|
1723
|
-
pageToken = data.nextPageToken;
|
|
1724
|
-
}
|
|
1725
|
-
return { verified: false, reason: "Cast not found" };
|
|
1726
|
-
} catch (err) {
|
|
1727
|
-
return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) didn't answer (${err.message})` };
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
async function verifyCodeberg(proof, fingerprint) {
|
|
1731
|
-
try {
|
|
1732
|
-
const resp = await fetch(
|
|
1733
|
-
`https://codeberg.org/api/v1/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
|
|
1734
|
-
);
|
|
1735
|
-
if (!resp.ok) return { verified: false, reason: `Codeberg API returned ${resp.status}` };
|
|
1736
|
-
const data = await resp.json();
|
|
1737
|
-
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
1738
|
-
return { verified: true };
|
|
1739
|
-
}
|
|
1740
|
-
return { verified: false, reason: "Fingerprint token not found in repo description" };
|
|
1741
|
-
} catch (err) {
|
|
1742
|
-
return { verified: false, reason: `Codeberg fetch failed: ${err.message}` };
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
async function verifyMastodon(proof, fingerprint) {
|
|
1746
|
-
try {
|
|
1747
|
-
if (!proof.instance || !/^[a-z0-9.-]+(?::\d+)?$/i.test(proof.instance)) {
|
|
1748
|
-
return { verified: false, reason: "Invalid Mastodon instance host" };
|
|
1749
|
-
}
|
|
1750
|
-
const resp = await fetch(
|
|
1751
|
-
`https://${proof.instance}/api/v1/accounts/lookup?acct=${encodeURIComponent(proof.user)}`
|
|
1752
|
-
);
|
|
1753
|
-
if (!resp.ok) return { verified: false, reason: `Mastodon API returned ${resp.status}` };
|
|
1754
|
-
const data = await resp.json();
|
|
1755
|
-
const keyId = keyIdHex(fingerprint.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase());
|
|
1756
|
-
for (const field of data.fields || []) {
|
|
1757
|
-
const text = field.value.replace(/<[^>]*>/g, "");
|
|
1758
|
-
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
1759
|
-
return { verified: true };
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
if (data.note) {
|
|
1763
|
-
const text = data.note.replace(/<[^>]*>/g, "");
|
|
1764
|
-
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
1765
|
-
return { verified: true };
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
return { verified: false, reason: "Fingerprint not found in profile fields or bio" };
|
|
1769
|
-
} catch (err) {
|
|
1770
|
-
return { verified: false, reason: `Mastodon fetch failed: ${err.message}` };
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
var verifiers = {
|
|
1774
|
-
github: verifyGitHub,
|
|
1775
|
-
dns: verifyDNS,
|
|
1776
|
-
farcaster: verifyFarcaster,
|
|
1777
|
-
codeberg: verifyCodeberg,
|
|
1778
|
-
mastodon: verifyMastodon
|
|
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
|
+
}
|
|
1779
2767
|
};
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
if (!fn) return { verified: false, reason: "Unknown provider" };
|
|
1783
|
-
return fn(proof, fingerprint, typeof opts === "string" ? { neynarApiKey: opts } : opts ?? {});
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
// src/core/avatar.ts
|
|
1787
|
-
var IPFS_GATEWAYS = ["https://ipfs.filebase.io/ipfs/", "https://gateway.pinata.cloud/ipfs/"];
|
|
1788
|
-
var IPFS_GATEWAY = IPFS_GATEWAYS[0];
|
|
1789
|
-
var ARWEAVE_GATEWAY = "https://arweave.net/";
|
|
1790
|
-
function avatarUrl(uri) {
|
|
1791
|
-
if (typeof uri !== "string") return null;
|
|
1792
|
-
const u = uri.trim();
|
|
1793
|
-
if (/^data:image\/(png|jpe?g|gif|webp|avif|svg\+xml)[;,]/i.test(u)) return u;
|
|
1794
|
-
let m = u.match(/^ipfs:\/\/(?:ipfs\/)?([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
|
|
1795
|
-
if (m) return IPFS_GATEWAY + m[1];
|
|
1796
|
-
m = u.match(/^ar:\/\/([a-zA-Z0-9_-]{43}(?:\/[^?#\s]*)?)$/);
|
|
1797
|
-
if (m) return ARWEAVE_GATEWAY + m[1];
|
|
1798
|
-
if (/^https:\/\/euc\.li\/[^?#\s]+$/.test(u)) return u;
|
|
1799
|
-
m = u.match(/^https:\/\/[^/]+\/ipfs\/([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
|
|
1800
|
-
if (m) return IPFS_GATEWAY + m[1];
|
|
1801
|
-
return null;
|
|
2768
|
+
function isNetworkName(value) {
|
|
2769
|
+
return value === "mainnet" || value === "sepolia" || value === "local";
|
|
1802
2770
|
}
|
|
1803
|
-
function
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
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;
|
|
1807
2778
|
}
|
|
1808
|
-
function
|
|
1809
|
-
|
|
1810
|
-
|
|
2779
|
+
function chainFor(network = "mainnet") {
|
|
2780
|
+
if (network === "sepolia") return sepolia;
|
|
2781
|
+
if (network === "local") return foundry;
|
|
2782
|
+
return mainnet;
|
|
1811
2783
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
const
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
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) => c.verification?.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 });
|
|
1833
2845
|
}
|
|
1834
|
-
if (!meta) return null;
|
|
1835
2846
|
}
|
|
1836
|
-
return
|
|
2847
|
+
return found;
|
|
1837
2848
|
}
|
|
1838
2849
|
|
|
1839
|
-
// src/core/
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
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" });
|
|
1851
2866
|
}
|
|
1852
|
-
function
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
return
|
|
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`;
|
|
1856
2871
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
];
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2872
|
+
function claimCheckText(v, formatDate = formatClaimDate) {
|
|
2873
|
+
const kind = 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
|
+
}
|
|
1869
2910
|
}
|
|
1870
|
-
|
|
1871
|
-
const
|
|
1872
|
-
|
|
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
|
+
}
|
|
1873
2930
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
function registryDomain(chainId, verifyingContract) {
|
|
1880
|
-
return { name: EIP712_NAME, version: EIP712_VERSION, chainId, verifyingContract };
|
|
2931
|
+
function expiresSoon(v, now = Date.now(), withinDays = 30) {
|
|
2932
|
+
if (!v?.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 };
|
|
1881
2936
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
{ name: "fingerprint", type: "bytes" },
|
|
1886
|
-
{ name: "pgpSignature", type: "bytes" },
|
|
1887
|
-
{ name: "pgpPublicKey", type: "bytes" },
|
|
1888
|
-
{ name: "nonce", type: "uint256" },
|
|
1889
|
-
{ name: "deadline", type: "uint256" }
|
|
1890
|
-
],
|
|
1891
|
-
Reattest: [
|
|
1892
|
-
{ name: "owner", type: "address" },
|
|
1893
|
-
{ name: "revokeIndex", type: "uint256" },
|
|
1894
|
-
{ name: "fingerprint", type: "bytes" },
|
|
1895
|
-
{ name: "pgpSignature", type: "bytes" },
|
|
1896
|
-
{ name: "pgpPublicKey", type: "bytes" },
|
|
1897
|
-
{ name: "nonce", type: "uint256" },
|
|
1898
|
-
{ name: "deadline", type: "uint256" }
|
|
1899
|
-
],
|
|
1900
|
-
UpdateKey: [
|
|
1901
|
-
{ name: "owner", type: "address" },
|
|
1902
|
-
{ name: "index", type: "uint256" },
|
|
1903
|
-
{ name: "pgpPublicKey", type: "bytes" },
|
|
1904
|
-
{ name: "nonce", type: "uint256" },
|
|
1905
|
-
{ name: "deadline", type: "uint256" }
|
|
1906
|
-
],
|
|
1907
|
-
Revoke: [
|
|
1908
|
-
{ name: "owner", type: "address" },
|
|
1909
|
-
{ name: "index", type: "uint256" },
|
|
1910
|
-
{ name: "nonce", type: "uint256" },
|
|
1911
|
-
{ name: "deadline", type: "uint256" }
|
|
1912
|
-
],
|
|
1913
|
-
SetRecord: [
|
|
1914
|
-
{ name: "owner", type: "address" },
|
|
1915
|
-
{ name: "index", type: "uint256" },
|
|
1916
|
-
{ name: "kind", type: "bytes32" },
|
|
1917
|
-
{ name: "value", type: "bytes" },
|
|
1918
|
-
{ name: "nonce", type: "uint256" },
|
|
1919
|
-
{ name: "deadline", type: "uint256" }
|
|
1920
|
-
]
|
|
1921
|
-
};
|
|
1922
|
-
function attestTypedData(chainId, registry, a) {
|
|
1923
|
-
return {
|
|
1924
|
-
domain: registryDomain(chainId, registry),
|
|
1925
|
-
types: { Attest: AUTHORIZATION_TYPES.Attest },
|
|
1926
|
-
primaryType: "Attest",
|
|
1927
|
-
message: {
|
|
1928
|
-
owner: a.owner,
|
|
1929
|
-
fingerprint: fingerprintToBytes(a.fingerprint),
|
|
1930
|
-
pgpSignature: stringToHex(a.pgpSignature),
|
|
1931
|
-
pgpPublicKey: stringToHex(a.pgpPublicKey),
|
|
1932
|
-
nonce: a.nonce,
|
|
1933
|
-
deadline: a.deadline
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
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)}).`;
|
|
1936
2940
|
}
|
|
1937
|
-
function
|
|
1938
|
-
const
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
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;
|
|
1945
2949
|
}
|
|
1946
|
-
function
|
|
1947
|
-
return {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
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 import("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 = newestSignature(sk.bindingSignatures)?.keyFlags?.[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 {
|
|
2995
|
+
return { ok: false, reason: "unverified", claim };
|
|
2996
|
+
}
|
|
1953
2997
|
}
|
|
1954
|
-
function
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
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
|
+
}
|
|
1961
3009
|
}
|
|
1962
|
-
function
|
|
1963
|
-
return
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
message: { owner: a.owner, index: a.index, kind: a.kind, value: a.value, nonce: a.nonce, deadline: a.deadline }
|
|
1968
|
-
};
|
|
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.`;
|
|
1969
3015
|
}
|
|
1970
|
-
function
|
|
1971
|
-
|
|
3016
|
+
async function encryptTo(key, data, { filename, armor = typeof data === "string", hideRecipient = true, date = /* @__PURE__ */ new Date() } = {}) {
|
|
3017
|
+
const openpgp = await import("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" });
|
|
1972
3023
|
}
|
|
1973
|
-
|
|
1974
|
-
|
|
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 = e.data?.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 ? CONTRACT_ERROR_TEXT[name] ?? `The registry refused it (${name}).` : null;
|
|
1975
3066
|
}
|
|
1976
3067
|
|
|
1977
3068
|
// src/core/records.ts
|
|
1978
|
-
import { hexToString, stringToHex as stringToHex2 } from "viem";
|
|
1979
3069
|
var MAX_RECORD_BYTES = 1024;
|
|
3070
|
+
var MAX_KIND_BYTES = 31;
|
|
1980
3071
|
function kindName(input) {
|
|
1981
3072
|
return input.includes(".") ? input : `thurin.${input}`;
|
|
1982
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
|
+
}
|
|
1983
3085
|
var KNOWN_KINDS = [
|
|
1984
3086
|
"thurin.railgun",
|
|
1985
3087
|
"thurin.security",
|
|
1986
3088
|
"thurin.successor",
|
|
1987
3089
|
"thurin.affiliation",
|
|
1988
3090
|
"thurin.canary",
|
|
1989
|
-
"thurin.
|
|
1990
|
-
"thurin.disclosure",
|
|
1991
|
-
"thurin.pointer"
|
|
1992
|
-
];
|
|
1993
|
-
var IDENTITY_KINDS = [
|
|
1994
|
-
"thurin.railgun",
|
|
1995
|
-
"thurin.security",
|
|
1996
|
-
"thurin.successor",
|
|
1997
|
-
"thurin.affiliation",
|
|
1998
|
-
"thurin.canary",
|
|
3091
|
+
"thurin.releases",
|
|
1999
3092
|
"thurin.private",
|
|
2000
3093
|
"thurin.disclosure"
|
|
2001
3094
|
];
|
|
2002
|
-
function
|
|
2003
|
-
const bytes = new TextEncoder().encode(value).length;
|
|
2004
|
-
if (bytes > MAX_RECORD_BYTES) throw new Error(`Record is ${bytes} bytes; the registry accepts up to ${MAX_RECORD_BYTES}`);
|
|
2005
|
-
return stringToHex2(value);
|
|
2006
|
-
}
|
|
2007
|
-
function decodeRecord(hex) {
|
|
2008
|
-
return hex === "0x" ? "" : hexToString(hex);
|
|
2009
|
-
}
|
|
2010
|
-
function parsePointer(text) {
|
|
3095
|
+
function parseReleases(text) {
|
|
2011
3096
|
const p = JSON.parse(text);
|
|
2012
|
-
if (p?.v !== 1 || !Array.isArray(p.releases)) throw new Error("Not a v1 thurin.
|
|
3097
|
+
if (p?.v !== 1 || !Array.isArray(p.releases)) throw new Error("Not a v1 thurin.releases record");
|
|
2013
3098
|
return p;
|
|
2014
3099
|
}
|
|
2015
|
-
function
|
|
3100
|
+
function addRelease(existing, entry) {
|
|
2016
3101
|
if (!/^[0-9a-f]{64}$/i.test(entry.sha256)) throw new Error("sha256 must be 64 hex characters");
|
|
2017
3102
|
const releases = [entry, ...(existing?.releases ?? []).filter((r) => r.name !== entry.name)];
|
|
2018
3103
|
const dropped = [];
|
|
@@ -2020,7 +3105,7 @@ function addPointer(existing, entry) {
|
|
|
2020
3105
|
while (new TextEncoder().encode(JSON.stringify(record)).length > MAX_RECORD_BYTES && releases.length > 1) dropped.push(releases.pop());
|
|
2021
3106
|
return { record, dropped };
|
|
2022
3107
|
}
|
|
2023
|
-
function
|
|
3108
|
+
function renderReleases(p) {
|
|
2024
3109
|
return p.releases.map((r) => `${r.name.padEnd(22)} ${r.date} sha256 ${r.sha256}${r.url ? ` ${r.url}` : ""}`).join("\n");
|
|
2025
3110
|
}
|
|
2026
3111
|
var RAILGUN_ADDRESS = /^0zk1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{60,200}$/;
|
|
@@ -2080,186 +3165,213 @@ async function parseRecord(kind, text, opts = {}) {
|
|
|
2080
3165
|
}
|
|
2081
3166
|
return { ...base, valid: true, data: { type: "encrypted", recipients } };
|
|
2082
3167
|
}
|
|
2083
|
-
case "thurin.
|
|
3168
|
+
case "thurin.releases": {
|
|
2084
3169
|
try {
|
|
2085
|
-
return { ...base, valid: true, data: { type: "
|
|
3170
|
+
return { ...base, valid: true, data: { type: "releases", releases: parseReleases(t).releases } };
|
|
2086
3171
|
} catch {
|
|
2087
|
-
return invalid(
|
|
3172
|
+
return invalid('Not a v1 release list: expected {"v":1,"releases":[\u2026]}');
|
|
2088
3173
|
}
|
|
2089
3174
|
}
|
|
2090
3175
|
default:
|
|
2091
3176
|
return { ...base, valid: true, data: { type: "text" } };
|
|
2092
3177
|
}
|
|
2093
3178
|
}
|
|
2094
|
-
|
|
3179
|
+
function pickRecords(names, values, kinds = KNOWN_KINDS) {
|
|
3180
|
+
const all = names.map((kind, i) => ({ kind, text: 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)] });
|
|
2095
3191
|
const out = [];
|
|
2096
|
-
for (const
|
|
2097
|
-
const hex = await client.readContract({ address: registry, abi, functionName: "record", args: [owner, BigInt(index), recordKind(kind)] });
|
|
2098
|
-
const text = decodeRecord(hex);
|
|
2099
|
-
if (text) out.push(await parseRecord(kind, text, opts));
|
|
2100
|
-
}
|
|
3192
|
+
for (const r of pickRecords(names, values, kinds)) out.push(await parseRecord(r.kind, r.text, opts));
|
|
2101
3193
|
return out;
|
|
2102
3194
|
}
|
|
2103
3195
|
|
|
2104
|
-
// src/core/
|
|
2105
|
-
var
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
};
|
|
2115
|
-
|
|
2116
|
-
if (
|
|
2117
|
-
|
|
2118
|
-
if (
|
|
2119
|
-
return
|
|
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;
|
|
2120
3212
|
}
|
|
2121
|
-
function
|
|
2122
|
-
if (!
|
|
2123
|
-
const
|
|
2124
|
-
return
|
|
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);
|
|
2125
3217
|
}
|
|
2126
|
-
function
|
|
2127
|
-
const
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
case "signing-key-revoked":
|
|
2154
|
-
return t(
|
|
2155
|
-
`The subkey that signed this claim was revoked on ${at}.`,
|
|
2156
|
-
"Replace the claim with a signature from a current key."
|
|
2157
|
-
);
|
|
2158
|
-
case "unsupported":
|
|
2159
|
-
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.`);
|
|
2160
|
-
case "bad-signature":
|
|
2161
|
-
default:
|
|
2162
|
-
return t("The signature stored with this claim doesn't match its key, so nothing from this claim is shown.");
|
|
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 {
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
if (!meta) return null;
|
|
2163
3245
|
}
|
|
3246
|
+
return avatarUrl(meta?.image ?? meta?.image_url);
|
|
2164
3247
|
}
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
3248
|
+
|
|
3249
|
+
// src/core/ensHint.ts
|
|
3250
|
+
import { namehash, normalize } from "viem/ens";
|
|
3251
|
+
var ENS_HINT_KEY = "id.thurin";
|
|
3252
|
+
function ensHintFor(record, verifiedFingerprint) {
|
|
3253
|
+
const raw = record?.trim() ?? "";
|
|
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" };
|
|
2170
3261
|
}
|
|
2171
|
-
function
|
|
2172
|
-
const
|
|
2173
|
-
|
|
3262
|
+
function ensHintValue(fingerprint) {
|
|
3263
|
+
const fp = normalizeFingerprint(fingerprint);
|
|
3264
|
+
if (!fp) throw new Error(`Not a fingerprint: ${fingerprint}`);
|
|
3265
|
+
return fp.toUpperCase();
|
|
2174
3266
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
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 = normalize(name);
|
|
3273
|
+
return {
|
|
3274
|
+
abi: ENS_TEXT_RESOLVER_ABI,
|
|
3275
|
+
functionName: "setText",
|
|
3276
|
+
args: [namehash(normalized), ENS_HINT_KEY, ensHintValue(fingerprint)],
|
|
3277
|
+
name: normalized
|
|
3278
|
+
};
|
|
2186
3279
|
}
|
|
2187
|
-
function
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
return null;
|
|
3280
|
+
async function fetchEnsHint(client, name, verifiedFingerprint) {
|
|
3281
|
+
const record = await client.getEnsText({ name: normalize(name), key: ENS_HINT_KEY });
|
|
3282
|
+
return ensHintFor(record, verifiedFingerprint);
|
|
2191
3283
|
}
|
|
2192
3284
|
|
|
2193
3285
|
export {
|
|
2194
|
-
REGISTRY_ABI,
|
|
2195
|
-
REGISTRY_ADDRESS,
|
|
2196
|
-
NETWORKS,
|
|
2197
|
-
isNetworkName,
|
|
2198
|
-
getRegistry,
|
|
2199
|
-
chainFor,
|
|
2200
|
-
needsRpcProbe,
|
|
2201
|
-
identityErrorKind,
|
|
2202
|
-
IDENTITY_ERROR_TEXT,
|
|
2203
3286
|
normalizeFingerprint,
|
|
3287
|
+
sameFingerprint,
|
|
2204
3288
|
fingerprintToBytes,
|
|
2205
3289
|
bytesToFingerprint,
|
|
2206
3290
|
fingerprintHash,
|
|
2207
3291
|
keyIdOf,
|
|
2208
3292
|
keyIdToBytes,
|
|
2209
|
-
parsePgpKey,
|
|
2210
|
-
verifyClearsigned,
|
|
2211
|
-
verifyAttestation,
|
|
2212
|
-
stripEmailUserIDs,
|
|
2213
|
-
hasEmailUserID,
|
|
2214
|
-
fetchEFPGraph,
|
|
2215
3293
|
identifyProof,
|
|
2216
3294
|
displayUrl,
|
|
2217
3295
|
proofHref,
|
|
2218
3296
|
proofSecondaryHref,
|
|
2219
3297
|
FARCASTER_HUB,
|
|
2220
3298
|
verifyProof,
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
ensHintWrite,
|
|
2234
|
-
fetchEnsHint,
|
|
3299
|
+
statementText,
|
|
3300
|
+
parsePgpKey,
|
|
3301
|
+
verifyClearsigned,
|
|
3302
|
+
verifyStatementSignature,
|
|
3303
|
+
verifyAttestation,
|
|
3304
|
+
stripEmailUserIDs,
|
|
3305
|
+
hasEmailUserID,
|
|
3306
|
+
leanKey,
|
|
3307
|
+
claimSignature,
|
|
3308
|
+
signatureEmail,
|
|
3309
|
+
payloadText,
|
|
3310
|
+
sshKeys,
|
|
2235
3311
|
EIP712_NAME,
|
|
2236
3312
|
EIP712_VERSION,
|
|
2237
3313
|
registryDomain,
|
|
2238
3314
|
AUTHORIZATION_TYPES,
|
|
3315
|
+
REVOKE_REASONS,
|
|
3316
|
+
OWNER_REVOKE_REASONS,
|
|
2239
3317
|
attestTypedData,
|
|
2240
3318
|
reattestTypedData,
|
|
2241
3319
|
updateKeyTypedData,
|
|
2242
3320
|
revokeTypedData,
|
|
2243
3321
|
setRecordTypedData,
|
|
3322
|
+
markCompromisedTypedData,
|
|
2244
3323
|
authorizationDigest,
|
|
2245
3324
|
recordKind,
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
fetchRecords,
|
|
3325
|
+
REGISTRY_ABI,
|
|
3326
|
+
REGISTRY_ADDRESS,
|
|
3327
|
+
NETWORKS,
|
|
3328
|
+
isNetworkName,
|
|
3329
|
+
getRegistry,
|
|
3330
|
+
chainFor,
|
|
3331
|
+
CLAIM_LIMIT,
|
|
3332
|
+
readClaims,
|
|
3333
|
+
keyStanding,
|
|
3334
|
+
findOwners,
|
|
2257
3335
|
CLAIM_CHECK_LABEL,
|
|
2258
3336
|
formatClaimDate,
|
|
2259
3337
|
claimCheckText,
|
|
3338
|
+
keyProblemText,
|
|
2260
3339
|
expiresSoon,
|
|
2261
3340
|
expiresSoonText,
|
|
2262
3341
|
claimFates,
|
|
2263
|
-
claimFateText
|
|
3342
|
+
claimFateText,
|
|
3343
|
+
encryptionKeyFor,
|
|
3344
|
+
encryptRefusalText,
|
|
3345
|
+
keyChangedText,
|
|
3346
|
+
encryptTo,
|
|
3347
|
+
CONTRACT_ERROR_TEXT,
|
|
3348
|
+
contractErrorName,
|
|
3349
|
+
contractErrorText,
|
|
3350
|
+
MAX_RECORD_BYTES,
|
|
3351
|
+
MAX_KIND_BYTES,
|
|
3352
|
+
kindName,
|
|
3353
|
+
checkKindName,
|
|
3354
|
+
checkRecordValue,
|
|
3355
|
+
KNOWN_KINDS,
|
|
3356
|
+
parseReleases,
|
|
3357
|
+
addRelease,
|
|
3358
|
+
renderReleases,
|
|
3359
|
+
parseRecord,
|
|
3360
|
+
pickRecords,
|
|
3361
|
+
pageRecords,
|
|
3362
|
+
fetchRecords,
|
|
3363
|
+
IPFS_GATEWAYS,
|
|
3364
|
+
IPFS_GATEWAY,
|
|
3365
|
+
ARWEAVE_GATEWAY,
|
|
3366
|
+
avatarUrl,
|
|
3367
|
+
avatarFallbacks,
|
|
3368
|
+
parseNftAvatar,
|
|
3369
|
+
NFT_AVATAR_ABI,
|
|
3370
|
+
nftAvatarImage,
|
|
3371
|
+
ENS_HINT_KEY,
|
|
3372
|
+
ensHintFor,
|
|
3373
|
+
ensHintValue,
|
|
3374
|
+
ENS_TEXT_RESOLVER_ABI,
|
|
3375
|
+
ensHintWrite,
|
|
3376
|
+
fetchEnsHint
|
|
2264
3377
|
};
|
|
2265
|
-
//# sourceMappingURL=chunk-ZAQ2EPF5.js.map
|