@thurinlabs/identity-kit 1.3.2 → 1.3.4
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 +14 -0
- package/dist/{chunk-D7YTACXZ.js → chunk-CF64QQZO.js} +65 -1
- package/dist/chunk-CF64QQZO.js.map +1 -0
- package/dist/{chunk-HUJD6LYZ.cjs → chunk-YTO7Z5SI.cjs} +71 -7
- package/dist/chunk-YTO7Z5SI.cjs.map +1 -0
- package/dist/core.cjs +20 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +80 -1
- package/dist/core.d.ts +80 -1
- package/dist/core.js +19 -1
- package/dist/embed.global.js +10645 -903
- package/dist/embed.global.js.map +1 -1
- package/dist/index.cjs +129 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +18 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +105 -21
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-D7YTACXZ.js.map +0 -1
- package/dist/chunk-HUJD6LYZ.cjs.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { IdentityKitConfig, Theme, ThurinIdentity, Attestation, EFPGraph, PGPKeyInfo, ProofResult, EnsHint, ParsedRecord } from './core.cjs';
|
|
4
|
-
export { AUTHORIZATION_TYPES, AttestAuthorization, AuthorizationAction, EIP712_NAME, EIP712_VERSION, ENS_HINT_KEY, ENS_TEXT_RESOLVER_ABI, EnsHintState, EnsTextReader, IDENTITY_KINDS, KNOWN_KINDS, KnownKind, MAX_RECORD_BYTES, NETWORKS, NetworkName, Notation, PGPVerification, PointerEntry, PointerRecord, Proof, REGISTRY_ABI, REGISTRY_ADDRESS, ReattestAuthorization, RecordData, RecordReader, RegistryDeployment, RevokeAuthorization, SetRecordAuthorization, StrippedKey, SubkeyInfo, UpdateKeyAuthorization, addPointer, attestTypedData, authorizationDigest, bytesToFingerprint, chainFor, decodeRecord, displayUrl, encodeRecord, ensHintFor, ensHintValue, ensHintWrite, fetchEFPGraph, fetchEnsHint, fetchRecords, fingerprintHash, fingerprintToBytes, getRegistry, hasEmailUserID, identifyProof, isNetworkName, keyIdOf, keyIdToBytes, kindName, normalizeFingerprint, parsePgpKey, parsePointer, parseRecord, proofHref, proofSecondaryHref, reattestTypedData, recordKind, registryDomain, renderPointer, revokeTypedData, setRecordTypedData, stripEmailUserIDs, updateKeyTypedData, verifyAttestation, verifyClearsigned, verifyProof } from './core.cjs';
|
|
4
|
+
export { ARWEAVE_GATEWAY, AUTHORIZATION_TYPES, AttestAuthorization, AuthorizationAction, EIP712_NAME, EIP712_VERSION, ENS_HINT_KEY, ENS_TEXT_RESOLVER_ABI, EnsHintState, EnsTextReader, IDENTITY_ERROR_TEXT, IDENTITY_KINDS, IPFS_GATEWAY, IdentityErrorKind, IdentityLookupState, KNOWN_KINDS, KnownKind, MAX_RECORD_BYTES, NETWORKS, NFT_AVATAR_ABI, NetworkName, NftAvatar, Notation, PGPVerification, PointerEntry, PointerRecord, Proof, REGISTRY_ABI, REGISTRY_ADDRESS, ReattestAuthorization, RecordData, RecordReader, RegistryDeployment, RevokeAuthorization, SetRecordAuthorization, StrippedKey, SubkeyInfo, UpdateKeyAuthorization, addPointer, attestTypedData, authorizationDigest, avatarUrl, bytesToFingerprint, chainFor, decodeRecord, displayUrl, encodeRecord, ensHintFor, ensHintValue, ensHintWrite, fetchEFPGraph, fetchEnsHint, fetchRecords, fingerprintHash, fingerprintToBytes, getRegistry, hasEmailUserID, identifyProof, identityErrorKind, isNetworkName, keyIdOf, keyIdToBytes, kindName, needsRpcProbe, nftAvatarImage, normalizeFingerprint, parseNftAvatar, parsePgpKey, parsePointer, parseRecord, proofHref, proofSecondaryHref, reattestTypedData, recordKind, registryDomain, renderPointer, revokeTypedData, setRecordTypedData, stripEmailUserIDs, updateKeyTypedData, verifyAttestation, verifyClearsigned, verifyProof } from './core.cjs';
|
|
5
5
|
import 'viem';
|
|
6
6
|
|
|
7
7
|
interface IdentityKitProviderProps extends IdentityKitConfig {
|
|
@@ -29,6 +29,9 @@ declare function useAttestations(address: string | undefined | null): {
|
|
|
29
29
|
activeClaims: number;
|
|
30
30
|
currentFingerprint: string | null;
|
|
31
31
|
isLoading: boolean;
|
|
32
|
+
/** A registry read failed (the RPC may be down; useThurinIdentity finds out which). */
|
|
33
|
+
error: Error | null;
|
|
34
|
+
refetch: () => void;
|
|
32
35
|
};
|
|
33
36
|
|
|
34
37
|
declare function useEFPGraph(address: string | undefined | null): {
|
|
@@ -71,4 +74,11 @@ declare function useRecords(address: string | undefined | null, index: number |
|
|
|
71
74
|
refetch: () => void;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
/**
|
|
78
|
+
* The avatar to show for a normalized ENS name, or null. Reads the raw `avatar` record and
|
|
79
|
+
* loads only what `avatarUrl` allows (see core/avatar.ts); wagmi's useEnsAvatar would fetch
|
|
80
|
+
* whatever host the name's owner chose.
|
|
81
|
+
*/
|
|
82
|
+
declare function useSafeAvatar(name: string | undefined, chainId: number): string | null;
|
|
83
|
+
|
|
84
|
+
export { Attestation, EFPGraph, EnsHint, IdentityKitConfig, IdentityKitProvider, type IdentityKitProviderProps, PGPKeyInfo, ParsedRecord, ProofResult, Theme, ThurinCard, type ThurinCardProps, ThurinIdentity, useAttestations, useEFPGraph, useEnsHint, usePGPProofs, useRecords, useSafeAvatar, useThurinIdentity };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { IdentityKitConfig, Theme, ThurinIdentity, Attestation, EFPGraph, PGPKeyInfo, ProofResult, EnsHint, ParsedRecord } from './core.js';
|
|
4
|
-
export { AUTHORIZATION_TYPES, AttestAuthorization, AuthorizationAction, EIP712_NAME, EIP712_VERSION, ENS_HINT_KEY, ENS_TEXT_RESOLVER_ABI, EnsHintState, EnsTextReader, IDENTITY_KINDS, KNOWN_KINDS, KnownKind, MAX_RECORD_BYTES, NETWORKS, NetworkName, Notation, PGPVerification, PointerEntry, PointerRecord, Proof, REGISTRY_ABI, REGISTRY_ADDRESS, ReattestAuthorization, RecordData, RecordReader, RegistryDeployment, RevokeAuthorization, SetRecordAuthorization, StrippedKey, SubkeyInfo, UpdateKeyAuthorization, addPointer, attestTypedData, authorizationDigest, bytesToFingerprint, chainFor, decodeRecord, displayUrl, encodeRecord, ensHintFor, ensHintValue, ensHintWrite, fetchEFPGraph, fetchEnsHint, fetchRecords, fingerprintHash, fingerprintToBytes, getRegistry, hasEmailUserID, identifyProof, isNetworkName, keyIdOf, keyIdToBytes, kindName, normalizeFingerprint, parsePgpKey, parsePointer, parseRecord, proofHref, proofSecondaryHref, reattestTypedData, recordKind, registryDomain, renderPointer, revokeTypedData, setRecordTypedData, stripEmailUserIDs, updateKeyTypedData, verifyAttestation, verifyClearsigned, verifyProof } from './core.js';
|
|
4
|
+
export { ARWEAVE_GATEWAY, AUTHORIZATION_TYPES, AttestAuthorization, AuthorizationAction, EIP712_NAME, EIP712_VERSION, ENS_HINT_KEY, ENS_TEXT_RESOLVER_ABI, EnsHintState, EnsTextReader, IDENTITY_ERROR_TEXT, IDENTITY_KINDS, IPFS_GATEWAY, IdentityErrorKind, IdentityLookupState, KNOWN_KINDS, KnownKind, MAX_RECORD_BYTES, NETWORKS, NFT_AVATAR_ABI, NetworkName, NftAvatar, Notation, PGPVerification, PointerEntry, PointerRecord, Proof, REGISTRY_ABI, REGISTRY_ADDRESS, ReattestAuthorization, RecordData, RecordReader, RegistryDeployment, RevokeAuthorization, SetRecordAuthorization, StrippedKey, SubkeyInfo, UpdateKeyAuthorization, addPointer, attestTypedData, authorizationDigest, avatarUrl, bytesToFingerprint, chainFor, decodeRecord, displayUrl, encodeRecord, ensHintFor, ensHintValue, ensHintWrite, fetchEFPGraph, fetchEnsHint, fetchRecords, fingerprintHash, fingerprintToBytes, getRegistry, hasEmailUserID, identifyProof, identityErrorKind, isNetworkName, keyIdOf, keyIdToBytes, kindName, needsRpcProbe, nftAvatarImage, normalizeFingerprint, parseNftAvatar, parsePgpKey, parsePointer, parseRecord, proofHref, proofSecondaryHref, reattestTypedData, recordKind, registryDomain, renderPointer, revokeTypedData, setRecordTypedData, stripEmailUserIDs, updateKeyTypedData, verifyAttestation, verifyClearsigned, verifyProof } from './core.js';
|
|
5
5
|
import 'viem';
|
|
6
6
|
|
|
7
7
|
interface IdentityKitProviderProps extends IdentityKitConfig {
|
|
@@ -29,6 +29,9 @@ declare function useAttestations(address: string | undefined | null): {
|
|
|
29
29
|
activeClaims: number;
|
|
30
30
|
currentFingerprint: string | null;
|
|
31
31
|
isLoading: boolean;
|
|
32
|
+
/** A registry read failed (the RPC may be down; useThurinIdentity finds out which). */
|
|
33
|
+
error: Error | null;
|
|
34
|
+
refetch: () => void;
|
|
32
35
|
};
|
|
33
36
|
|
|
34
37
|
declare function useEFPGraph(address: string | undefined | null): {
|
|
@@ -71,4 +74,11 @@ declare function useRecords(address: string | undefined | null, index: number |
|
|
|
71
74
|
refetch: () => void;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
|
-
|
|
77
|
+
/**
|
|
78
|
+
* The avatar to show for a normalized ENS name, or null. Reads the raw `avatar` record and
|
|
79
|
+
* loads only what `avatarUrl` allows (see core/avatar.ts); wagmi's useEnsAvatar would fetch
|
|
80
|
+
* whatever host the name's owner chose.
|
|
81
|
+
*/
|
|
82
|
+
declare function useSafeAvatar(name: string | undefined, chainId: number): string | null;
|
|
83
|
+
|
|
84
|
+
export { Attestation, EFPGraph, EnsHint, IdentityKitConfig, IdentityKitProvider, type IdentityKitProviderProps, PGPKeyInfo, ParsedRecord, ProofResult, Theme, ThurinCard, type ThurinCardProps, ThurinIdentity, useAttestations, useEFPGraph, useEnsHint, usePGPProofs, useRecords, useSafeAvatar, useThurinIdentity };
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ARWEAVE_GATEWAY,
|
|
2
3
|
AUTHORIZATION_TYPES,
|
|
3
4
|
EIP712_NAME,
|
|
4
5
|
EIP712_VERSION,
|
|
5
6
|
ENS_HINT_KEY,
|
|
6
7
|
ENS_TEXT_RESOLVER_ABI,
|
|
8
|
+
IDENTITY_ERROR_TEXT,
|
|
7
9
|
IDENTITY_KINDS,
|
|
10
|
+
IPFS_GATEWAY,
|
|
8
11
|
KNOWN_KINDS,
|
|
9
12
|
MAX_RECORD_BYTES,
|
|
10
13
|
NETWORKS,
|
|
14
|
+
NFT_AVATAR_ABI,
|
|
11
15
|
REGISTRY_ABI,
|
|
12
16
|
REGISTRY_ADDRESS,
|
|
13
17
|
addPointer,
|
|
14
18
|
attestTypedData,
|
|
15
19
|
authorizationDigest,
|
|
20
|
+
avatarUrl,
|
|
16
21
|
bytesToFingerprint,
|
|
17
22
|
chainFor,
|
|
18
23
|
decodeRecord,
|
|
@@ -29,11 +34,15 @@ import {
|
|
|
29
34
|
getRegistry,
|
|
30
35
|
hasEmailUserID,
|
|
31
36
|
identifyProof,
|
|
37
|
+
identityErrorKind,
|
|
32
38
|
isNetworkName,
|
|
33
39
|
keyIdOf,
|
|
34
40
|
keyIdToBytes,
|
|
35
41
|
kindName,
|
|
42
|
+
needsRpcProbe,
|
|
43
|
+
nftAvatarImage,
|
|
36
44
|
normalizeFingerprint,
|
|
45
|
+
parseNftAvatar,
|
|
37
46
|
parsePgpKey,
|
|
38
47
|
parsePointer,
|
|
39
48
|
parseRecord,
|
|
@@ -50,7 +59,7 @@ import {
|
|
|
50
59
|
verifyAttestation,
|
|
51
60
|
verifyClearsigned,
|
|
52
61
|
verifyProof
|
|
53
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-CF64QQZO.js";
|
|
54
63
|
|
|
55
64
|
// src/provider.tsx
|
|
56
65
|
import { useMemo } from "react";
|
|
@@ -114,7 +123,8 @@ function IdentityKitProvider({
|
|
|
114
123
|
}
|
|
115
124
|
|
|
116
125
|
// src/hooks/useThurinIdentity.ts
|
|
117
|
-
import { useEnsAddress, useEnsName,
|
|
126
|
+
import { useEnsAddress, useEnsName, usePublicClient } from "wagmi";
|
|
127
|
+
import { useQuery as useQuery5 } from "@tanstack/react-query";
|
|
118
128
|
import { normalize } from "viem/ens";
|
|
119
129
|
|
|
120
130
|
// src/hooks/useAttestations.ts
|
|
@@ -126,7 +136,7 @@ function useAttestations(address) {
|
|
|
126
136
|
const registry = getRegistry(config.network, config.registryAddress);
|
|
127
137
|
const chain = chainFor(config.network);
|
|
128
138
|
const owner = address;
|
|
129
|
-
const { data: rows, isLoading: rowsLoading } = useReadContract({
|
|
139
|
+
const { data: rows, isLoading: rowsLoading, isFetched: rowsFetched, error: rowsError, refetch: refetchRows } = useReadContract({
|
|
130
140
|
address: registry.address,
|
|
131
141
|
abi: REGISTRY_ABI,
|
|
132
142
|
functionName: "attestationsOf",
|
|
@@ -142,7 +152,7 @@ function useAttestations(address) {
|
|
|
142
152
|
args: [owner, BigInt(i)],
|
|
143
153
|
chainId: chain.id
|
|
144
154
|
})) : [];
|
|
145
|
-
const { data: payloads, isLoading: payloadsLoading } = useReadContracts({
|
|
155
|
+
const { data: payloads, isLoading: payloadsLoading, error: payloadsError, refetch: refetchPayloads } = useReadContracts({
|
|
146
156
|
contracts,
|
|
147
157
|
query: { enabled: contracts.length > 0 }
|
|
148
158
|
});
|
|
@@ -182,7 +192,7 @@ function useAttestations(address) {
|
|
|
182
192
|
enabled: !!rows && payloadsReady,
|
|
183
193
|
staleTime: 3e5
|
|
184
194
|
});
|
|
185
|
-
const isLoading = rowsLoading || payloadsLoading || verifyLoading || !!owner && rows !== void 0 && !payloadsReady;
|
|
195
|
+
const isLoading = rowsLoading || !!owner && !rowsFetched || payloadsLoading || verifyLoading || !!owner && rows !== void 0 && !payloadsReady;
|
|
186
196
|
const activeClaims = (claims || []).filter((c) => !c.revoked);
|
|
187
197
|
const verifiedClaims = activeClaims.filter((c) => c.verification?.verified);
|
|
188
198
|
const currentFingerprint = verifiedClaims.length > 0 ? verifiedClaims[verifiedClaims.length - 1].fingerprint : null;
|
|
@@ -191,7 +201,15 @@ function useAttestations(address) {
|
|
|
191
201
|
totalClaims: count,
|
|
192
202
|
activeClaims: activeClaims.length,
|
|
193
203
|
currentFingerprint,
|
|
194
|
-
isLoading
|
|
204
|
+
isLoading,
|
|
205
|
+
/** A registry read failed (the RPC may be down; useThurinIdentity finds out which). */
|
|
206
|
+
error: rowsError ?? payloadsError ?? null,
|
|
207
|
+
refetch: () => {
|
|
208
|
+
if (owner) {
|
|
209
|
+
refetchRows();
|
|
210
|
+
if (contracts.length > 0) refetchPayloads();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
195
213
|
};
|
|
196
214
|
}
|
|
197
215
|
|
|
@@ -262,6 +280,29 @@ function usePGPProofs(fingerprint, armoredKey) {
|
|
|
262
280
|
};
|
|
263
281
|
}
|
|
264
282
|
|
|
283
|
+
// src/hooks/useSafeAvatar.ts
|
|
284
|
+
import { useEnsText, useReadContract as useReadContract2 } from "wagmi";
|
|
285
|
+
import { useQuery as useQuery4 } from "@tanstack/react-query";
|
|
286
|
+
function useSafeAvatar(name, chainId) {
|
|
287
|
+
const { data: raw } = useEnsText({ name, key: "avatar", chainId, query: { enabled: !!name } });
|
|
288
|
+
const direct = avatarUrl(raw);
|
|
289
|
+
const nft = direct ? null : parseNftAvatar(raw);
|
|
290
|
+
const { data: tokenUri } = useReadContract2({
|
|
291
|
+
address: nft?.contract,
|
|
292
|
+
abi: NFT_AVATAR_ABI,
|
|
293
|
+
functionName: nft?.standard === "erc1155" ? "uri" : "tokenURI",
|
|
294
|
+
args: nft ? [nft.tokenId] : void 0,
|
|
295
|
+
chainId,
|
|
296
|
+
query: { enabled: !!nft && nft.chainId === chainId }
|
|
297
|
+
});
|
|
298
|
+
const { data: image } = useQuery4({
|
|
299
|
+
queryKey: ["thurin-nft-avatar", tokenUri, nft?.tokenId.toString()],
|
|
300
|
+
queryFn: () => nftAvatarImage(tokenUri, nft.tokenId).catch(() => null),
|
|
301
|
+
enabled: typeof tokenUri === "string" && !!nft
|
|
302
|
+
});
|
|
303
|
+
return direct ?? image ?? null;
|
|
304
|
+
}
|
|
305
|
+
|
|
265
306
|
// src/hooks/useThurinIdentity.ts
|
|
266
307
|
function isAddress(value) {
|
|
267
308
|
return /^0x[0-9a-fA-F]{40}$/.test(value);
|
|
@@ -277,7 +318,7 @@ function useThurinIdentity(ensOrAddress) {
|
|
|
277
318
|
const chain = chainFor(useIdentityKitConfig().network);
|
|
278
319
|
const isAddr = ensOrAddress ? isAddress(ensOrAddress) : false;
|
|
279
320
|
const ensInput = ensOrAddress && !isAddr ? safeNormalize(ensOrAddress) : void 0;
|
|
280
|
-
const { data: resolvedAddress } = useEnsAddress({
|
|
321
|
+
const { data: resolvedAddress, error: ensError, isFetched: ensFetched, refetch: refetchEns } = useEnsAddress({
|
|
281
322
|
name: ensInput,
|
|
282
323
|
chainId: chain.id,
|
|
283
324
|
query: { enabled: !!ensInput }
|
|
@@ -289,17 +330,15 @@ function useThurinIdentity(ensOrAddress) {
|
|
|
289
330
|
query: { enabled: !!address }
|
|
290
331
|
});
|
|
291
332
|
const displayName = ensInput || ensName || null;
|
|
292
|
-
const
|
|
293
|
-
name: displayName ? safeNormalize(displayName) : void 0,
|
|
294
|
-
chainId: chain.id,
|
|
295
|
-
query: { enabled: !!displayName }
|
|
296
|
-
});
|
|
333
|
+
const ensAvatar = useSafeAvatar(displayName ? safeNormalize(displayName) : void 0, chain.id);
|
|
297
334
|
const {
|
|
298
335
|
claims,
|
|
299
336
|
totalClaims,
|
|
300
337
|
activeClaims,
|
|
301
338
|
currentFingerprint,
|
|
302
|
-
isLoading: claimsLoading
|
|
339
|
+
isLoading: claimsLoading,
|
|
340
|
+
error: claimsError,
|
|
341
|
+
refetch: refetchClaims
|
|
303
342
|
} = useAttestations(address);
|
|
304
343
|
const currentClaim = [...claims].reverse().find(
|
|
305
344
|
(c) => !c.revoked && c.verification?.verified && c.fingerprint === currentFingerprint
|
|
@@ -310,10 +349,28 @@ function useThurinIdentity(ensOrAddress) {
|
|
|
310
349
|
isLoading: proofsLoading
|
|
311
350
|
} = usePGPProofs(currentFingerprint, currentClaim?.pgpPublicKey ?? null);
|
|
312
351
|
const { efp, isLoading: efpLoading } = useEFPGraph(address);
|
|
352
|
+
const lookup = { ensEmpty: !!ensInput && ensFetched && !resolvedAddress, ensFailed: !!ensError, claimsFailed: !!claimsError };
|
|
353
|
+
const probeNeeded = needsRpcProbe(lookup);
|
|
354
|
+
const client = usePublicClient({ chainId: chain.id });
|
|
355
|
+
const probe = useQuery5({
|
|
356
|
+
queryKey: ["thurin-rpc-probe", chain.id, ensOrAddress],
|
|
357
|
+
queryFn: () => client.getBlockNumber(),
|
|
358
|
+
enabled: probeNeeded && !!client,
|
|
359
|
+
retry: 0,
|
|
360
|
+
staleTime: 0
|
|
361
|
+
});
|
|
362
|
+
const rpcAnswered = !probeNeeded || probe.isFetching ? void 0 : probe.isSuccess ? true : probe.isError ? false : void 0;
|
|
363
|
+
const errorKind = identityErrorKind({ ...lookup, rpcAnswered });
|
|
364
|
+
const probing = probeNeeded && rpcAnswered === void 0;
|
|
365
|
+
const retry = () => {
|
|
366
|
+
if (ensInput) refetchEns();
|
|
367
|
+
refetchClaims();
|
|
368
|
+
if (probeNeeded) probe.refetch();
|
|
369
|
+
};
|
|
313
370
|
return {
|
|
314
371
|
address,
|
|
315
372
|
ensName: displayName,
|
|
316
|
-
ensAvatar
|
|
373
|
+
ensAvatar,
|
|
317
374
|
claims,
|
|
318
375
|
totalClaims,
|
|
319
376
|
activeClaims,
|
|
@@ -321,8 +378,12 @@ function useThurinIdentity(ensOrAddress) {
|
|
|
321
378
|
pgpKeyInfo,
|
|
322
379
|
proofs,
|
|
323
380
|
efp,
|
|
324
|
-
|
|
325
|
-
|
|
381
|
+
// "Not finished" counts as loading, including queries paused in a background tab: the card
|
|
382
|
+
// must not render defaults (zeros) for an identity it hasn't looked up yet.
|
|
383
|
+
isLoading: !!ensInput && !ensFetched || claimsLoading || proofsLoading || efpLoading || probing,
|
|
384
|
+
error: errorKind ? new Error(IDENTITY_ERROR_TEXT[errorKind]) : null,
|
|
385
|
+
errorKind,
|
|
386
|
+
retry
|
|
326
387
|
};
|
|
327
388
|
}
|
|
328
389
|
|
|
@@ -346,6 +407,19 @@ function ThurinCard({ ens, address, theme = "thurin" }) {
|
|
|
346
407
|
return /* @__PURE__ */ jsx2("div", { className: "thurin-card", "data-thurin-theme": theme, children: /* @__PURE__ */ jsx2("div", { className: "thurin-card-loading", children: "Loading identity..." }) });
|
|
347
408
|
}
|
|
348
409
|
const displayAddress = identity.address || null;
|
|
410
|
+
if (identity.errorKind) {
|
|
411
|
+
return /* @__PURE__ */ jsxs("div", { className: "thurin-card", "data-thurin-theme": theme, children: [
|
|
412
|
+
/* @__PURE__ */ jsxs("div", { className: "thurin-card-header", children: [
|
|
413
|
+
/* @__PURE__ */ jsx2(ThurinLogo, {}),
|
|
414
|
+
/* @__PURE__ */ jsx2("div", { children: /* @__PURE__ */ jsx2("div", { className: "thurin-card-name", children: identity.ensName || ens || address }) })
|
|
415
|
+
] }),
|
|
416
|
+
/* @__PURE__ */ jsxs("div", { className: "thurin-card-error", role: "status", children: [
|
|
417
|
+
identity.error?.message,
|
|
418
|
+
identity.errorKind !== "not-found" && /* @__PURE__ */ jsx2("button", { type: "button", className: "thurin-card-retry", onClick: identity.retry, children: "Try again" })
|
|
419
|
+
] }),
|
|
420
|
+
profileUrl && identity.errorKind !== "not-found" && /* @__PURE__ */ jsx2("a", { className: "thurin-card-link", href: profileUrl, target: "_blank", rel: "noopener noreferrer", children: "View on Thurin" })
|
|
421
|
+
] });
|
|
422
|
+
}
|
|
349
423
|
const verifiedProofs = identity.proofs.filter((p) => p.status === "verified").length;
|
|
350
424
|
const hasVerifiedPgp = identity.claims.some((c) => c.verification?.verified && !c.revoked);
|
|
351
425
|
return /* @__PURE__ */ jsxs("div", { className: "thurin-card", "data-thurin-theme": theme, children: [
|
|
@@ -373,7 +447,7 @@ function ThurinCard({ ens, address, theme = "thurin" }) {
|
|
|
373
447
|
/* @__PURE__ */ jsx2("span", { className: "thurin-card-stat-label", children: "Proofs" })
|
|
374
448
|
] }),
|
|
375
449
|
/* @__PURE__ */ jsxs("div", { className: "thurin-card-stat", children: [
|
|
376
|
-
/* @__PURE__ */ jsx2("span", { className: "thurin-card-stat-value", children: identity.efp
|
|
450
|
+
/* @__PURE__ */ jsx2("span", { className: "thurin-card-stat-value", children: identity.efp ? identity.efp.followers : "\u2013" }),
|
|
377
451
|
/* @__PURE__ */ jsx2("span", { className: "thurin-card-stat-label", children: "Followers" })
|
|
378
452
|
] })
|
|
379
453
|
] }),
|
|
@@ -403,7 +477,7 @@ function ThurinCard({ ens, address, theme = "thurin" }) {
|
|
|
403
477
|
}
|
|
404
478
|
|
|
405
479
|
// src/hooks/useEnsHint.ts
|
|
406
|
-
import { useEnsText } from "wagmi";
|
|
480
|
+
import { useEnsText as useEnsText2 } from "wagmi";
|
|
407
481
|
import { normalize as normalize2 } from "viem/ens";
|
|
408
482
|
function safeNormalize2(name) {
|
|
409
483
|
try {
|
|
@@ -415,7 +489,7 @@ function safeNormalize2(name) {
|
|
|
415
489
|
function useEnsHint(name, verifiedFingerprint) {
|
|
416
490
|
const chain = chainFor(useIdentityKitConfig().network);
|
|
417
491
|
const ensName = name ? safeNormalize2(name) : void 0;
|
|
418
|
-
const { data, isLoading, refetch } =
|
|
492
|
+
const { data, isLoading, refetch } = useEnsText2({
|
|
419
493
|
name: ensName,
|
|
420
494
|
key: ENS_HINT_KEY,
|
|
421
495
|
chainId: chain.id,
|
|
@@ -428,7 +502,7 @@ function useEnsHint(name, verifiedFingerprint) {
|
|
|
428
502
|
|
|
429
503
|
// src/hooks/useRecords.ts
|
|
430
504
|
import { useReadContracts as useReadContracts2 } from "wagmi";
|
|
431
|
-
import { useQuery as
|
|
505
|
+
import { useQuery as useQuery6 } from "@tanstack/react-query";
|
|
432
506
|
function useRecords(address, index, kinds = IDENTITY_KINDS, armoredKey) {
|
|
433
507
|
const config = useIdentityKitConfig();
|
|
434
508
|
const registry = getRegistry(config.network, config.registryAddress);
|
|
@@ -438,7 +512,7 @@ function useRecords(address, index, kinds = IDENTITY_KINDS, armoredKey) {
|
|
|
438
512
|
const contracts = enabled ? kinds.map((kind) => ({ address: registry.address, abi: REGISTRY_ABI, functionName: "record", args: [owner, BigInt(index), recordKind(kind)], chainId: chain.id })) : [];
|
|
439
513
|
const { data: raw, isLoading: readLoading, refetch } = useReadContracts2({ contracts, query: { enabled: contracts.length > 0 } });
|
|
440
514
|
const texts = (raw ?? []).map((r) => r.status === "success" ? decodeRecord(r.result) : "");
|
|
441
|
-
const { data: records, isLoading: parseLoading } =
|
|
515
|
+
const { data: records, isLoading: parseLoading } = useQuery6({
|
|
442
516
|
queryKey: ["records", config.network, registry.address, address, index, kinds.join(","), texts.join(" "), armoredKey ?? ""],
|
|
443
517
|
queryFn: async () => {
|
|
444
518
|
const out = [];
|
|
@@ -452,22 +526,27 @@ function useRecords(address, index, kinds = IDENTITY_KINDS, armoredKey) {
|
|
|
452
526
|
} };
|
|
453
527
|
}
|
|
454
528
|
export {
|
|
529
|
+
ARWEAVE_GATEWAY,
|
|
455
530
|
AUTHORIZATION_TYPES,
|
|
456
531
|
EIP712_NAME,
|
|
457
532
|
EIP712_VERSION,
|
|
458
533
|
ENS_HINT_KEY,
|
|
459
534
|
ENS_TEXT_RESOLVER_ABI,
|
|
535
|
+
IDENTITY_ERROR_TEXT,
|
|
460
536
|
IDENTITY_KINDS,
|
|
537
|
+
IPFS_GATEWAY,
|
|
461
538
|
IdentityKitProvider,
|
|
462
539
|
KNOWN_KINDS,
|
|
463
540
|
MAX_RECORD_BYTES,
|
|
464
541
|
NETWORKS,
|
|
542
|
+
NFT_AVATAR_ABI,
|
|
465
543
|
REGISTRY_ABI,
|
|
466
544
|
REGISTRY_ADDRESS,
|
|
467
545
|
ThurinCard,
|
|
468
546
|
addPointer,
|
|
469
547
|
attestTypedData,
|
|
470
548
|
authorizationDigest,
|
|
549
|
+
avatarUrl,
|
|
471
550
|
bytesToFingerprint,
|
|
472
551
|
chainFor,
|
|
473
552
|
decodeRecord,
|
|
@@ -484,11 +563,15 @@ export {
|
|
|
484
563
|
getRegistry,
|
|
485
564
|
hasEmailUserID,
|
|
486
565
|
identifyProof,
|
|
566
|
+
identityErrorKind,
|
|
487
567
|
isNetworkName,
|
|
488
568
|
keyIdOf,
|
|
489
569
|
keyIdToBytes,
|
|
490
570
|
kindName,
|
|
571
|
+
needsRpcProbe,
|
|
572
|
+
nftAvatarImage,
|
|
491
573
|
normalizeFingerprint,
|
|
574
|
+
parseNftAvatar,
|
|
492
575
|
parsePgpKey,
|
|
493
576
|
parsePointer,
|
|
494
577
|
parseRecord,
|
|
@@ -507,6 +590,7 @@ export {
|
|
|
507
590
|
useEnsHint,
|
|
508
591
|
usePGPProofs,
|
|
509
592
|
useRecords,
|
|
593
|
+
useSafeAvatar,
|
|
510
594
|
useThurinIdentity,
|
|
511
595
|
verifyAttestation,
|
|
512
596
|
verifyClearsigned,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider.tsx","../src/context.ts","../src/hooks/useThurinIdentity.ts","../src/hooks/useAttestations.ts","../src/hooks/useEFPGraph.ts","../src/hooks/usePGPProofs.ts","../src/components/ThurinCard/ThurinCard.tsx","../src/hooks/useEnsHint.ts","../src/hooks/useRecords.ts"],"sourcesContent":["import { type ReactNode, useMemo } from 'react'\nimport { WagmiProvider, createConfig, http, useConfig } from 'wagmi'\nimport { mainnet, sepolia, foundry } from 'wagmi/chains'\nimport { getRegistry } from './core/contract'\nimport type { NetworkName } from './core/contract'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { IdentityKitContext } from './context'\nimport type { IdentityKitConfig } from './core/types'\n\nconst defaultQueryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60_000,\n retry: 1,\n },\n },\n})\n\nexport { chainFor } from './core/contract'\n\nfunction createDefaultWagmiConfig(rpcUrl: string | undefined, network: NetworkName) {\n const url = rpcUrl || getRegistry(network).defaultRpcUrl\n // Spelled out per network: wagmi types `transports` by the exact chain ids.\n if (network === 'sepolia') return createConfig({ chains: [sepolia], transports: { [sepolia.id]: http(url) } })\n if (network === 'local') return createConfig({ chains: [foundry], transports: { [foundry.id]: http(url) } })\n return createConfig({ chains: [mainnet], transports: { [mainnet.id]: http(url) } })\n}\n\nfunction WagmiDetector({ children, rpcUrl, network }: { children: ReactNode; rpcUrl?: string; network: NetworkName }) {\n // Try to use existing wagmi config\n let hasWagmi = false\n try {\n useConfig()\n hasWagmi = true\n } catch {\n // No WagmiProvider above us\n }\n\n const wagmiConfig = useMemo(() => createDefaultWagmiConfig(rpcUrl, network), [rpcUrl, network])\n\n if (hasWagmi) {\n return <>{children}</>\n }\n\n return (\n <WagmiProvider config={wagmiConfig}>\n <QueryClientProvider client={defaultQueryClient}>\n {children}\n </QueryClientProvider>\n </WagmiProvider>\n )\n}\n\nexport interface IdentityKitProviderProps extends IdentityKitConfig {\n children: ReactNode\n}\n\nexport function IdentityKitProvider({\n children,\n rpcUrl,\n neynarApiKey,\n baseUrl = 'https://thurin.id',\n network = 'mainnet',\n registryAddress,\n}: IdentityKitProviderProps) {\n const config = useMemo(\n () => ({ rpcUrl, neynarApiKey, baseUrl, network, registryAddress }),\n [rpcUrl, neynarApiKey, baseUrl, network, registryAddress],\n )\n\n return (\n <IdentityKitContext.Provider value={config}>\n <WagmiDetector rpcUrl={rpcUrl} network={network}>\n {children}\n </WagmiDetector>\n </IdentityKitContext.Provider>\n )\n}\n","import { createContext, useContext } from 'react'\nimport type { IdentityKitConfig } from './core/types'\n\nconst defaultConfig: IdentityKitConfig = {\n baseUrl: 'https://thurin.id',\n network: 'mainnet',\n}\n\nexport const IdentityKitContext = createContext<IdentityKitConfig>(defaultConfig)\n\nexport function useIdentityKitConfig(): IdentityKitConfig {\n return useContext(IdentityKitContext)\n}\n","import { useEnsAddress, useEnsName, useEnsAvatar } from 'wagmi'\nimport { useIdentityKitConfig } from '../context'\nimport { chainFor } from '../provider'\nimport { normalize } from 'viem/ens'\nimport { useAttestations } from './useAttestations'\nimport { useEFPGraph } from './useEFPGraph'\nimport { usePGPProofs } from './usePGPProofs'\nimport type { ThurinIdentity } from '../core/types'\n\nfunction isAddress(value: string): boolean {\n return /^0x[0-9a-fA-F]{40}$/.test(value)\n}\n\nfunction safeNormalize(name: string): string | undefined {\n try {\n return normalize(name)\n } catch {\n return undefined\n }\n}\n\nexport function useThurinIdentity(ensOrAddress: string | undefined | null): ThurinIdentity {\n const chain = chainFor(useIdentityKitConfig().network)\n const isAddr = ensOrAddress ? isAddress(ensOrAddress) : false\n const ensInput = ensOrAddress && !isAddr ? safeNormalize(ensOrAddress) : undefined\n\n // Resolve ENS → address\n const { data: resolvedAddress } = useEnsAddress({\n name: ensInput,\n chainId: chain.id,\n query: { enabled: !!ensInput },\n })\n\n const address = isAddr ? ensOrAddress! : resolvedAddress ?? null\n\n // Reverse resolve address → ENS\n const { data: ensName } = useEnsName({\n address: address as `0x${string}` | undefined,\n chainId: chain.id,\n query: { enabled: !!address },\n })\n\n const displayName = ensInput || ensName || null\n\n const { data: ensAvatar } = useEnsAvatar({\n name: displayName ? safeNormalize(displayName) : undefined,\n chainId: chain.id,\n query: { enabled: !!displayName },\n })\n\n // On-chain attestations\n const {\n claims,\n totalClaims,\n activeClaims,\n currentFingerprint,\n isLoading: claimsLoading,\n } = useAttestations(address)\n\n // PGP proofs come from the key stored in the current attestation — the\n // latest claim that is active and whose signature verified (the same rule\n // useAttestations uses to pick currentFingerprint).\n const currentClaim = [...claims].reverse().find(\n (c) => !c.revoked && c.verification?.verified && c.fingerprint === currentFingerprint,\n )\n const {\n keyInfo: pgpKeyInfo,\n proofs,\n isLoading: proofsLoading,\n } = usePGPProofs(currentFingerprint, currentClaim?.pgpPublicKey ?? null)\n\n // EFP social graph\n const { efp, isLoading: efpLoading } = useEFPGraph(address)\n\n return {\n address,\n ensName: displayName,\n ensAvatar: ensAvatar ?? null,\n claims,\n totalClaims,\n activeClaims,\n currentFingerprint,\n pgpKeyInfo,\n proofs,\n efp,\n isLoading: claimsLoading || proofsLoading || efpLoading,\n error: null,\n }\n}\n","import { useReadContract, useReadContracts } from 'wagmi'\nimport { useQuery } from '@tanstack/react-query'\nimport { hexToString } from 'viem'\nimport { REGISTRY_ABI, getRegistry } from '../core/contract'\nimport { bytesToFingerprint } from '../core/fingerprint'\nimport { chainFor } from '../provider'\nimport { verifyAttestation } from '../core/pgp'\nimport { useIdentityKitConfig } from '../context'\nimport type { Attestation } from '../core/types'\n\n/**\n * On-chain attestations for an address, read straight from the v2 registry:\n * `attestationsOf` for the history, `getPayload` for each stored signature + key.\n * No event logs, so any RPC works. Each claim is verified off-chain.\n */\nexport function useAttestations(address: string | undefined | null) {\n const config = useIdentityKitConfig()\n const registry = getRegistry(config.network, config.registryAddress)\n const chain = chainFor(config.network)\n const owner = address as `0x${string}` | undefined\n\n const { data: rows, isLoading: rowsLoading } = useReadContract({\n address: registry.address,\n abi: REGISTRY_ABI,\n functionName: 'attestationsOf',\n args: owner ? [owner] : undefined,\n chainId: chain.id,\n query: { enabled: !!owner },\n })\n\n const count = rows?.length ?? 0\n\n const contracts = owner && rows\n ? rows.map((_, i) => ({\n address: registry.address,\n abi: REGISTRY_ABI,\n functionName: 'getPayload' as const,\n args: [owner, BigInt(i)] as const,\n chainId: chain.id,\n }))\n : []\n\n const { data: payloads, isLoading: payloadsLoading } = useReadContracts({\n contracts,\n query: { enabled: contracts.length > 0 },\n })\n\n const payloadsReady = count === 0 || (payloads !== undefined && payloads.length === count)\n\n const { data: claims, isLoading: verifyLoading } = useQuery({\n queryKey: ['attestations', config.network, registry.address, address, count, payloadsReady],\n queryFn: async (): Promise<Attestation[]> => {\n if (!rows || !address) return []\n const results: Attestation[] = []\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i]\n const payload = payloads?.[i]\n let pgpSignature: string | null = null\n let pgpPublicKey: string | null = null\n if (payload && payload.status === 'success') {\n const [sigHex, keyHex] = payload.result as readonly [`0x${string}`, `0x${string}`]\n pgpSignature = hexToString(sigHex)\n pgpPublicKey = hexToString(keyHex)\n }\n\n const fingerprint = bytesToFingerprint(row.fingerprint)\n const verification = pgpPublicKey && pgpSignature\n ? await verifyAttestation({ pgpPublicKey, pgpSignature, fingerprint, ethAddress: address })\n : null\n\n const revokedAt = Number(row.revokedAt)\n results.push({\n index: i,\n fingerprint,\n createdAt: Number(row.createdAt),\n revoked: revokedAt !== 0,\n revokedAt: revokedAt === 0 ? null : revokedAt,\n messageVersion: Number(row.messageVersion),\n pgpSignature,\n pgpPublicKey,\n verification,\n })\n }\n return results\n },\n enabled: !!rows && payloadsReady,\n staleTime: 300_000,\n })\n\n const isLoading = rowsLoading || payloadsLoading || verifyLoading || (!!owner && rows !== undefined && !payloadsReady)\n\n const activeClaims = (claims || []).filter((c) => !c.revoked)\n // The current identity is the latest claim that is both active and has a\n // verified signature binding the key to this address. A claim whose signature\n // has not verified must not surface the key's proofs or user data.\n const verifiedClaims = activeClaims.filter((c) => c.verification?.verified)\n const currentFingerprint = verifiedClaims.length > 0\n ? verifiedClaims[verifiedClaims.length - 1].fingerprint\n : null\n\n return {\n claims: claims || [],\n totalClaims: count,\n activeClaims: activeClaims.length,\n currentFingerprint,\n isLoading,\n }\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { fetchEFPGraph } from '../core/efp'\nimport type { EFPGraph } from '../core/types'\n\nexport function useEFPGraph(address: string | undefined | null) {\n const { data, isLoading } = useQuery<EFPGraph | null>({\n queryKey: ['efp-graph', address],\n queryFn: () => fetchEFPGraph(address!),\n enabled: !!address,\n staleTime: 300_000,\n })\n\n return {\n efp: data ?? null,\n isLoading,\n }\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { parsePgpKey } from '../core/pgp'\nimport { identifyProof, verifyProof, displayUrl, proofHref, proofSecondaryHref } from '../core/proofs'\nimport { useIdentityKitConfig } from '../context'\nimport type { PGPKeyInfo, ProofResult } from '../core/types'\n\n/**\n * Proofs for an identity, read from the PGP key stored in its on-chain\n * attestation. No keyserver is consulted: keys.openpgp.org only serves user\n * IDs whose email is verified and drops non-email user IDs entirely, so it can\n * never carry the published (non-email) identity that holds the notations.\n *\n * @param fingerprint the attested fingerprint (proofs must name it)\n * @param armoredKey the attestation's `pgpPublicKey`\n */\nexport function usePGPProofs(fingerprint: string | undefined | null, armoredKey: string | undefined | null) {\n const config = useIdentityKitConfig()\n\n const { data } = useQuery({\n // The key is large; key the query on its length + fingerprint, which\n // changes whenever a different attestation's key is supplied.\n queryKey: ['pgp-proofs', fingerprint, armoredKey?.length ?? 0],\n queryFn: async (): Promise<{ keyInfo: PGPKeyInfo | null; proofs: ProofResult[] }> => {\n if (!fingerprint || !armoredKey) return { keyInfo: null, proofs: [] }\n\n const keyInfo = await parsePgpKey(armoredKey)\n if (!keyInfo) return { keyInfo: null, proofs: [] }\n\n // Identify proofs from notations. Drop unrecognized targets (provider\n // 'unknown') so the card doesn't render \"Unknown\" badges for platforms\n // Thurin has no verifier for — matching the explorer's behavior.\n const identified = keyInfo.notations\n .map((n) => identifyProof(n))\n .filter((p): p is NonNullable<typeof p> => p !== null && p.provider !== 'unknown')\n\n // Verify all proofs in parallel\n const proofs: ProofResult[] = await Promise.all(\n identified.map(async (proof) => {\n const result = await verifyProof(proof, fingerprint, config.neynarApiKey)\n return {\n provider: proof.provider,\n label: proof.label,\n url: proof.url,\n displayUrl: displayUrl(proof),\n href: proofHref(proof),\n secondaryHref: proofSecondaryHref(proof),\n status: result.verified ? 'verified' as const : 'unverified' as const,\n reason: result.reason,\n }\n }),\n )\n\n // Collapse proofs that resolve to the same account — the same target is\n // often listed under both proof@thurin.id and proof@ariadne.id (and can\n // be reached via more than one proof URL). Key by provider + display\n // identity, preferring a verified result.\n const byAccount = new Map<string, ProofResult>()\n for (const p of proofs) {\n const key = `${p.provider}:${p.displayUrl}`\n const prev = byAccount.get(key)\n if (!prev || (p.status === 'verified' && prev.status !== 'verified')) {\n byAccount.set(key, p)\n }\n }\n\n return { keyInfo, proofs: [...byAccount.values()] }\n },\n enabled: !!fingerprint && !!armoredKey,\n staleTime: 300_000,\n })\n\n return {\n keyInfo: data?.keyInfo ?? null,\n proofs: data?.proofs ?? [],\n // Report loading whenever a fingerprint is set but its proofs haven't\n // resolved yet. Using react-query's own isLoading leaves a gap while the\n // query is enabling (fingerprint just became known), during which the card\n // would briefly paint without proof badges and then pop them in.\n isLoading: !!fingerprint && !!armoredKey && data === undefined,\n }\n}\n","import { useThurinIdentity } from '../../hooks/useThurinIdentity'\nimport { useIdentityKitConfig } from '../../context'\nimport type { Theme } from '../../core/types'\nimport '../../themes/index.css'\nimport './ThurinCard.css'\n\nfunction ThurinLogo() {\n return (\n <svg className=\"thurin-card-avatar-placeholder\" viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"50\" cy=\"50\" r=\"50\" fill=\"var(--thurin-surface-deep)\" />\n <path d=\"M25 80 Q25 25 50 25 Q75 25 75 50\" fill=\"none\" stroke=\"#7c9a3e\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n <path d=\"M33 75 Q33 35 50 35 Q67 35 67 52\" fill=\"none\" stroke=\"#7c9a3e\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n <path d=\"M41 70 Q41 45 50 45 Q59 45 59 55\" fill=\"none\" stroke=\"#c9a227\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n <path d=\"M50 65 L50 53\" fill=\"none\" stroke=\"#c9a227\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n </svg>\n )\n}\n\nexport interface ThurinCardProps {\n ens?: string\n address?: string\n theme?: Theme\n}\n\nexport function ThurinCard({ ens, address, theme = 'thurin' }: ThurinCardProps) {\n const input = ens || address\n const identity = useThurinIdentity(input)\n const config = useIdentityKitConfig()\n\n const profileUrl = identity.address\n ? `${config.baseUrl}/eth/${identity.address}`\n : ens\n ? `${config.baseUrl}/ens/${ens}`\n : null\n\n if (identity.isLoading) {\n return (\n <div className=\"thurin-card\" data-thurin-theme={theme}>\n <div className=\"thurin-card-loading\">Loading identity...</div>\n </div>\n )\n }\n\n const displayAddress = identity.address || null\n\n const verifiedProofs = identity.proofs.filter((p) => p.status === 'verified').length\n const hasVerifiedPgp = identity.claims.some((c) => c.verification?.verified && !c.revoked)\n\n return (\n <div className=\"thurin-card\" data-thurin-theme={theme}>\n <a className=\"thurin-card-header\" href={profileUrl || undefined} target=\"_blank\" rel=\"noopener noreferrer\">\n {identity.ensAvatar ? (\n <img\n className=\"thurin-card-avatar\"\n src={identity.ensAvatar}\n alt={identity.ensName || ''}\n />\n ) : (\n <ThurinLogo />\n )}\n <div>\n {identity.ensName && (\n <div className=\"thurin-card-name\">{identity.ensName}</div>\n )}\n {displayAddress && (\n <div className=\"thurin-card-address\">{displayAddress}</div>\n )}\n </div>\n </a>\n\n <div className=\"thurin-card-stats\">\n <div className=\"thurin-card-stat\">\n <span className=\"thurin-card-stat-value\">{identity.activeClaims}</span>\n <span className=\"thurin-card-stat-label\">Attestations</span>\n </div>\n <div className=\"thurin-card-stat\">\n <span className=\"thurin-card-stat-value\">{verifiedProofs}</span>\n <span className=\"thurin-card-stat-label\">Proofs</span>\n </div>\n <div className=\"thurin-card-stat\">\n <span className=\"thurin-card-stat-value\">\n {identity.efp?.followers ?? 0}\n </span>\n <span className=\"thurin-card-stat-label\">Followers</span>\n </div>\n </div>\n\n <div className=\"thurin-card-badges\">\n {hasVerifiedPgp && (\n <span className=\"thurin-card-badge thurin-card-badge--verified\">PGP Verified</span>\n )}\n {identity.efp?.hasEfp && (\n <span className=\"thurin-card-badge thurin-card-badge--verified\">EFP</span>\n )}\n {identity.proofs.map((p, i) => (\n <span\n key={i}\n className={`thurin-card-badge thurin-card-badge--${p.status === 'verified' ? 'verified' : 'unverified'}`}\n >\n {p.label}\n </span>\n ))}\n </div>\n\n {profileUrl && (\n <a\n className=\"thurin-card-link\"\n href={profileUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n View on Thurin\n </a>\n )}\n </div>\n )\n}\n","import { useEnsText } from 'wagmi'\nimport { normalize } from 'viem/ens'\nimport { useIdentityKitConfig } from '../context'\nimport { chainFor } from '../provider'\nimport { ENS_HINT_KEY, ensHintFor, type EnsHint } from '../core/ensHint'\n\nfunction safeNormalize(name: string): string | undefined {\n try { return normalize(name) } catch { return undefined }\n}\n\n/**\n * A name's `id.thurin` record against the fingerprint the registry verifies for its\n * address. `state` is `match`, `unset`, or `mismatch`; the last one is the signal worth\n * showing — a name advertising a key its address has not claimed.\n */\nexport function useEnsHint(name: string | undefined | null, verifiedFingerprint: string | null | undefined): EnsHint & { isLoading: boolean; refetch: () => void } {\n const chain = chainFor(useIdentityKitConfig().network)\n const ensName = name ? safeNormalize(name) : undefined\n const { data, isLoading, refetch } = useEnsText({\n name: ensName,\n key: ENS_HINT_KEY,\n chainId: chain.id,\n query: { enabled: !!ensName },\n })\n return { ...ensHintFor(data ?? null, verifiedFingerprint), isLoading: !!ensName && isLoading, refetch: () => { void refetch() } }\n}\n","import { useReadContracts } from 'wagmi'\nimport { useQuery } from '@tanstack/react-query'\nimport { REGISTRY_ABI, getRegistry } from '../core/contract'\nimport { chainFor } from '../provider'\nimport { useIdentityKitConfig } from '../context'\nimport { IDENTITY_KINDS, decodeRecord, parseRecord, recordKind, type ParsedRecord } from '../core/records'\n\n/**\n * The records on one claim, for the kinds given (default: the kinds an identity page shows),\n * read in one multicall and parsed. Kinds with no record are left out.\n */\nexport function useRecords(address: string | undefined | null, index: number | undefined | null, kinds: readonly string[] = IDENTITY_KINDS, armoredKey?: string | null) {\n const config = useIdentityKitConfig()\n const registry = getRegistry(config.network, config.registryAddress)\n const chain = chainFor(config.network)\n const owner = address as `0x${string}` | undefined\n const enabled = !!owner && index !== undefined && index !== null\n\n const contracts = enabled\n ? kinds.map(kind => ({ address: registry.address, abi: REGISTRY_ABI, functionName: 'record' as const, args: [owner!, BigInt(index!), recordKind(kind)] as const, chainId: chain.id }))\n : []\n const { data: raw, isLoading: readLoading, refetch } = useReadContracts({ contracts, query: { enabled: contracts.length > 0 } })\n\n const texts = (raw ?? []).map(r => (r.status === 'success' ? decodeRecord(r.result as `0x${string}`) : ''))\n const { data: records, isLoading: parseLoading } = useQuery({\n queryKey: ['records', config.network, registry.address, address, index, kinds.join(','), texts.join(' '), armoredKey ?? ''],\n queryFn: async (): Promise<ParsedRecord[]> => {\n const out: ParsedRecord[] = []\n for (let i = 0; i < kinds.length; i++) if (texts[i]) out.push(await parseRecord(kinds[i], texts[i], { armoredKey: armoredKey ?? undefined }))\n return out\n },\n enabled: enabled && raw !== undefined,\n })\n\n return { records: records ?? [], isLoading: enabled && (readLoading || parseLoading), refetch: () => { void refetch() } }\n}\n"],"mappings":"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","names":["useQuery","useQuery","useQuery","useQuery","jsx","normalize","safeNormalize","normalize","useReadContracts","useQuery","useReadContracts","useQuery"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx","../src/context.ts","../src/hooks/useThurinIdentity.ts","../src/hooks/useAttestations.ts","../src/hooks/useEFPGraph.ts","../src/hooks/usePGPProofs.ts","../src/hooks/useSafeAvatar.ts","../src/components/ThurinCard/ThurinCard.tsx","../src/hooks/useEnsHint.ts","../src/hooks/useRecords.ts"],"sourcesContent":["import { type ReactNode, useMemo } from 'react'\nimport { WagmiProvider, createConfig, http, useConfig } from 'wagmi'\nimport { mainnet, sepolia, foundry } from 'wagmi/chains'\nimport { getRegistry } from './core/contract'\nimport type { NetworkName } from './core/contract'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { IdentityKitContext } from './context'\nimport type { IdentityKitConfig } from './core/types'\n\nconst defaultQueryClient = new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 60_000,\n retry: 1,\n },\n },\n})\n\nexport { chainFor } from './core/contract'\n\nfunction createDefaultWagmiConfig(rpcUrl: string | undefined, network: NetworkName) {\n const url = rpcUrl || getRegistry(network).defaultRpcUrl\n // Spelled out per network: wagmi types `transports` by the exact chain ids.\n if (network === 'sepolia') return createConfig({ chains: [sepolia], transports: { [sepolia.id]: http(url) } })\n if (network === 'local') return createConfig({ chains: [foundry], transports: { [foundry.id]: http(url) } })\n return createConfig({ chains: [mainnet], transports: { [mainnet.id]: http(url) } })\n}\n\nfunction WagmiDetector({ children, rpcUrl, network }: { children: ReactNode; rpcUrl?: string; network: NetworkName }) {\n // Try to use existing wagmi config\n let hasWagmi = false\n try {\n useConfig()\n hasWagmi = true\n } catch {\n // No WagmiProvider above us\n }\n\n const wagmiConfig = useMemo(() => createDefaultWagmiConfig(rpcUrl, network), [rpcUrl, network])\n\n if (hasWagmi) {\n return <>{children}</>\n }\n\n return (\n <WagmiProvider config={wagmiConfig}>\n <QueryClientProvider client={defaultQueryClient}>\n {children}\n </QueryClientProvider>\n </WagmiProvider>\n )\n}\n\nexport interface IdentityKitProviderProps extends IdentityKitConfig {\n children: ReactNode\n}\n\nexport function IdentityKitProvider({\n children,\n rpcUrl,\n neynarApiKey,\n baseUrl = 'https://thurin.id',\n network = 'mainnet',\n registryAddress,\n}: IdentityKitProviderProps) {\n const config = useMemo(\n () => ({ rpcUrl, neynarApiKey, baseUrl, network, registryAddress }),\n [rpcUrl, neynarApiKey, baseUrl, network, registryAddress],\n )\n\n return (\n <IdentityKitContext.Provider value={config}>\n <WagmiDetector rpcUrl={rpcUrl} network={network}>\n {children}\n </WagmiDetector>\n </IdentityKitContext.Provider>\n )\n}\n","import { createContext, useContext } from 'react'\nimport type { IdentityKitConfig } from './core/types'\n\nconst defaultConfig: IdentityKitConfig = {\n baseUrl: 'https://thurin.id',\n network: 'mainnet',\n}\n\nexport const IdentityKitContext = createContext<IdentityKitConfig>(defaultConfig)\n\nexport function useIdentityKitConfig(): IdentityKitConfig {\n return useContext(IdentityKitContext)\n}\n","import { useEnsAddress, useEnsName, usePublicClient } from 'wagmi'\nimport { useQuery } from '@tanstack/react-query'\nimport { identityErrorKind, needsRpcProbe, IDENTITY_ERROR_TEXT } from '../core/identityError'\nimport { useIdentityKitConfig } from '../context'\nimport { chainFor } from '../provider'\nimport { normalize } from 'viem/ens'\nimport { useAttestations } from './useAttestations'\nimport { useEFPGraph } from './useEFPGraph'\nimport { usePGPProofs } from './usePGPProofs'\nimport { useSafeAvatar } from './useSafeAvatar'\nimport type { ThurinIdentity } from '../core/types'\n\nfunction isAddress(value: string): boolean {\n return /^0x[0-9a-fA-F]{40}$/.test(value)\n}\n\nfunction safeNormalize(name: string): string | undefined {\n try {\n return normalize(name)\n } catch {\n return undefined\n }\n}\n\nexport function useThurinIdentity(ensOrAddress: string | undefined | null): ThurinIdentity {\n const chain = chainFor(useIdentityKitConfig().network)\n const isAddr = ensOrAddress ? isAddress(ensOrAddress) : false\n const ensInput = ensOrAddress && !isAddr ? safeNormalize(ensOrAddress) : undefined\n\n // Resolve ENS → address\n const { data: resolvedAddress, error: ensError, isFetched: ensFetched, refetch: refetchEns } = useEnsAddress({\n name: ensInput,\n chainId: chain.id,\n query: { enabled: !!ensInput },\n })\n\n const address = isAddr ? ensOrAddress! : resolvedAddress ?? null\n\n // Reverse resolve address → ENS\n const { data: ensName } = useEnsName({\n address: address as `0x${string}` | undefined,\n chainId: chain.id,\n query: { enabled: !!address },\n })\n\n const displayName = ensInput || ensName || null\n\n const ensAvatar = useSafeAvatar(displayName ? safeNormalize(displayName) : undefined, chain.id)\n\n // On-chain attestations\n const {\n claims,\n totalClaims,\n activeClaims,\n currentFingerprint,\n isLoading: claimsLoading,\n error: claimsError,\n refetch: refetchClaims,\n } = useAttestations(address)\n\n // PGP proofs come from the key stored in the current attestation — the\n // latest claim that is active and whose signature verified (the same rule\n // useAttestations uses to pick currentFingerprint).\n const currentClaim = [...claims].reverse().find(\n (c) => !c.revoked && c.verification?.verified && c.fingerprint === currentFingerprint,\n )\n const {\n keyInfo: pgpKeyInfo,\n proofs,\n isLoading: proofsLoading,\n } = usePGPProofs(currentFingerprint, currentClaim?.pgpPublicKey ?? null)\n\n // EFP social graph\n const { efp, isLoading: efpLoading } = useEFPGraph(address)\n\n // An empty ENS result or a failed read might just be a dead RPC; ask it for the block\n // number before saying anything about the identity (core/identityError.ts).\n const lookup = { ensEmpty: !!ensInput && ensFetched && !resolvedAddress, ensFailed: !!ensError, claimsFailed: !!claimsError }\n const probeNeeded = needsRpcProbe(lookup)\n const client = usePublicClient({ chainId: chain.id })\n const probe = useQuery({\n queryKey: ['thurin-rpc-probe', chain.id, ensOrAddress],\n queryFn: () => client!.getBlockNumber(),\n enabled: probeNeeded && !!client,\n retry: 0,\n staleTime: 0,\n })\n const rpcAnswered = !probeNeeded || probe.isFetching ? undefined : probe.isSuccess ? true : probe.isError ? false : undefined\n const errorKind = identityErrorKind({ ...lookup, rpcAnswered })\n const probing = probeNeeded && rpcAnswered === undefined\n const retry = () => {\n if (ensInput) refetchEns()\n refetchClaims()\n if (probeNeeded) probe.refetch()\n }\n\n return {\n address,\n ensName: displayName,\n ensAvatar,\n claims,\n totalClaims,\n activeClaims,\n currentFingerprint,\n pgpKeyInfo,\n proofs,\n efp,\n // \"Not finished\" counts as loading, including queries paused in a background tab: the card\n // must not render defaults (zeros) for an identity it hasn't looked up yet.\n isLoading: (!!ensInput && !ensFetched) || claimsLoading || proofsLoading || efpLoading || probing,\n error: errorKind ? new Error(IDENTITY_ERROR_TEXT[errorKind]) : null,\n errorKind,\n retry,\n }\n}\n","import { useReadContract, useReadContracts } from 'wagmi'\nimport { useQuery } from '@tanstack/react-query'\nimport { hexToString } from 'viem'\nimport { REGISTRY_ABI, getRegistry } from '../core/contract'\nimport { bytesToFingerprint } from '../core/fingerprint'\nimport { chainFor } from '../provider'\nimport { verifyAttestation } from '../core/pgp'\nimport { useIdentityKitConfig } from '../context'\nimport type { Attestation } from '../core/types'\n\n/**\n * On-chain attestations for an address, read straight from the v2 registry:\n * `attestationsOf` for the history, `getPayload` for each stored signature + key.\n * No event logs, so any RPC works. Each claim is verified off-chain.\n */\nexport function useAttestations(address: string | undefined | null) {\n const config = useIdentityKitConfig()\n const registry = getRegistry(config.network, config.registryAddress)\n const chain = chainFor(config.network)\n const owner = address as `0x${string}` | undefined\n\n const { data: rows, isLoading: rowsLoading, isFetched: rowsFetched, error: rowsError, refetch: refetchRows } = useReadContract({\n address: registry.address,\n abi: REGISTRY_ABI,\n functionName: 'attestationsOf',\n args: owner ? [owner] : undefined,\n chainId: chain.id,\n query: { enabled: !!owner },\n })\n\n const count = rows?.length ?? 0\n\n const contracts = owner && rows\n ? rows.map((_, i) => ({\n address: registry.address,\n abi: REGISTRY_ABI,\n functionName: 'getPayload' as const,\n args: [owner, BigInt(i)] as const,\n chainId: chain.id,\n }))\n : []\n\n const { data: payloads, isLoading: payloadsLoading, error: payloadsError, refetch: refetchPayloads } = useReadContracts({\n contracts,\n query: { enabled: contracts.length > 0 },\n })\n\n const payloadsReady = count === 0 || (payloads !== undefined && payloads.length === count)\n\n const { data: claims, isLoading: verifyLoading } = useQuery({\n queryKey: ['attestations', config.network, registry.address, address, count, payloadsReady],\n queryFn: async (): Promise<Attestation[]> => {\n if (!rows || !address) return []\n const results: Attestation[] = []\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i]\n const payload = payloads?.[i]\n let pgpSignature: string | null = null\n let pgpPublicKey: string | null = null\n if (payload && payload.status === 'success') {\n const [sigHex, keyHex] = payload.result as readonly [`0x${string}`, `0x${string}`]\n pgpSignature = hexToString(sigHex)\n pgpPublicKey = hexToString(keyHex)\n }\n\n const fingerprint = bytesToFingerprint(row.fingerprint)\n const verification = pgpPublicKey && pgpSignature\n ? await verifyAttestation({ pgpPublicKey, pgpSignature, fingerprint, ethAddress: address })\n : null\n\n const revokedAt = Number(row.revokedAt)\n results.push({\n index: i,\n fingerprint,\n createdAt: Number(row.createdAt),\n revoked: revokedAt !== 0,\n revokedAt: revokedAt === 0 ? null : revokedAt,\n messageVersion: Number(row.messageVersion),\n pgpSignature,\n pgpPublicKey,\n verification,\n })\n }\n return results\n },\n enabled: !!rows && payloadsReady,\n staleTime: 300_000,\n })\n\n // Not fetched yet counts as loading (a paused query in a background tab is not \"no claims\").\n const isLoading = rowsLoading || (!!owner && !rowsFetched) || payloadsLoading || verifyLoading || (!!owner && rows !== undefined && !payloadsReady)\n\n const activeClaims = (claims || []).filter((c) => !c.revoked)\n // The current identity is the latest claim that is both active and has a\n // verified signature binding the key to this address. A claim whose signature\n // has not verified must not surface the key's proofs or user data.\n const verifiedClaims = activeClaims.filter((c) => c.verification?.verified)\n const currentFingerprint = verifiedClaims.length > 0\n ? verifiedClaims[verifiedClaims.length - 1].fingerprint\n : null\n\n return {\n claims: claims || [],\n totalClaims: count,\n activeClaims: activeClaims.length,\n currentFingerprint,\n isLoading,\n /** A registry read failed (the RPC may be down; useThurinIdentity finds out which). */\n error: (rowsError ?? payloadsError ?? null) as Error | null,\n refetch: () => { if (owner) { refetchRows(); if (contracts.length > 0) refetchPayloads() } },\n }\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { fetchEFPGraph } from '../core/efp'\nimport type { EFPGraph } from '../core/types'\n\nexport function useEFPGraph(address: string | undefined | null) {\n const { data, isLoading } = useQuery<EFPGraph | null>({\n queryKey: ['efp-graph', address],\n queryFn: () => fetchEFPGraph(address!),\n enabled: !!address,\n staleTime: 300_000,\n })\n\n return {\n efp: data ?? null,\n isLoading,\n }\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { parsePgpKey } from '../core/pgp'\nimport { identifyProof, verifyProof, displayUrl, proofHref, proofSecondaryHref } from '../core/proofs'\nimport { useIdentityKitConfig } from '../context'\nimport type { PGPKeyInfo, ProofResult } from '../core/types'\n\n/**\n * Proofs for an identity, read from the PGP key stored in its on-chain\n * attestation. No keyserver is consulted: keys.openpgp.org only serves user\n * IDs whose email is verified and drops non-email user IDs entirely, so it can\n * never carry the published (non-email) identity that holds the notations.\n *\n * @param fingerprint the attested fingerprint (proofs must name it)\n * @param armoredKey the attestation's `pgpPublicKey`\n */\nexport function usePGPProofs(fingerprint: string | undefined | null, armoredKey: string | undefined | null) {\n const config = useIdentityKitConfig()\n\n const { data } = useQuery({\n // The key is large; key the query on its length + fingerprint, which\n // changes whenever a different attestation's key is supplied.\n queryKey: ['pgp-proofs', fingerprint, armoredKey?.length ?? 0],\n queryFn: async (): Promise<{ keyInfo: PGPKeyInfo | null; proofs: ProofResult[] }> => {\n if (!fingerprint || !armoredKey) return { keyInfo: null, proofs: [] }\n\n const keyInfo = await parsePgpKey(armoredKey)\n if (!keyInfo) return { keyInfo: null, proofs: [] }\n\n // Identify proofs from notations. Drop unrecognized targets (provider\n // 'unknown') so the card doesn't render \"Unknown\" badges for platforms\n // Thurin has no verifier for — matching the explorer's behavior.\n const identified = keyInfo.notations\n .map((n) => identifyProof(n))\n .filter((p): p is NonNullable<typeof p> => p !== null && p.provider !== 'unknown')\n\n // Verify all proofs in parallel\n const proofs: ProofResult[] = await Promise.all(\n identified.map(async (proof) => {\n const result = await verifyProof(proof, fingerprint, config.neynarApiKey)\n return {\n provider: proof.provider,\n label: proof.label,\n url: proof.url,\n displayUrl: displayUrl(proof),\n href: proofHref(proof),\n secondaryHref: proofSecondaryHref(proof),\n status: result.verified ? 'verified' as const : 'unverified' as const,\n reason: result.reason,\n }\n }),\n )\n\n // Collapse proofs that resolve to the same account — the same target is\n // often listed under both proof@thurin.id and proof@ariadne.id (and can\n // be reached via more than one proof URL). Key by provider + display\n // identity, preferring a verified result.\n const byAccount = new Map<string, ProofResult>()\n for (const p of proofs) {\n const key = `${p.provider}:${p.displayUrl}`\n const prev = byAccount.get(key)\n if (!prev || (p.status === 'verified' && prev.status !== 'verified')) {\n byAccount.set(key, p)\n }\n }\n\n return { keyInfo, proofs: [...byAccount.values()] }\n },\n enabled: !!fingerprint && !!armoredKey,\n staleTime: 300_000,\n })\n\n return {\n keyInfo: data?.keyInfo ?? null,\n proofs: data?.proofs ?? [],\n // Report loading whenever a fingerprint is set but its proofs haven't\n // resolved yet. Using react-query's own isLoading leaves a gap while the\n // query is enabling (fingerprint just became known), during which the card\n // would briefly paint without proof badges and then pop them in.\n isLoading: !!fingerprint && !!armoredKey && data === undefined,\n }\n}\n","import { useEnsText, useReadContract } from 'wagmi'\nimport { useQuery } from '@tanstack/react-query'\nimport { avatarUrl, parseNftAvatar, nftAvatarImage, NFT_AVATAR_ABI } from '../core/avatar'\n\n/**\n * The avatar to show for a normalized ENS name, or null. Reads the raw `avatar` record and\n * loads only what `avatarUrl` allows (see core/avatar.ts); wagmi's useEnsAvatar would fetch\n * whatever host the name's owner chose.\n */\nexport function useSafeAvatar(name: string | undefined, chainId: number): string | null {\n const { data: raw } = useEnsText({ name, key: 'avatar', chainId, query: { enabled: !!name } })\n const direct = avatarUrl(raw)\n const nft = direct ? null : parseNftAvatar(raw)\n const { data: tokenUri } = useReadContract({\n address: nft?.contract,\n abi: NFT_AVATAR_ABI,\n functionName: nft?.standard === 'erc1155' ? 'uri' : 'tokenURI',\n args: nft ? [nft.tokenId] : undefined,\n chainId,\n query: { enabled: !!nft && nft.chainId === chainId },\n })\n const { data: image } = useQuery({\n queryKey: ['thurin-nft-avatar', tokenUri, nft?.tokenId.toString()],\n queryFn: () => nftAvatarImage(tokenUri as string, nft!.tokenId).catch(() => null),\n enabled: typeof tokenUri === 'string' && !!nft,\n })\n return direct ?? image ?? null\n}\n","import { useThurinIdentity } from '../../hooks/useThurinIdentity'\nimport { useIdentityKitConfig } from '../../context'\nimport type { Theme } from '../../core/types'\nimport '../../themes/index.css'\nimport './ThurinCard.css'\n\nfunction ThurinLogo() {\n return (\n <svg className=\"thurin-card-avatar-placeholder\" viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"50\" cy=\"50\" r=\"50\" fill=\"var(--thurin-surface-deep)\" />\n <path d=\"M25 80 Q25 25 50 25 Q75 25 75 50\" fill=\"none\" stroke=\"#7c9a3e\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n <path d=\"M33 75 Q33 35 50 35 Q67 35 67 52\" fill=\"none\" stroke=\"#7c9a3e\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n <path d=\"M41 70 Q41 45 50 45 Q59 45 59 55\" fill=\"none\" stroke=\"#c9a227\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n <path d=\"M50 65 L50 53\" fill=\"none\" stroke=\"#c9a227\" strokeWidth=\"4\" strokeLinecap=\"round\"/>\n </svg>\n )\n}\n\nexport interface ThurinCardProps {\n ens?: string\n address?: string\n theme?: Theme\n}\n\nexport function ThurinCard({ ens, address, theme = 'thurin' }: ThurinCardProps) {\n const input = ens || address\n const identity = useThurinIdentity(input)\n const config = useIdentityKitConfig()\n\n const profileUrl = identity.address\n ? `${config.baseUrl}/eth/${identity.address}`\n : ens\n ? `${config.baseUrl}/ens/${ens}`\n : null\n\n if (identity.isLoading) {\n return (\n <div className=\"thurin-card\" data-thurin-theme={theme}>\n <div className=\"thurin-card-loading\">Loading identity...</div>\n </div>\n )\n }\n\n const displayAddress = identity.address || null\n\n // A failed lookup says so, instead of rendering zeros that read as facts.\n if (identity.errorKind) {\n return (\n <div className=\"thurin-card\" data-thurin-theme={theme}>\n <div className=\"thurin-card-header\">\n <ThurinLogo />\n <div>\n <div className=\"thurin-card-name\">{identity.ensName || ens || address}</div>\n </div>\n </div>\n <div className=\"thurin-card-error\" role=\"status\">\n {identity.error?.message}\n {identity.errorKind !== 'not-found' && (\n <button type=\"button\" className=\"thurin-card-retry\" onClick={identity.retry}>Try again</button>\n )}\n </div>\n {profileUrl && identity.errorKind !== 'not-found' && (\n <a className=\"thurin-card-link\" href={profileUrl} target=\"_blank\" rel=\"noopener noreferrer\">View on Thurin</a>\n )}\n </div>\n )\n }\n\n const verifiedProofs = identity.proofs.filter((p) => p.status === 'verified').length\n const hasVerifiedPgp = identity.claims.some((c) => c.verification?.verified && !c.revoked)\n\n return (\n <div className=\"thurin-card\" data-thurin-theme={theme}>\n <a className=\"thurin-card-header\" href={profileUrl || undefined} target=\"_blank\" rel=\"noopener noreferrer\">\n {identity.ensAvatar ? (\n <img\n className=\"thurin-card-avatar\"\n src={identity.ensAvatar}\n alt={identity.ensName || ''}\n />\n ) : (\n <ThurinLogo />\n )}\n <div>\n {identity.ensName && (\n <div className=\"thurin-card-name\">{identity.ensName}</div>\n )}\n {displayAddress && (\n <div className=\"thurin-card-address\">{displayAddress}</div>\n )}\n </div>\n </a>\n\n <div className=\"thurin-card-stats\">\n <div className=\"thurin-card-stat\">\n <span className=\"thurin-card-stat-value\">{identity.activeClaims}</span>\n <span className=\"thurin-card-stat-label\">Attestations</span>\n </div>\n <div className=\"thurin-card-stat\">\n <span className=\"thurin-card-stat-value\">{verifiedProofs}</span>\n <span className=\"thurin-card-stat-label\">Proofs</span>\n </div>\n <div className=\"thurin-card-stat\">\n <span className=\"thurin-card-stat-value\">\n {/* EFP answers separately; a null graph means it didn't, so don't claim zero. */}\n {identity.efp ? identity.efp.followers : '–'}\n </span>\n <span className=\"thurin-card-stat-label\">Followers</span>\n </div>\n </div>\n\n <div className=\"thurin-card-badges\">\n {hasVerifiedPgp && (\n <span className=\"thurin-card-badge thurin-card-badge--verified\">PGP Verified</span>\n )}\n {identity.efp?.hasEfp && (\n <span className=\"thurin-card-badge thurin-card-badge--verified\">EFP</span>\n )}\n {identity.proofs.map((p, i) => (\n <span\n key={i}\n className={`thurin-card-badge thurin-card-badge--${p.status === 'verified' ? 'verified' : 'unverified'}`}\n >\n {p.label}\n </span>\n ))}\n </div>\n\n {profileUrl && (\n <a\n className=\"thurin-card-link\"\n href={profileUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n View on Thurin\n </a>\n )}\n </div>\n )\n}\n","import { useEnsText } from 'wagmi'\nimport { normalize } from 'viem/ens'\nimport { useIdentityKitConfig } from '../context'\nimport { chainFor } from '../provider'\nimport { ENS_HINT_KEY, ensHintFor, type EnsHint } from '../core/ensHint'\n\nfunction safeNormalize(name: string): string | undefined {\n try { return normalize(name) } catch { return undefined }\n}\n\n/**\n * A name's `id.thurin` record against the fingerprint the registry verifies for its\n * address. `state` is `match`, `unset`, or `mismatch`; the last one is the signal worth\n * showing — a name advertising a key its address has not claimed.\n */\nexport function useEnsHint(name: string | undefined | null, verifiedFingerprint: string | null | undefined): EnsHint & { isLoading: boolean; refetch: () => void } {\n const chain = chainFor(useIdentityKitConfig().network)\n const ensName = name ? safeNormalize(name) : undefined\n const { data, isLoading, refetch } = useEnsText({\n name: ensName,\n key: ENS_HINT_KEY,\n chainId: chain.id,\n query: { enabled: !!ensName },\n })\n return { ...ensHintFor(data ?? null, verifiedFingerprint), isLoading: !!ensName && isLoading, refetch: () => { void refetch() } }\n}\n","import { useReadContracts } from 'wagmi'\nimport { useQuery } from '@tanstack/react-query'\nimport { REGISTRY_ABI, getRegistry } from '../core/contract'\nimport { chainFor } from '../provider'\nimport { useIdentityKitConfig } from '../context'\nimport { IDENTITY_KINDS, decodeRecord, parseRecord, recordKind, type ParsedRecord } from '../core/records'\n\n/**\n * The records on one claim, for the kinds given (default: the kinds an identity page shows),\n * read in one multicall and parsed. Kinds with no record are left out.\n */\nexport function useRecords(address: string | undefined | null, index: number | undefined | null, kinds: readonly string[] = IDENTITY_KINDS, armoredKey?: string | null) {\n const config = useIdentityKitConfig()\n const registry = getRegistry(config.network, config.registryAddress)\n const chain = chainFor(config.network)\n const owner = address as `0x${string}` | undefined\n const enabled = !!owner && index !== undefined && index !== null\n\n const contracts = enabled\n ? kinds.map(kind => ({ address: registry.address, abi: REGISTRY_ABI, functionName: 'record' as const, args: [owner!, BigInt(index!), recordKind(kind)] as const, chainId: chain.id }))\n : []\n const { data: raw, isLoading: readLoading, refetch } = useReadContracts({ contracts, query: { enabled: contracts.length > 0 } })\n\n const texts = (raw ?? []).map(r => (r.status === 'success' ? decodeRecord(r.result as `0x${string}`) : ''))\n const { data: records, isLoading: parseLoading } = useQuery({\n queryKey: ['records', config.network, registry.address, address, index, kinds.join(','), texts.join(' '), armoredKey ?? ''],\n queryFn: async (): Promise<ParsedRecord[]> => {\n const out: ParsedRecord[] = []\n for (let i = 0; i < kinds.length; i++) if (texts[i]) out.push(await parseRecord(kinds[i], texts[i], { armoredKey: armoredKey ?? undefined }))\n return out\n },\n enabled: enabled && raw !== undefined,\n })\n\n return { records: records ?? [], isLoading: enabled && (readLoading || parseLoading), refetch: () => { void refetch() } }\n}\n"],"mappings":"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","names":["useQuery","useQuery","useQuery","useQuery","useQuery","useReadContract","useQuery","useReadContract","useQuery","useQuery","jsx","useEnsText","normalize","safeNormalize","normalize","useEnsText","useReadContracts","useQuery","useReadContracts","useQuery"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thurinlabs/identity-kit",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "React SDK for embedding Thurin identity data",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup",
|
|
39
39
|
"test": "vitest run",
|
|
40
|
-
"test:watch": "vitest"
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"prepublishOnly": "node -e \"const o=require('child_process').execSync('git status --porcelain').toString().trim(); if (o) { console.error('Refusing to publish: uncommitted changes. npm would ship whatever dist/ holds, not a commit.\\n'+o); process.exit(1) }\" && npm test && npm run build"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
44
|
"@tanstack/react-query": ">=5",
|