@thurinlabs/identity-kit 0.1.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 +157 -0
- package/dist/index.cjs +759 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +173 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +204 -0
- package/dist/index.d.ts +204 -0
- package/dist/index.js +759 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/provider.tsx
|
|
2
|
+
var _react = require('react');
|
|
3
|
+
var _wagmi = require('wagmi');
|
|
4
|
+
var _chains = require('wagmi/chains');
|
|
5
|
+
var _reactquery = require('@tanstack/react-query');
|
|
6
|
+
|
|
7
|
+
// src/context.ts
|
|
8
|
+
|
|
9
|
+
var defaultConfig = {
|
|
10
|
+
scryBaseUrl: "https://scry.thurin.id"
|
|
11
|
+
};
|
|
12
|
+
var IdentityKitContext = _react.createContext.call(void 0, defaultConfig);
|
|
13
|
+
function useIdentityKitConfig() {
|
|
14
|
+
return _react.useContext.call(void 0, IdentityKitContext);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/provider.tsx
|
|
18
|
+
var _jsxruntime = require('react/jsx-runtime');
|
|
19
|
+
var defaultQueryClient = new (0, _reactquery.QueryClient)({
|
|
20
|
+
defaultOptions: {
|
|
21
|
+
queries: {
|
|
22
|
+
staleTime: 6e4,
|
|
23
|
+
retry: 1
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
function createDefaultWagmiConfig(rpcUrl) {
|
|
28
|
+
return _wagmi.createConfig.call(void 0, {
|
|
29
|
+
chains: [_chains.mainnet],
|
|
30
|
+
transports: {
|
|
31
|
+
[_chains.mainnet.id]: _wagmi.http.call(void 0, rpcUrl || "https://ethereum-rpc.publicnode.com")
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function WagmiDetector({ children, rpcUrl }) {
|
|
36
|
+
let hasWagmi = false;
|
|
37
|
+
try {
|
|
38
|
+
_wagmi.useConfig.call(void 0, );
|
|
39
|
+
hasWagmi = true;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
}
|
|
42
|
+
const wagmiConfig = _react.useMemo.call(void 0, () => createDefaultWagmiConfig(rpcUrl), [rpcUrl]);
|
|
43
|
+
if (hasWagmi) {
|
|
44
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children });
|
|
45
|
+
}
|
|
46
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _wagmi.WagmiProvider, { config: wagmiConfig, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactquery.QueryClientProvider, { client: defaultQueryClient, children }) });
|
|
47
|
+
}
|
|
48
|
+
function IdentityKitProvider({
|
|
49
|
+
children,
|
|
50
|
+
rpcUrl,
|
|
51
|
+
neynarApiKey,
|
|
52
|
+
scryBaseUrl = "https://scry.thurin.id"
|
|
53
|
+
}) {
|
|
54
|
+
const config = _react.useMemo.call(void 0,
|
|
55
|
+
() => ({ rpcUrl, neynarApiKey, scryBaseUrl }),
|
|
56
|
+
[rpcUrl, neynarApiKey, scryBaseUrl]
|
|
57
|
+
);
|
|
58
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, IdentityKitContext.Provider, { value: config, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, WagmiDetector, { rpcUrl, children }) });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/hooks/useScryIdentity.ts
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
var _ens = require('viem/ens');
|
|
65
|
+
|
|
66
|
+
// src/hooks/useSignetClaims.ts
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
var _viem = require('viem');
|
|
71
|
+
|
|
72
|
+
// src/core/contract.ts
|
|
73
|
+
var REGISTRY_ADDRESS = "0xf7a45BC662A78a6fb417ED5f52b3766cbf13EbBb";
|
|
74
|
+
var CONTRACT_DEPLOY_BLOCK = 24515891n;
|
|
75
|
+
var REGISTRY_ABI = [
|
|
76
|
+
{
|
|
77
|
+
name: "Attested",
|
|
78
|
+
type: "event",
|
|
79
|
+
inputs: [
|
|
80
|
+
{ name: "ethAddress", type: "address", indexed: true },
|
|
81
|
+
{ name: "fingerprintHash", type: "string", indexed: true },
|
|
82
|
+
{ name: "fingerprint", type: "string", indexed: false },
|
|
83
|
+
{ name: "pgpSignature", type: "string", indexed: false },
|
|
84
|
+
{ name: "pgpPublicKey", type: "string", indexed: false },
|
|
85
|
+
{ name: "index", type: "uint256", indexed: false },
|
|
86
|
+
{ name: "timestamp", type: "uint256", indexed: false }
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "attestationCount",
|
|
91
|
+
type: "function",
|
|
92
|
+
stateMutability: "view",
|
|
93
|
+
inputs: [{ name: "addr", type: "address" }],
|
|
94
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "getAttestation",
|
|
98
|
+
type: "function",
|
|
99
|
+
stateMutability: "view",
|
|
100
|
+
inputs: [
|
|
101
|
+
{ name: "addr", type: "address" },
|
|
102
|
+
{ name: "index", type: "uint256" }
|
|
103
|
+
],
|
|
104
|
+
outputs: [
|
|
105
|
+
{ name: "fingerprint", type: "string" },
|
|
106
|
+
{ name: "createdAt", type: "uint256" },
|
|
107
|
+
{ name: "revoked", type: "bool" }
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
// src/core/logs.ts
|
|
113
|
+
var LOG_CHUNK_SIZE = 49999n;
|
|
114
|
+
async function getLogsChunked(client, params) {
|
|
115
|
+
const latest = await client.getBlockNumber();
|
|
116
|
+
const fromBlock = _nullishCoalesce(params.fromBlock, () => ( CONTRACT_DEPLOY_BLOCK));
|
|
117
|
+
const toBlock = params.toBlock === "latest" || !params.toBlock ? latest : params.toBlock;
|
|
118
|
+
const allLogs = [];
|
|
119
|
+
for (let start = fromBlock; start <= toBlock; start += LOG_CHUNK_SIZE) {
|
|
120
|
+
const end = start + LOG_CHUNK_SIZE - 1n > toBlock ? toBlock : start + LOG_CHUNK_SIZE - 1n;
|
|
121
|
+
const logs = await client.getLogs({
|
|
122
|
+
...params,
|
|
123
|
+
fromBlock: start,
|
|
124
|
+
toBlock: end
|
|
125
|
+
});
|
|
126
|
+
allLogs.push(...logs);
|
|
127
|
+
}
|
|
128
|
+
return allLogs;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/core/pgp.ts
|
|
132
|
+
async function parsePgpKey(armoredKey) {
|
|
133
|
+
try {
|
|
134
|
+
const { readKey } = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
|
|
135
|
+
const key = await readKey({ armoredKey });
|
|
136
|
+
const fingerprint = key.getFingerprint().toUpperCase();
|
|
137
|
+
const userIDs = key.users.map((u) => _optionalChain([u, 'access', _2 => _2.userID, 'optionalAccess', _3 => _3.userID])).filter(Boolean);
|
|
138
|
+
const algorithm = String(key.keyPacket.algorithm);
|
|
139
|
+
const created = _nullishCoalesce(_optionalChain([key, 'access', _4 => _4.keyPacket, 'access', _5 => _5.created, 'optionalAccess', _6 => _6.toISOString, 'call', _7 => _7()]), () => ( null));
|
|
140
|
+
const expiration = await key.getExpirationTime();
|
|
141
|
+
const expires = expiration && expiration !== Infinity ? new Date(expiration).toISOString() : null;
|
|
142
|
+
const notations = [];
|
|
143
|
+
const seen = /* @__PURE__ */ new Set();
|
|
144
|
+
for (const user of key.users) {
|
|
145
|
+
if (!user.selfCertifications) continue;
|
|
146
|
+
for (const cert of user.selfCertifications) {
|
|
147
|
+
if (cert.rawNotations) {
|
|
148
|
+
for (const n of cert.rawNotations) {
|
|
149
|
+
const name = typeof n.name === "string" ? n.name : new TextDecoder().decode(n.name);
|
|
150
|
+
const value = n.value instanceof Uint8Array ? new TextDecoder().decode(n.value) : typeof n.value === "string" ? n.value : null;
|
|
151
|
+
if (value) {
|
|
152
|
+
const key2 = `${name}:${value}`;
|
|
153
|
+
if (!seen.has(key2)) {
|
|
154
|
+
seen.add(key2);
|
|
155
|
+
notations.push({ name, value });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const subkeys = key.subkeys.map((sk) => ({
|
|
163
|
+
algorithm: String(sk.keyPacket.algorithm),
|
|
164
|
+
created: _nullishCoalesce(_optionalChain([sk, 'access', _8 => _8.keyPacket, 'access', _9 => _9.created, 'optionalAccess', _10 => _10.toISOString, 'call', _11 => _11()]), () => ( null)),
|
|
165
|
+
fingerprint: sk.getFingerprint().toUpperCase()
|
|
166
|
+
}));
|
|
167
|
+
return { fingerprint, userIDs, algorithm, created, expires, notations, subkeys };
|
|
168
|
+
} catch (e2) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function verifyAttestation({
|
|
173
|
+
pgpPublicKey,
|
|
174
|
+
pgpSignature,
|
|
175
|
+
fingerprint,
|
|
176
|
+
ethAddress
|
|
177
|
+
}) {
|
|
178
|
+
try {
|
|
179
|
+
if (!pgpPublicKey || !pgpSignature) {
|
|
180
|
+
return { verified: false, reason: "Missing PGP data" };
|
|
181
|
+
}
|
|
182
|
+
const { readKey, readCleartextMessage, verify } = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
|
|
183
|
+
const publicKey = await readKey({ armoredKey: pgpPublicKey });
|
|
184
|
+
const keyFingerprint = publicKey.getFingerprint().toUpperCase();
|
|
185
|
+
if (keyFingerprint !== fingerprint.toUpperCase()) {
|
|
186
|
+
return { verified: false, reason: "Key fingerprint mismatch" };
|
|
187
|
+
}
|
|
188
|
+
const message = await readCleartextMessage({ cleartextMessage: pgpSignature });
|
|
189
|
+
const { signatures } = await verify({ message, verificationKeys: publicKey });
|
|
190
|
+
await signatures[0].verified;
|
|
191
|
+
const signedText = message.getText();
|
|
192
|
+
if (!signedText.toLowerCase().includes(ethAddress.toLowerCase())) {
|
|
193
|
+
return { verified: false, reason: "Signed message does not contain ETH address" };
|
|
194
|
+
}
|
|
195
|
+
return { verified: true };
|
|
196
|
+
} catch (e3) {
|
|
197
|
+
return { verified: false, reason: "Signature verification failed" };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
var KEYSERVER_BASE = "https://keys.openpgp.org/vks/v1";
|
|
201
|
+
async function fetchKeyByFingerprint(fingerprint) {
|
|
202
|
+
try {
|
|
203
|
+
const resp = await fetch(`${KEYSERVER_BASE}/by-fingerprint/${fingerprint}`);
|
|
204
|
+
if (!resp.ok) return null;
|
|
205
|
+
return await resp.text();
|
|
206
|
+
} catch (e4) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function fetchKeyByKeyId(keyId) {
|
|
211
|
+
try {
|
|
212
|
+
const resp = await fetch(`${KEYSERVER_BASE}/by-keyid/${keyId}`);
|
|
213
|
+
if (!resp.ok) return null;
|
|
214
|
+
return await resp.text();
|
|
215
|
+
} catch (e5) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/hooks/useSignetClaims.ts
|
|
221
|
+
function useSignetClaims(address) {
|
|
222
|
+
const config = useIdentityKitConfig();
|
|
223
|
+
const { data: count, isLoading: countLoading } = _wagmi.useReadContract.call(void 0, {
|
|
224
|
+
address: REGISTRY_ADDRESS,
|
|
225
|
+
abi: REGISTRY_ABI,
|
|
226
|
+
functionName: "attestationCount",
|
|
227
|
+
args: address ? [address] : void 0,
|
|
228
|
+
chainId: _chains.mainnet.id,
|
|
229
|
+
query: { enabled: !!address }
|
|
230
|
+
});
|
|
231
|
+
const attestationCount = count ? Number(count) : 0;
|
|
232
|
+
const contracts = address ? Array.from({ length: attestationCount }, (_, i) => ({
|
|
233
|
+
address: REGISTRY_ADDRESS,
|
|
234
|
+
abi: REGISTRY_ABI,
|
|
235
|
+
functionName: "getAttestation",
|
|
236
|
+
args: [address, BigInt(i)],
|
|
237
|
+
chainId: _chains.mainnet.id
|
|
238
|
+
})) : [];
|
|
239
|
+
const { data: attestations, isLoading: attestationsLoading } = _wagmi.useReadContracts.call(void 0, {
|
|
240
|
+
contracts,
|
|
241
|
+
query: { enabled: contracts.length > 0 }
|
|
242
|
+
});
|
|
243
|
+
const { data: eventData } = _reactquery.useQuery.call(void 0, {
|
|
244
|
+
queryKey: ["signet-logs", address],
|
|
245
|
+
queryFn: async () => {
|
|
246
|
+
if (!address) return [];
|
|
247
|
+
const client = _viem.createPublicClient.call(void 0, {
|
|
248
|
+
chain: _chains.mainnet,
|
|
249
|
+
transport: _viem.http.call(void 0, config.rpcUrl || "https://ethereum-rpc.publicnode.com")
|
|
250
|
+
});
|
|
251
|
+
const event = _viem.parseAbiItem.call(void 0,
|
|
252
|
+
"event Attested(address indexed ethAddress, string indexed fingerprintHash, string fingerprint, string pgpSignature, string pgpPublicKey, uint256 index, uint256 timestamp)"
|
|
253
|
+
);
|
|
254
|
+
return getLogsChunked(client, {
|
|
255
|
+
address: REGISTRY_ADDRESS,
|
|
256
|
+
event,
|
|
257
|
+
args: { ethAddress: address },
|
|
258
|
+
fromBlock: CONTRACT_DEPLOY_BLOCK,
|
|
259
|
+
toBlock: "latest"
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
enabled: !!address && attestationCount > 0,
|
|
263
|
+
staleTime: 3e5
|
|
264
|
+
});
|
|
265
|
+
const { data: claims, isLoading: verifyLoading } = _reactquery.useQuery.call(void 0, {
|
|
266
|
+
queryKey: ["signet-claims", address, attestationCount, !!eventData],
|
|
267
|
+
queryFn: async () => {
|
|
268
|
+
if (!attestations || !address) return [];
|
|
269
|
+
const results = [];
|
|
270
|
+
for (let i = 0; i < attestations.length; i++) {
|
|
271
|
+
const att = attestations[i];
|
|
272
|
+
if (att.status !== "success") continue;
|
|
273
|
+
const [fingerprint, createdAt, revoked] = att.result;
|
|
274
|
+
const log = (eventData || [])[i];
|
|
275
|
+
const pgpSignature = _nullishCoalesce(_optionalChain([log, 'optionalAccess', _12 => _12.args, 'optionalAccess', _13 => _13.pgpSignature]), () => ( null));
|
|
276
|
+
const pgpPublicKey = _nullishCoalesce(_optionalChain([log, 'optionalAccess', _14 => _14.args, 'optionalAccess', _15 => _15.pgpPublicKey]), () => ( null));
|
|
277
|
+
const txHash = _nullishCoalesce(_optionalChain([log, 'optionalAccess', _16 => _16.transactionHash]), () => ( null));
|
|
278
|
+
let verification = null;
|
|
279
|
+
if (pgpPublicKey && pgpSignature) {
|
|
280
|
+
verification = await verifyAttestation({
|
|
281
|
+
pgpPublicKey,
|
|
282
|
+
pgpSignature,
|
|
283
|
+
fingerprint,
|
|
284
|
+
ethAddress: address
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
results.push({
|
|
288
|
+
index: i,
|
|
289
|
+
fingerprint,
|
|
290
|
+
createdAt: Number(createdAt),
|
|
291
|
+
revoked,
|
|
292
|
+
pgpSignature,
|
|
293
|
+
pgpPublicKey,
|
|
294
|
+
txHash,
|
|
295
|
+
verification
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return results;
|
|
299
|
+
},
|
|
300
|
+
enabled: !!attestations && attestations.length > 0,
|
|
301
|
+
staleTime: 3e5
|
|
302
|
+
});
|
|
303
|
+
const isLoading = countLoading || attestationsLoading || verifyLoading;
|
|
304
|
+
const activeClaims = (claims || []).filter((c) => !c.revoked);
|
|
305
|
+
const currentFingerprint = activeClaims.length > 0 ? activeClaims[activeClaims.length - 1].fingerprint : null;
|
|
306
|
+
return {
|
|
307
|
+
claims: claims || [],
|
|
308
|
+
totalClaims: attestationCount,
|
|
309
|
+
activeClaims: activeClaims.length,
|
|
310
|
+
currentFingerprint,
|
|
311
|
+
isLoading
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/hooks/useEFPGraph.ts
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
// src/core/efp.ts
|
|
319
|
+
var EFP_API = "https://api.ethfollow.xyz/api/v1";
|
|
320
|
+
async function fetchEFPGraph(address) {
|
|
321
|
+
try {
|
|
322
|
+
const listsResp = await fetch(`${EFP_API}/users/${address}/lists`);
|
|
323
|
+
if (!listsResp.ok) return null;
|
|
324
|
+
const listsData = await listsResp.json();
|
|
325
|
+
const listId = listsData.primary_list;
|
|
326
|
+
if (!listId) return null;
|
|
327
|
+
const [followingResp, statsResp, top8Resp] = await Promise.all([
|
|
328
|
+
fetch(`${EFP_API}/lists/${listId}/following?limit=100`),
|
|
329
|
+
fetch(`${EFP_API}/users/${address}/stats`),
|
|
330
|
+
fetch(`${EFP_API}/lists/${listId}/following?limit=8&tags=top8`)
|
|
331
|
+
]);
|
|
332
|
+
const followingData = followingResp.ok ? await followingResp.json() : { following: [] };
|
|
333
|
+
const statsData = statsResp.ok ? await statsResp.json() : {};
|
|
334
|
+
const top8Data = top8Resp.ok ? await top8Resp.json() : { following: [] };
|
|
335
|
+
return {
|
|
336
|
+
followers: parseInt(statsData.followers_count) || 0,
|
|
337
|
+
following: (followingData.following || []).length,
|
|
338
|
+
top8: (top8Data.following || []).map((f) => f.data),
|
|
339
|
+
hasEfp: true
|
|
340
|
+
};
|
|
341
|
+
} catch (e6) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/hooks/useEFPGraph.ts
|
|
347
|
+
function useEFPGraph(address) {
|
|
348
|
+
const { data, isLoading } = _reactquery.useQuery.call(void 0, {
|
|
349
|
+
queryKey: ["efp-graph", address],
|
|
350
|
+
queryFn: () => fetchEFPGraph(address),
|
|
351
|
+
enabled: !!address,
|
|
352
|
+
staleTime: 3e5
|
|
353
|
+
});
|
|
354
|
+
return {
|
|
355
|
+
efp: _nullishCoalesce(data, () => ( null)),
|
|
356
|
+
isLoading
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/hooks/usePGPProofs.ts
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
// src/core/proofs.ts
|
|
364
|
+
var PROVIDERS = [
|
|
365
|
+
{
|
|
366
|
+
provider: "github",
|
|
367
|
+
label: "GitHub",
|
|
368
|
+
pattern: /^https:\/\/gist\.github\.com\/([^/]+)\/([a-f0-9]+)$/i,
|
|
369
|
+
parse: (m) => ({ user: m[1], gistId: m[2] })
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
provider: "dns",
|
|
373
|
+
label: "DNS",
|
|
374
|
+
pattern: /^dns:([^?]+)\?type=TXT$/i,
|
|
375
|
+
parse: (m) => ({ domain: m[1] })
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
provider: "farcaster",
|
|
379
|
+
label: "Farcaster",
|
|
380
|
+
pattern: /^https:\/\/farcaster\.xyz\/([^/]+)\/(0x[a-f0-9]+)$/i,
|
|
381
|
+
parse: (m) => ({ user: m[1], castHash: m[2] })
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
provider: "codeberg",
|
|
385
|
+
label: "Codeberg",
|
|
386
|
+
pattern: /^https:\/\/codeberg\.org\/([^/]+)\/([^/]+)$/i,
|
|
387
|
+
parse: (m) => ({ user: m[1], repo: m[2] })
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
provider: "mastodon",
|
|
391
|
+
label: "Mastodon",
|
|
392
|
+
pattern: /^https:\/\/([^/]+)\/@([^/]+)$/i,
|
|
393
|
+
parse: (m) => ({ instance: m[1], user: m[2] })
|
|
394
|
+
}
|
|
395
|
+
];
|
|
396
|
+
function identifyProof(notation) {
|
|
397
|
+
if (notation.name !== "proof@thurin.id") return null;
|
|
398
|
+
for (const { provider, label, pattern, parse } of PROVIDERS) {
|
|
399
|
+
const m = notation.value.match(pattern);
|
|
400
|
+
if (m) {
|
|
401
|
+
return { provider, label, url: notation.value, ...parse(m) };
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return { provider: "unknown", label: "Unknown", url: notation.value };
|
|
405
|
+
}
|
|
406
|
+
function displayUrl(proof) {
|
|
407
|
+
if (proof.provider === "dns") return proof.domain;
|
|
408
|
+
if (proof.provider === "github") return proof.user;
|
|
409
|
+
if (proof.provider === "farcaster") return `@${proof.user}`;
|
|
410
|
+
if (proof.provider === "codeberg") return proof.user;
|
|
411
|
+
if (proof.provider === "mastodon") return `@${proof.user}@${proof.instance}`;
|
|
412
|
+
return proof.url;
|
|
413
|
+
}
|
|
414
|
+
function proofHref(proof) {
|
|
415
|
+
if (proof.provider === "dns") return `https://${proof.domain}`;
|
|
416
|
+
if (proof.provider === "github") return `https://github.com/${proof.user}`;
|
|
417
|
+
if (proof.provider === "farcaster") return `https://farcaster.xyz/${proof.user}`;
|
|
418
|
+
if (proof.provider === "codeberg") return `https://codeberg.org/${proof.user}`;
|
|
419
|
+
if (proof.provider === "mastodon") return `https://${proof.instance}/@${proof.user}`;
|
|
420
|
+
if (proof.url.startsWith("http")) return proof.url;
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
function proofSecondaryHref(proof) {
|
|
424
|
+
if (proof.provider === "github") return proof.url;
|
|
425
|
+
if (proof.provider === "farcaster") return proof.url;
|
|
426
|
+
if (proof.provider === "codeberg") return proof.url;
|
|
427
|
+
if (proof.provider === "mastodon") return proof.url;
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
var FPR_TOKEN = "OPENPGP4FPR:";
|
|
431
|
+
function containsFingerprint(text, fingerprint) {
|
|
432
|
+
return text.toUpperCase().includes(FPR_TOKEN + fingerprint.toUpperCase());
|
|
433
|
+
}
|
|
434
|
+
function containsFingerprintUrl(text, fingerprint) {
|
|
435
|
+
return text.toUpperCase().includes(fingerprint.toUpperCase());
|
|
436
|
+
}
|
|
437
|
+
async function verifyGitHub(proof, fingerprint) {
|
|
438
|
+
try {
|
|
439
|
+
const resp = await fetch(`https://api.github.com/gists/${proof.gistId}`);
|
|
440
|
+
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
441
|
+
const data = await resp.json();
|
|
442
|
+
for (const file of Object.values(data.files || {})) {
|
|
443
|
+
if (file.content && containsFingerprint(file.content, fingerprint)) {
|
|
444
|
+
return { verified: true };
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return { verified: false, reason: "Fingerprint token not found in gist" };
|
|
448
|
+
} catch (err) {
|
|
449
|
+
return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async function verifyDNS(proof, fingerprint) {
|
|
453
|
+
try {
|
|
454
|
+
const resp = await fetch(
|
|
455
|
+
`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(proof.domain)}&type=TXT`,
|
|
456
|
+
{ headers: { Accept: "application/dns-json" } }
|
|
457
|
+
);
|
|
458
|
+
if (!resp.ok) return { verified: false, reason: `DNS query returned ${resp.status}` };
|
|
459
|
+
const data = await resp.json();
|
|
460
|
+
for (const answer of data.Answer || []) {
|
|
461
|
+
if (answer.data && containsFingerprint(answer.data, fingerprint)) {
|
|
462
|
+
return { verified: true };
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return { verified: false, reason: "Fingerprint token not found in TXT records" };
|
|
466
|
+
} catch (err) {
|
|
467
|
+
return { verified: false, reason: `DNS fetch failed: ${err.message}` };
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
var NEYNAR_HUB = "https://hub-api.neynar.com";
|
|
471
|
+
async function resolveFid(username, apiKey) {
|
|
472
|
+
const resp = await fetch(
|
|
473
|
+
`${NEYNAR_HUB}/v1/userNameProofByName?name=${encodeURIComponent(username)}`,
|
|
474
|
+
{ headers: { "x-api-key": apiKey } }
|
|
475
|
+
);
|
|
476
|
+
if (!resp.ok) return null;
|
|
477
|
+
const data = await resp.json();
|
|
478
|
+
return _nullishCoalesce(data.fid, () => ( null));
|
|
479
|
+
}
|
|
480
|
+
async function verifyFarcaster(proof, fingerprint, neynarApiKey) {
|
|
481
|
+
if (!neynarApiKey) {
|
|
482
|
+
return { verified: false, reason: "Farcaster verification requires a Neynar API key" };
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
const fid = await resolveFid(proof.user, neynarApiKey);
|
|
486
|
+
if (!fid) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
487
|
+
const headers = { "x-api-key": neynarApiKey };
|
|
488
|
+
let pageToken = "";
|
|
489
|
+
for (let page = 0; page < 5; page++) {
|
|
490
|
+
const url = `${NEYNAR_HUB}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ""}`;
|
|
491
|
+
const resp = await fetch(url, { headers });
|
|
492
|
+
if (!resp.ok) return { verified: false, reason: `Farcaster Hub returned ${resp.status}` };
|
|
493
|
+
const data = await resp.json();
|
|
494
|
+
for (const msg of data.messages || []) {
|
|
495
|
+
if (msg.hash && msg.hash.startsWith(proof.castHash)) {
|
|
496
|
+
const text = _optionalChain([msg, 'access', _17 => _17.data, 'optionalAccess', _18 => _18.castAddBody, 'optionalAccess', _19 => _19.text]) || "";
|
|
497
|
+
if (containsFingerprint(text, fingerprint)) {
|
|
498
|
+
return { verified: true };
|
|
499
|
+
}
|
|
500
|
+
return { verified: false, reason: "Cast found but fingerprint token not in text" };
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (!data.nextPageToken) break;
|
|
504
|
+
pageToken = data.nextPageToken;
|
|
505
|
+
}
|
|
506
|
+
return { verified: false, reason: "Cast not found" };
|
|
507
|
+
} catch (err) {
|
|
508
|
+
return { verified: false, reason: `Farcaster fetch failed: ${err.message}` };
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function verifyCodeberg(proof, fingerprint) {
|
|
512
|
+
try {
|
|
513
|
+
const resp = await fetch(
|
|
514
|
+
`https://codeberg.org/api/v1/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
|
|
515
|
+
);
|
|
516
|
+
if (!resp.ok) return { verified: false, reason: `Codeberg API returned ${resp.status}` };
|
|
517
|
+
const data = await resp.json();
|
|
518
|
+
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
519
|
+
return { verified: true };
|
|
520
|
+
}
|
|
521
|
+
return { verified: false, reason: "Fingerprint token not found in repo description" };
|
|
522
|
+
} catch (err) {
|
|
523
|
+
return { verified: false, reason: `Codeberg fetch failed: ${err.message}` };
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async function verifyMastodon(proof, fingerprint) {
|
|
527
|
+
try {
|
|
528
|
+
const resp = await fetch(
|
|
529
|
+
`https://${proof.instance}/api/v1/accounts/lookup?acct=${encodeURIComponent(proof.user)}`
|
|
530
|
+
);
|
|
531
|
+
if (!resp.ok) return { verified: false, reason: `Mastodon API returned ${resp.status}` };
|
|
532
|
+
const data = await resp.json();
|
|
533
|
+
const keyId = fingerprint.slice(-16);
|
|
534
|
+
for (const field of data.fields || []) {
|
|
535
|
+
const text = field.value.replace(/<[^>]*>/g, "");
|
|
536
|
+
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
537
|
+
return { verified: true };
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (data.note) {
|
|
541
|
+
const text = data.note.replace(/<[^>]*>/g, "");
|
|
542
|
+
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
543
|
+
return { verified: true };
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return { verified: false, reason: "Fingerprint not found in profile fields or bio" };
|
|
547
|
+
} catch (err) {
|
|
548
|
+
return { verified: false, reason: `Mastodon fetch failed: ${err.message}` };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
var verifiers = {
|
|
552
|
+
github: verifyGitHub,
|
|
553
|
+
dns: verifyDNS,
|
|
554
|
+
farcaster: verifyFarcaster,
|
|
555
|
+
codeberg: verifyCodeberg,
|
|
556
|
+
mastodon: verifyMastodon
|
|
557
|
+
};
|
|
558
|
+
async function verifyProof(proof, fingerprint, neynarApiKey) {
|
|
559
|
+
const fn = verifiers[proof.provider];
|
|
560
|
+
if (!fn) return { verified: false, reason: "Unknown provider" };
|
|
561
|
+
return fn(proof, fingerprint, neynarApiKey);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/hooks/usePGPProofs.ts
|
|
565
|
+
function usePGPProofs(fingerprint) {
|
|
566
|
+
const config = useIdentityKitConfig();
|
|
567
|
+
const { data, isLoading } = _reactquery.useQuery.call(void 0, {
|
|
568
|
+
queryKey: ["pgp-proofs", fingerprint],
|
|
569
|
+
queryFn: async () => {
|
|
570
|
+
if (!fingerprint) return { keyInfo: null, proofs: [] };
|
|
571
|
+
const armoredKey = await fetchKeyByFingerprint(fingerprint);
|
|
572
|
+
if (!armoredKey) return { keyInfo: null, proofs: [] };
|
|
573
|
+
const keyInfo = await parsePgpKey(armoredKey);
|
|
574
|
+
if (!keyInfo) return { keyInfo: null, proofs: [] };
|
|
575
|
+
const identified = keyInfo.notations.map((n) => identifyProof(n)).filter((p) => p !== null);
|
|
576
|
+
const proofs = await Promise.all(
|
|
577
|
+
identified.map(async (proof) => {
|
|
578
|
+
const result = await verifyProof(proof, fingerprint, config.neynarApiKey);
|
|
579
|
+
return {
|
|
580
|
+
provider: proof.provider,
|
|
581
|
+
label: proof.label,
|
|
582
|
+
url: proof.url,
|
|
583
|
+
displayUrl: displayUrl(proof),
|
|
584
|
+
href: proofHref(proof),
|
|
585
|
+
secondaryHref: proofSecondaryHref(proof),
|
|
586
|
+
status: result.verified ? "verified" : "unverified",
|
|
587
|
+
reason: result.reason
|
|
588
|
+
};
|
|
589
|
+
})
|
|
590
|
+
);
|
|
591
|
+
return { keyInfo, proofs };
|
|
592
|
+
},
|
|
593
|
+
enabled: !!fingerprint,
|
|
594
|
+
staleTime: 3e5
|
|
595
|
+
});
|
|
596
|
+
return {
|
|
597
|
+
keyInfo: _nullishCoalesce(_optionalChain([data, 'optionalAccess', _20 => _20.keyInfo]), () => ( null)),
|
|
598
|
+
proofs: _nullishCoalesce(_optionalChain([data, 'optionalAccess', _21 => _21.proofs]), () => ( [])),
|
|
599
|
+
isLoading
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/hooks/useScryIdentity.ts
|
|
604
|
+
function isAddress(value) {
|
|
605
|
+
return /^0x[0-9a-fA-F]{40}$/.test(value);
|
|
606
|
+
}
|
|
607
|
+
function safeNormalize(name) {
|
|
608
|
+
try {
|
|
609
|
+
return _ens.normalize.call(void 0, name);
|
|
610
|
+
} catch (e7) {
|
|
611
|
+
return void 0;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function useScryIdentity(ensOrAddress) {
|
|
615
|
+
const isAddr = ensOrAddress ? isAddress(ensOrAddress) : false;
|
|
616
|
+
const ensInput = ensOrAddress && !isAddr ? safeNormalize(ensOrAddress) : void 0;
|
|
617
|
+
const { data: resolvedAddress } = _wagmi.useEnsAddress.call(void 0, {
|
|
618
|
+
name: ensInput,
|
|
619
|
+
chainId: _chains.mainnet.id,
|
|
620
|
+
query: { enabled: !!ensInput }
|
|
621
|
+
});
|
|
622
|
+
const address = isAddr ? ensOrAddress : _nullishCoalesce(resolvedAddress, () => ( null));
|
|
623
|
+
const { data: ensName } = _wagmi.useEnsName.call(void 0, {
|
|
624
|
+
address,
|
|
625
|
+
chainId: _chains.mainnet.id,
|
|
626
|
+
query: { enabled: !!address }
|
|
627
|
+
});
|
|
628
|
+
const displayName = ensInput || ensName || null;
|
|
629
|
+
const { data: ensAvatar } = _wagmi.useEnsAvatar.call(void 0, {
|
|
630
|
+
name: displayName ? safeNormalize(displayName) : void 0,
|
|
631
|
+
chainId: _chains.mainnet.id,
|
|
632
|
+
query: { enabled: !!displayName }
|
|
633
|
+
});
|
|
634
|
+
const {
|
|
635
|
+
claims,
|
|
636
|
+
totalClaims,
|
|
637
|
+
activeClaims,
|
|
638
|
+
currentFingerprint,
|
|
639
|
+
isLoading: claimsLoading
|
|
640
|
+
} = useSignetClaims(address);
|
|
641
|
+
const {
|
|
642
|
+
keyInfo: pgpKeyInfo,
|
|
643
|
+
proofs,
|
|
644
|
+
isLoading: proofsLoading
|
|
645
|
+
} = usePGPProofs(currentFingerprint);
|
|
646
|
+
const { efp, isLoading: efpLoading } = useEFPGraph(address);
|
|
647
|
+
return {
|
|
648
|
+
address,
|
|
649
|
+
ensName: displayName,
|
|
650
|
+
ensAvatar: _nullishCoalesce(ensAvatar, () => ( null)),
|
|
651
|
+
claims,
|
|
652
|
+
totalClaims,
|
|
653
|
+
activeClaims,
|
|
654
|
+
currentFingerprint,
|
|
655
|
+
pgpKeyInfo,
|
|
656
|
+
proofs,
|
|
657
|
+
efp,
|
|
658
|
+
isLoading: claimsLoading || proofsLoading || efpLoading,
|
|
659
|
+
error: null
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/components/ScryCard/ScryCard.tsx
|
|
664
|
+
|
|
665
|
+
function ThurinLogo() {
|
|
666
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "svg", { className: "scry-card-avatar-placeholder", viewBox: "0 0 100 100", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
667
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "50", cy: "50", r: "50", fill: "var(--scry-surface-deep)" }),
|
|
668
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M25 80 Q25 25 50 25 Q75 25 75 50", fill: "none", stroke: "#7c9a3e", strokeWidth: "4", strokeLinecap: "round" }),
|
|
669
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M33 75 Q33 35 50 35 Q67 35 67 52", fill: "none", stroke: "#7c9a3e", strokeWidth: "4", strokeLinecap: "round" }),
|
|
670
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M41 70 Q41 45 50 45 Q59 45 59 55", fill: "none", stroke: "#c9a227", strokeWidth: "4", strokeLinecap: "round" }),
|
|
671
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M50 65 L50 53", fill: "none", stroke: "#c9a227", strokeWidth: "4", strokeLinecap: "round" })
|
|
672
|
+
] });
|
|
673
|
+
}
|
|
674
|
+
function ScryCard({ ens, address, theme = "thurin" }) {
|
|
675
|
+
const input = ens || address;
|
|
676
|
+
const identity = useScryIdentity(input);
|
|
677
|
+
const config = useIdentityKitConfig();
|
|
678
|
+
const scryUrl = identity.address ? `${config.scryBaseUrl}/#/eth/${identity.address}` : ens ? `${config.scryBaseUrl}/#/ens/${ens}` : null;
|
|
679
|
+
if (identity.isLoading) {
|
|
680
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "scry-card", "data-scry-theme": theme, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "scry-card-loading", children: "Loading identity..." }) });
|
|
681
|
+
}
|
|
682
|
+
const displayAddress = identity.address || null;
|
|
683
|
+
const verifiedProofs = identity.proofs.filter((p) => p.status === "verified").length;
|
|
684
|
+
const hasVerifiedPgp = identity.claims.some((c) => _optionalChain([c, 'access', _22 => _22.verification, 'optionalAccess', _23 => _23.verified]) && !c.revoked);
|
|
685
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "scry-card", "data-scry-theme": theme, children: [
|
|
686
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "a", { className: "scry-card-header", href: scryUrl || void 0, target: "_blank", rel: "noopener noreferrer", children: [
|
|
687
|
+
identity.ensAvatar ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
688
|
+
"img",
|
|
689
|
+
{
|
|
690
|
+
className: "scry-card-avatar",
|
|
691
|
+
src: identity.ensAvatar,
|
|
692
|
+
alt: identity.ensName || ""
|
|
693
|
+
}
|
|
694
|
+
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ThurinLogo, {}),
|
|
695
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { children: [
|
|
696
|
+
identity.ensName && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "scry-card-name", children: identity.ensName }),
|
|
697
|
+
displayAddress && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "scry-card-address", children: displayAddress })
|
|
698
|
+
] })
|
|
699
|
+
] }),
|
|
700
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "scry-card-stats", children: [
|
|
701
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "scry-card-stat", children: [
|
|
702
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-stat-value", children: identity.activeClaims }),
|
|
703
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-stat-label", children: "Seals" })
|
|
704
|
+
] }),
|
|
705
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "scry-card-stat", children: [
|
|
706
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-stat-value", children: verifiedProofs }),
|
|
707
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-stat-label", children: "Proofs" })
|
|
708
|
+
] }),
|
|
709
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "scry-card-stat", children: [
|
|
710
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-stat-value", children: _nullishCoalesce(_optionalChain([identity, 'access', _24 => _24.efp, 'optionalAccess', _25 => _25.followers]), () => ( 0)) }),
|
|
711
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-stat-label", children: "Followers" })
|
|
712
|
+
] })
|
|
713
|
+
] }),
|
|
714
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "scry-card-badges", children: [
|
|
715
|
+
hasVerifiedPgp && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-badge scry-card-badge--verified", children: "PGP Verified" }),
|
|
716
|
+
_optionalChain([identity, 'access', _26 => _26.efp, 'optionalAccess', _27 => _27.hasEfp]) && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "scry-card-badge scry-card-badge--verified", children: "EFP" }),
|
|
717
|
+
identity.proofs.map((p, i) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
718
|
+
"span",
|
|
719
|
+
{
|
|
720
|
+
className: `scry-card-badge scry-card-badge--${p.status === "verified" ? "verified" : "unverified"}`,
|
|
721
|
+
children: p.label
|
|
722
|
+
},
|
|
723
|
+
i
|
|
724
|
+
))
|
|
725
|
+
] }),
|
|
726
|
+
scryUrl && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
727
|
+
"a",
|
|
728
|
+
{
|
|
729
|
+
className: "scry-card-link",
|
|
730
|
+
href: scryUrl,
|
|
731
|
+
target: "_blank",
|
|
732
|
+
rel: "noopener noreferrer",
|
|
733
|
+
children: "View on Scry"
|
|
734
|
+
}
|
|
735
|
+
)
|
|
736
|
+
] });
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
exports.CONTRACT_DEPLOY_BLOCK = CONTRACT_DEPLOY_BLOCK; exports.IdentityKitProvider = IdentityKitProvider; exports.REGISTRY_ABI = REGISTRY_ABI; exports.REGISTRY_ADDRESS = REGISTRY_ADDRESS; exports.ScryCard = ScryCard; exports.displayUrl = displayUrl; exports.fetchEFPGraph = fetchEFPGraph; exports.fetchKeyByFingerprint = fetchKeyByFingerprint; exports.fetchKeyByKeyId = fetchKeyByKeyId; exports.identifyProof = identifyProof; exports.parsePgpKey = parsePgpKey; exports.proofHref = proofHref; exports.proofSecondaryHref = proofSecondaryHref; exports.useEFPGraph = useEFPGraph; exports.usePGPProofs = usePGPProofs; exports.useScryIdentity = useScryIdentity; exports.useSignetClaims = useSignetClaims; exports.verifyAttestation = verifyAttestation; exports.verifyProof = verifyProof;
|
|
759
|
+
//# sourceMappingURL=index.cjs.map
|