@thurinlabs/identity-kit 1.3.6 → 1.4.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 +36 -9
- package/dist/{chunk-I3Y63ZPK.cjs → chunk-M2OJDB7I.cjs} +205 -48
- package/dist/chunk-M2OJDB7I.cjs.map +1 -0
- package/dist/{chunk-HDBHNN4I.js → chunk-ZAQ2EPF5.js} +185 -28
- package/dist/chunk-ZAQ2EPF5.js.map +1 -0
- package/dist/core.cjs +18 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +85 -2
- package/dist/core.d.ts +85 -2
- package/dist/core.js +17 -1
- package/dist/embed.global.js +92 -30
- package/dist/embed.global.js.map +1 -1
- package/dist/index.cjs +53 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +21 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HDBHNN4I.js.map +0 -1
- package/dist/chunk-I3Y63ZPK.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ A self-contained identity card that fetches and displays all available identity
|
|
|
57
57
|
Wrap your app (or just the part using identity-kit) in `IdentityKitProvider`. If you already have a `WagmiProvider`, the SDK detects it and uses your existing config.
|
|
58
58
|
|
|
59
59
|
```tsx
|
|
60
|
-
// Zero config —
|
|
60
|
+
// Zero config — public RPC and a public Farcaster node, no keys
|
|
61
61
|
<IdentityKitProvider>
|
|
62
62
|
<ThurinCard ens="vitalik.eth" />
|
|
63
63
|
</IdentityKitProvider>
|
|
@@ -65,7 +65,7 @@ Wrap your app (or just the part using identity-kit) in `IdentityKitProvider`. If
|
|
|
65
65
|
// With options
|
|
66
66
|
<IdentityKitProvider
|
|
67
67
|
rpcUrl="https://your-node.example"
|
|
68
|
-
|
|
68
|
+
farcasterHub="https://your-farcaster-node.example"
|
|
69
69
|
baseUrl="https://thurin.id"
|
|
70
70
|
>
|
|
71
71
|
<ThurinCard ens="vitalik.eth" />
|
|
@@ -75,7 +75,8 @@ Wrap your app (or just the part using identity-kit) in `IdentityKitProvider`. If
|
|
|
75
75
|
| Prop | Type | Default | Description |
|
|
76
76
|
|------|------|---------|-------------|
|
|
77
77
|
| `rpcUrl` | `string` | publicnode | Ethereum RPC endpoint. Any RPC works — v2 needs only `eth_call` |
|
|
78
|
-
| `
|
|
78
|
+
| `farcasterHub` | `string` | Hypersnap public node | A Farcaster node's HTTP API for Farcaster proofs. The default, Quilibrium's `haatz.quilibrium.com`, needs no key; it sees the visitor's IP and which account was checked (1.3.7) |
|
|
79
|
+
| `neynarApiKey` | `string` | — | Optional: read Farcaster through Neynar's hub instead. Not needed since 1.3.7 |
|
|
79
80
|
| `baseUrl` | `string` | `https://thurin.id` | Base URL for "View on Thurin" links |
|
|
80
81
|
| `network` | `'mainnet' \| 'sepolia' \| 'local'` | `'mainnet'` | Which chain to read the PGPRegistry on (`local` = a running anvil) |
|
|
81
82
|
| `registryAddress` | `string` | v2 address | Override the registry contract address |
|
|
@@ -169,7 +170,7 @@ import { identifyProof, verifyProof, displayUrl, proofHref, proofSecondaryHref }
|
|
|
169
170
|
const proof = identifyProof({ name: 'proof@thurin.id', value: 'https://gist.github.com/alice/abc123' })
|
|
170
171
|
// → { provider: 'github', label: 'GitHub', user: 'alice', gistId: 'abc123', url }
|
|
171
172
|
|
|
172
|
-
const result = await verifyProof(proof, fingerprint
|
|
173
|
+
const result = await verifyProof(proof, fingerprint) // options: { farcasterHub?, neynarApiKey? }
|
|
173
174
|
// → { verified: boolean, reason?: string }
|
|
174
175
|
```
|
|
175
176
|
|
|
@@ -184,7 +185,9 @@ const keyInfo = await parsePgpKey(armoredKey)
|
|
|
184
185
|
// → { fingerprint, userIDs, algorithm, created, expires, notations, subkeys } | null
|
|
185
186
|
|
|
186
187
|
const verification = await verifyAttestation({ pgpPublicKey, pgpSignature, fingerprint, ethAddress })
|
|
187
|
-
// → { verified
|
|
188
|
+
// → { verified, kind, at?, revocationReason?, signingKey?, expiresAt?, algorithm?, reason? }
|
|
189
|
+
// kind: 'verified' | 'expired' | 'signing-key-expired' | 'revoked' | 'compromised'
|
|
190
|
+
// | 'signing-key-revoked' | 'unsupported' | 'bad-signature' (1.4.0)
|
|
188
191
|
|
|
189
192
|
// Prepare a key for publishing: drop every user ID that contains an email address.
|
|
190
193
|
const stripped = await stripEmailUserIDs(armoredKey)
|
|
@@ -193,7 +196,30 @@ const stripped = await stripEmailUserIDs(armoredKey)
|
|
|
193
196
|
await hasEmailUserID(armoredKey) // → true if any user ID contains an @
|
|
194
197
|
```
|
|
195
198
|
|
|
196
|
-
**Published identity.** An attestation stores the armored key on-chain, permanently and publicly. Since 0.9.0 the intended shape is a key whose only user ID is a non-email one (
|
|
199
|
+
**Published identity.** An attestation stores the armored key on-chain, permanently and publicly. Since 0.9.0 the intended shape is a key whose only user ID is a non-email one (usually the name already on the key, without the email), carrying the `proof@thurin.id` notations. `stripEmailUserIDs` produces that from a normal export; the stripped key still verifies (`verifyAttestation` needs at least one self-certified user ID, so a key with none is rejected) and keeps the notations on the user ID it retains. Proofs are then read from the on-chain key, never from a keyserver.
|
|
200
|
+
|
|
201
|
+
### Why a claim does or doesn't count (1.4.0)
|
|
202
|
+
|
|
203
|
+
`verifyAttestation` returns a `kind` along with `verified`, so a page can say why in plain words instead of showing a library error. Checked in this order: the key revoked (`compromised` when the owner's reason was compromise), the key expired, the signing subkey revoked, the signing subkey expired; then an algorithm openpgp.js refuses (`unsupported`, e.g. DSA); anything else is `bad-signature`. `at` is the date that goes with it. A verified result carries `expiresAt`: the earlier of the key's and the signing subkey's expiry.
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import { claimCheckText, expiresSoon, expiresSoonText, claimFates, claimFateText, CLAIM_CHECK_LABEL } from '@thurinlabs/identity-kit'
|
|
207
|
+
|
|
208
|
+
claimCheckText(verification)
|
|
209
|
+
// → { kind: 'expired', label: 'key expired',
|
|
210
|
+
// sentence: 'The key on this claim expired on Mar 5, 2029, so the claim no longer counts.',
|
|
211
|
+
// fix: 'Extend the key, then Update key. No new signature needed.' } // `fix` is for the owner
|
|
212
|
+
|
|
213
|
+
const soon = expiresSoon(verification) // within 30 days → { days, at } | null
|
|
214
|
+
if (soon) expiresSoonText(soon) // 'Key expires in 12 days (Mar 6, 2027).'
|
|
215
|
+
|
|
216
|
+
// Revoked or replaced: reattest revokes and attests in one transaction, so a claim revoked the
|
|
217
|
+
// same second a newer one was created was replaced by it.
|
|
218
|
+
const fates = claimFates(attestations) // Map<index, { state: 'active' | 'revoked' | 'replaced', at?, by? }>
|
|
219
|
+
claimFateText(fates.get(1)!) // 'Replaced by claim #2 on Oct 3, 2026.'
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Dates are formatted in UTC ("Mar 5, 2029") so every viewer sees the same day; pass your own formatter as the second argument.
|
|
197
223
|
|
|
198
224
|
Nothing in the kit talks to a keyserver: keys come from the registry, and `thurin keyserver` / keys.thurin.id serve them over HKP for gpg.
|
|
199
225
|
|
|
@@ -311,7 +337,8 @@ For static sites, Jekyll blogs, WordPress, or any HTML page — use the standalo
|
|
|
311
337
|
| `data-thurin-card` | ENS name or ETH address to look up (required) |
|
|
312
338
|
| `data-theme` | `thurin`, `dark`, or `light` (default: `thurin`). Change it after render and the card follows, so a page with a theme switch can keep the card in step. |
|
|
313
339
|
| `data-rpc-url` | Optional. Any Ethereum RPC; the card reads the v2 registry with plain calls, so the keyless public default works. |
|
|
314
|
-
| `data-
|
|
340
|
+
| `data-farcaster-hub` | Optional. A Farcaster node for Farcaster proofs; the default is a public node that needs no key. |
|
|
341
|
+
| `data-neynar-key` | Optional. Read Farcaster through Neynar with your key instead (not needed since 1.3.7). |
|
|
315
342
|
| `data-base-url` | Optional. Where the card's "View on Thurin" link points (default `https://thurin.id`). A page served from ENS can pass its own name so the link stays on ENS. |
|
|
316
343
|
| `data-network` | Optional. `sepolia` or `local` instead of mainnet. |
|
|
317
344
|
| `data-registry-address` | Optional. Override the registry contract address. |
|
|
@@ -324,7 +351,7 @@ The card talks directly to Ethereum and each proof platform — no intermediary,
|
|
|
324
351
|
|----------|-------------|
|
|
325
352
|
| GitHub | Public gist, or a repository description (the form an organisation can use) |
|
|
326
353
|
| DNS | TXT record |
|
|
327
|
-
| Farcaster | Public cast (
|
|
354
|
+
| Farcaster | Public cast (read from a public Farcaster node; no key) |
|
|
328
355
|
| Codeberg | Repository description |
|
|
329
356
|
| Mastodon | Profile metadata |
|
|
330
357
|
|
|
@@ -334,7 +361,7 @@ The card talks directly to Ethereum and each proof platform — no intermediary,
|
|
|
334
361
|
|
|
335
362
|
## Key algorithms
|
|
336
363
|
|
|
337
|
-
Any curve openpgp.js can compute is accepted: Ed25519, Cv25519, NIST P-256/384/521, brainpool, RSA, and **secp256k1**. openpgp.js rejects secp256k1 by default because RFC 9580 does not list it; identity-kit removes secp256k1 from `rejectCurves` and leaves the rest of the list alone, since that one entry is a compatibility rule, not a security one (1.3.2; earlier versions cleared the whole set). A secp256k1 PGP key doubles as an Ethereum key (the address is derived from the same public point), so anything that can sign with the PGP key can sign Ethereum transactions: hold such a key if you like, but do not fund its derived address. In Node, openpgp.js needs the `eckey-utils` package for this curve; identity-kit depends on it, so `npm install` brings it in. The browser build needs nothing extra.
|
|
364
|
+
Any curve openpgp.js can compute is accepted: Ed25519, Cv25519, NIST P-256/384/521, brainpool, RSA (2048 bits and up), and **secp256k1**. DSA and short RSA keys are refused by openpgp.js as too weak; since 1.4.0 such a claim reports `kind: 'unsupported'`. openpgp.js rejects secp256k1 by default because RFC 9580 does not list it; identity-kit removes secp256k1 from `rejectCurves` and leaves the rest of the list alone, since that one entry is a compatibility rule, not a security one (1.3.2; earlier versions cleared the whole set). A secp256k1 PGP key doubles as an Ethereum key (the address is derived from the same public point), so anything that can sign with the PGP key can sign Ethereum transactions: hold such a key if you like, but do not fund its derived address. In Node, openpgp.js needs the `eckey-utils` package for this curve; identity-kit depends on it, so `npm install` brings it in. The browser build needs nothing extra.
|
|
338
365
|
|
|
339
366
|
## Migrating from 0.9.x
|
|
340
367
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/core/abi.ts
|
|
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(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await 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; } async function _asyncOptionalChain(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 = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/core/abi.ts
|
|
2
2
|
var REGISTRY_ABI = [
|
|
3
3
|
{
|
|
4
4
|
"type": "function",
|
|
@@ -1392,6 +1392,61 @@ async function verifyClearsigned({ armoredKey, clearsigned }) {
|
|
|
1392
1392
|
return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text: "" };
|
|
1393
1393
|
}
|
|
1394
1394
|
}
|
|
1395
|
+
var REVOCATION_REASON = {
|
|
1396
|
+
// 0 = "no reason specified", gpg's default: say nothing rather than "(reason: no reason given)"
|
|
1397
|
+
1: "replaced by a newer key",
|
|
1398
|
+
2: "compromised",
|
|
1399
|
+
3: "no longer used",
|
|
1400
|
+
32: "a user ID is no longer valid"
|
|
1401
|
+
};
|
|
1402
|
+
var iso = (d) => d instanceof Date ? d.toISOString() : typeof d === "number" && Number.isFinite(d) ? new Date(d).toISOString() : null;
|
|
1403
|
+
function revocationOf(sigs) {
|
|
1404
|
+
const sig = [..._nullishCoalesce(sigs, () => ( []))].sort((a, b) => (_nullishCoalesce(_optionalChain([b, 'access', _13 => _13.created, 'optionalAccess', _14 => _14.getTime, 'optionalCall', _15 => _15()]), () => ( 0))) - (_nullishCoalesce(_optionalChain([a, 'access', _16 => _16.created, 'optionalAccess', _17 => _17.getTime, 'optionalCall', _18 => _18()]), () => ( 0))))[0];
|
|
1405
|
+
const flag = _nullishCoalesce(_optionalChain([sig, 'optionalAccess', _19 => _19.reasonForRevocationFlag]), () => ( null));
|
|
1406
|
+
return {
|
|
1407
|
+
at: iso(_optionalChain([sig, 'optionalAccess', _20 => _20.created])),
|
|
1408
|
+
compromised: flag === 2,
|
|
1409
|
+
revocationReason: _optionalChain([sig, 'optionalAccess', _21 => _21.reasonForRevocationString]) || (flag !== null ? _nullishCoalesce(REVOCATION_REASON[flag], () => ( null)) : null)
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
async function keyLife(openpgp, key, clearsigned, config) {
|
|
1413
|
+
const now = /* @__PURE__ */ new Date();
|
|
1414
|
+
let issuer = null;
|
|
1415
|
+
try {
|
|
1416
|
+
issuer = await _asyncNullishCoalesce(await _asyncOptionalChain([(await openpgp.readCleartextMessage({ cleartextMessage: clearsigned })), 'access', async _22 => _22.signature, 'access', async _23 => _23.packets, 'access', async _24 => _24[0], 'optionalAccess', async _25 => _25.issuerKeyID]), async () => ( null));
|
|
1417
|
+
} catch (e4) {
|
|
1418
|
+
}
|
|
1419
|
+
const signer = issuer && !key.getKeyID().equals(issuer) ? _nullishCoalesce(key.subkeys.find((s) => s.getKeyID().equals(issuer)), () => ( null)) : key;
|
|
1420
|
+
const signingKey = signer ? signer.getFingerprint().toUpperCase() : null;
|
|
1421
|
+
const safe = async (f, fallback) => {
|
|
1422
|
+
try {
|
|
1423
|
+
return await f();
|
|
1424
|
+
} catch (e5) {
|
|
1425
|
+
return fallback;
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
const keyRevoked = await safe(() => key.isRevoked(void 0, void 0, now, config), false);
|
|
1429
|
+
const keyExp = await safe(() => key.getExpirationTime(void 0, config), null);
|
|
1430
|
+
const isSub = signer && signer !== key;
|
|
1431
|
+
const subRevoked = isSub ? await safe(() => signer.isRevoked(null, key.keyPacket, now, config), false) : false;
|
|
1432
|
+
const subExp = isSub ? await safe(() => signer.getExpirationTime(null, config), null) : null;
|
|
1433
|
+
const finite = (d) => d instanceof Date && Number.isFinite(d.getTime()) ? d : null;
|
|
1434
|
+
const kExp = finite(keyExp), sExp = finite(subExp);
|
|
1435
|
+
let problem = null;
|
|
1436
|
+
if (keyRevoked) {
|
|
1437
|
+
const r = revocationOf(key.revocationSignatures);
|
|
1438
|
+
problem = { kind: r.compromised ? "compromised" : "revoked", at: r.at, revocationReason: r.revocationReason };
|
|
1439
|
+
} else if (kExp && kExp <= now) {
|
|
1440
|
+
problem = { kind: "expired", at: kExp.toISOString() };
|
|
1441
|
+
} else if (subRevoked) {
|
|
1442
|
+
const r = revocationOf(signer.revocationSignatures);
|
|
1443
|
+
problem = { kind: r.compromised ? "compromised" : "signing-key-revoked", at: r.at, revocationReason: r.revocationReason };
|
|
1444
|
+
} else if (sExp && sExp <= now) {
|
|
1445
|
+
problem = { kind: "signing-key-expired", at: sExp.toISOString() };
|
|
1446
|
+
}
|
|
1447
|
+
const earliest = [kExp, sExp].filter(Boolean).sort((a, b) => a - b)[0];
|
|
1448
|
+
return { problem, signingKey, expiresAt: earliest ? earliest.toISOString() : null, algorithm: algorithmName(_nullishCoalesce(signer, () => ( key))) };
|
|
1449
|
+
}
|
|
1395
1450
|
async function verifyAttestation({
|
|
1396
1451
|
pgpPublicKey,
|
|
1397
1452
|
pgpSignature,
|
|
@@ -1400,24 +1455,31 @@ async function verifyAttestation({
|
|
|
1400
1455
|
}) {
|
|
1401
1456
|
try {
|
|
1402
1457
|
if (!pgpPublicKey || !pgpSignature) {
|
|
1403
|
-
return { verified: false, reason: "Missing PGP data" };
|
|
1458
|
+
return { verified: false, reason: "Missing PGP data", kind: "bad-signature" };
|
|
1404
1459
|
}
|
|
1405
1460
|
const openpgp = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
|
|
1406
1461
|
const { readKey } = openpgp;
|
|
1407
1462
|
const publicKey = await readKey({ armoredKey: pgpPublicKey });
|
|
1408
1463
|
const keyFingerprint = publicKey.getFingerprint().toUpperCase();
|
|
1409
1464
|
if (keyFingerprint !== fingerprint.toUpperCase()) {
|
|
1410
|
-
return { verified: false, reason: "Key fingerprint mismatch" };
|
|
1465
|
+
return { verified: false, reason: "Key fingerprint mismatch", kind: "bad-signature" };
|
|
1411
1466
|
}
|
|
1467
|
+
const life = await keyLife(openpgp, publicKey, pgpSignature, pgpConfig(openpgp));
|
|
1412
1468
|
const v = await verifyClearsigned({ armoredKey: pgpPublicKey, clearsigned: pgpSignature });
|
|
1413
|
-
if (!v.verified)
|
|
1469
|
+
if (!v.verified) {
|
|
1470
|
+
if (life.problem) return { verified: false, reason: v.reason, signingKey: life.signingKey, ...life.problem };
|
|
1471
|
+
if (/too weak|reject|unsupported|not supported|unknown (curve|algorithm)/i.test(v.reason || "")) {
|
|
1472
|
+
return { verified: false, reason: v.reason, kind: "unsupported", algorithm: life.algorithm, signingKey: life.signingKey };
|
|
1473
|
+
}
|
|
1474
|
+
return { verified: false, reason: v.reason, kind: "bad-signature", signingKey: life.signingKey };
|
|
1475
|
+
}
|
|
1414
1476
|
const signedText = v.text;
|
|
1415
1477
|
if (!signedText.toLowerCase().includes(ethAddress.toLowerCase())) {
|
|
1416
|
-
return { verified: false, reason: "Signed message does not contain ETH address" };
|
|
1478
|
+
return { verified: false, reason: "Signed message does not contain ETH address", kind: "bad-signature", signingKey: life.signingKey };
|
|
1417
1479
|
}
|
|
1418
|
-
return { verified: true };
|
|
1419
|
-
} catch (
|
|
1420
|
-
return { verified: false, reason: "Signature verification failed" };
|
|
1480
|
+
return { verified: true, kind: "verified", signingKey: life.signingKey, expiresAt: life.expiresAt };
|
|
1481
|
+
} catch (e6) {
|
|
1482
|
+
return { verified: false, reason: "Signature verification failed", kind: "bad-signature" };
|
|
1421
1483
|
}
|
|
1422
1484
|
}
|
|
1423
1485
|
var EMAIL_UID = /@/;
|
|
@@ -1428,7 +1490,7 @@ async function stripEmailUserIDs(armoredKey) {
|
|
|
1428
1490
|
const kept = [];
|
|
1429
1491
|
const removed = [];
|
|
1430
1492
|
const users = key.users.filter((u) => {
|
|
1431
|
-
const uid = _optionalChain([u, 'access',
|
|
1493
|
+
const uid = _optionalChain([u, 'access', _26 => _26.userID, 'optionalAccess', _27 => _27.userID]);
|
|
1432
1494
|
if (!uid) return false;
|
|
1433
1495
|
if (EMAIL_UID.test(uid)) {
|
|
1434
1496
|
removed.push(uid);
|
|
@@ -1440,7 +1502,7 @@ async function stripEmailUserIDs(armoredKey) {
|
|
|
1440
1502
|
if (users.length === 0) return null;
|
|
1441
1503
|
key.users = users;
|
|
1442
1504
|
return { armored: key.armor(), kept, removed };
|
|
1443
|
-
} catch (
|
|
1505
|
+
} catch (e7) {
|
|
1444
1506
|
return null;
|
|
1445
1507
|
}
|
|
1446
1508
|
}
|
|
@@ -1448,8 +1510,8 @@ async function hasEmailUserID(armoredKey) {
|
|
|
1448
1510
|
try {
|
|
1449
1511
|
const { readKey } = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
|
|
1450
1512
|
const key = await readKey({ armoredKey });
|
|
1451
|
-
return key.users.some((u) => EMAIL_UID.test(_nullishCoalesce(_optionalChain([u, 'access',
|
|
1452
|
-
} catch (
|
|
1513
|
+
return key.users.some((u) => EMAIL_UID.test(_nullishCoalesce(_optionalChain([u, 'access', _28 => _28.userID, 'optionalAccess', _29 => _29.userID]), () => ( ""))));
|
|
1514
|
+
} catch (e8) {
|
|
1453
1515
|
return false;
|
|
1454
1516
|
}
|
|
1455
1517
|
}
|
|
@@ -1480,7 +1542,7 @@ async function fetchEFPGraph(address) {
|
|
|
1480
1542
|
top8 = (top8Data.following || []).map((f) => f.data);
|
|
1481
1543
|
}
|
|
1482
1544
|
return { followers, following, top8, hasEfp: !!listId || followers > 0 };
|
|
1483
|
-
} catch (
|
|
1545
|
+
} catch (e9) {
|
|
1484
1546
|
return null;
|
|
1485
1547
|
}
|
|
1486
1548
|
}
|
|
@@ -1577,7 +1639,7 @@ async function verifyGitHub(proof, fingerprint) {
|
|
|
1577
1639
|
const resp = await fetch(`https://api.github.com/gists/${proof.gistId}`);
|
|
1578
1640
|
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
1579
1641
|
const data = await resp.json();
|
|
1580
|
-
if (_optionalChain([data, 'access',
|
|
1642
|
+
if (_optionalChain([data, 'access', _30 => _30.owner, 'optionalAccess', _31 => _31.login, 'optionalAccess', _32 => _32.toLowerCase, 'call', _33 => _33()]) !== proof.user.toLowerCase()) {
|
|
1581
1643
|
return { verified: false, reason: "Gist owner does not match claimed user" };
|
|
1582
1644
|
}
|
|
1583
1645
|
for (const file of Object.values(data.files || {})) {
|
|
@@ -1597,7 +1659,7 @@ async function verifyGitHubRepo(proof, fingerprint) {
|
|
|
1597
1659
|
);
|
|
1598
1660
|
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
1599
1661
|
const data = await resp.json();
|
|
1600
|
-
if (_optionalChain([data, 'access',
|
|
1662
|
+
if (_optionalChain([data, 'access', _34 => _34.owner, 'optionalAccess', _35 => _35.login, 'optionalAccess', _36 => _36.toLowerCase, 'call', _37 => _37()]) !== proof.user.toLowerCase()) {
|
|
1601
1663
|
return { verified: false, reason: "Repository owner does not match claimed account" };
|
|
1602
1664
|
}
|
|
1603
1665
|
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
@@ -1627,33 +1689,31 @@ async function verifyDNS(proof, fingerprint) {
|
|
|
1627
1689
|
return { verified: false, reason: `DNS fetch failed: ${err.message}` };
|
|
1628
1690
|
}
|
|
1629
1691
|
}
|
|
1692
|
+
var FARCASTER_HUB = "https://haatz.quilibrium.com";
|
|
1630
1693
|
var NEYNAR_HUB = "https://hub-api.neynar.com";
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
const
|
|
1638
|
-
|
|
1639
|
-
}
|
|
1640
|
-
async function verifyFarcaster(proof, fingerprint, neynarApiKey) {
|
|
1641
|
-
if (!neynarApiKey) {
|
|
1642
|
-
return { verified: false, reason: "Farcaster verification requires a Neynar API key" };
|
|
1643
|
-
}
|
|
1694
|
+
function farcasterSource(opts) {
|
|
1695
|
+
if (opts.farcasterHub) return { base: opts.farcasterHub.replace(/\/+$/, ""), headers: {} };
|
|
1696
|
+
if (opts.neynarApiKey) return { base: NEYNAR_HUB, headers: { "x-api-key": opts.neynarApiKey } };
|
|
1697
|
+
return { base: FARCASTER_HUB, headers: {} };
|
|
1698
|
+
}
|
|
1699
|
+
async function verifyFarcaster(proof, fingerprint, opts = {}) {
|
|
1700
|
+
const { base, headers } = farcasterSource(opts);
|
|
1701
|
+
const host = base.replace(/^https?:\/\//, "");
|
|
1644
1702
|
try {
|
|
1645
|
-
const
|
|
1703
|
+
const nameResp = await fetch(`${base}/v1/userNameProofByName?name=${encodeURIComponent(proof.user)}`, { headers });
|
|
1704
|
+
if (nameResp.status === 404) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
1705
|
+
if (!nameResp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${nameResp.status}` };
|
|
1706
|
+
const fid = (await nameResp.json()).fid;
|
|
1646
1707
|
if (!fid) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
1647
|
-
const headers = { "x-api-key": neynarApiKey };
|
|
1648
1708
|
let pageToken = "";
|
|
1649
1709
|
for (let page = 0; page < 5; page++) {
|
|
1650
|
-
const url = `${
|
|
1710
|
+
const url = `${base}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ""}`;
|
|
1651
1711
|
const resp = await fetch(url, { headers });
|
|
1652
|
-
if (!resp.ok) return { verified: false, reason: `Farcaster
|
|
1712
|
+
if (!resp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${resp.status}` };
|
|
1653
1713
|
const data = await resp.json();
|
|
1654
1714
|
for (const msg of data.messages || []) {
|
|
1655
1715
|
if (msg.hash && msg.hash.startsWith(proof.castHash)) {
|
|
1656
|
-
const text = _optionalChain([msg, 'access',
|
|
1716
|
+
const text = _optionalChain([msg, 'access', _38 => _38.data, 'optionalAccess', _39 => _39.castAddBody, 'optionalAccess', _40 => _40.text]) || "";
|
|
1657
1717
|
if (containsFingerprint(text, fingerprint)) {
|
|
1658
1718
|
return { verified: true };
|
|
1659
1719
|
}
|
|
@@ -1664,7 +1724,7 @@ async function verifyFarcaster(proof, fingerprint, neynarApiKey) {
|
|
|
1664
1724
|
}
|
|
1665
1725
|
return { verified: false, reason: "Cast not found" };
|
|
1666
1726
|
} catch (err) {
|
|
1667
|
-
return { verified: false, reason: `Farcaster
|
|
1727
|
+
return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) didn't answer (${err.message})` };
|
|
1668
1728
|
}
|
|
1669
1729
|
}
|
|
1670
1730
|
async function verifyCodeberg(proof, fingerprint) {
|
|
@@ -1717,10 +1777,10 @@ var verifiers = {
|
|
|
1717
1777
|
codeberg: verifyCodeberg,
|
|
1718
1778
|
mastodon: verifyMastodon
|
|
1719
1779
|
};
|
|
1720
|
-
async function verifyProof(proof, fingerprint,
|
|
1780
|
+
async function verifyProof(proof, fingerprint, opts) {
|
|
1721
1781
|
const fn = verifiers[proof.provider];
|
|
1722
1782
|
if (!fn) return { verified: false, reason: "Unknown provider" };
|
|
1723
|
-
return fn(proof, fingerprint, neynarApiKey);
|
|
1783
|
+
return fn(proof, fingerprint, typeof opts === "string" ? { neynarApiKey: opts } : _nullishCoalesce(opts, () => ( {})));
|
|
1724
1784
|
}
|
|
1725
1785
|
|
|
1726
1786
|
// src/core/avatar.ts
|
|
@@ -1768,19 +1828,19 @@ async function nftAvatarImage(tokenUri, tokenId, fetchFn = fetch) {
|
|
|
1768
1828
|
meta = await res.json();
|
|
1769
1829
|
break;
|
|
1770
1830
|
}
|
|
1771
|
-
} catch (
|
|
1831
|
+
} catch (e10) {
|
|
1772
1832
|
}
|
|
1773
1833
|
}
|
|
1774
1834
|
if (!meta) return null;
|
|
1775
1835
|
}
|
|
1776
|
-
return avatarUrl(_nullishCoalesce(_optionalChain([meta, 'optionalAccess',
|
|
1836
|
+
return avatarUrl(_nullishCoalesce(_optionalChain([meta, 'optionalAccess', _41 => _41.image]), () => ( _optionalChain([meta, 'optionalAccess', _42 => _42.image_url]))));
|
|
1777
1837
|
}
|
|
1778
1838
|
|
|
1779
1839
|
// src/core/ensHint.ts
|
|
1780
1840
|
var _ens = require('viem/ens');
|
|
1781
1841
|
var ENS_HINT_KEY = "id.thurin";
|
|
1782
1842
|
function ensHintFor(record, verifiedFingerprint) {
|
|
1783
|
-
const raw = _nullishCoalesce(_optionalChain([record, 'optionalAccess',
|
|
1843
|
+
const raw = _nullishCoalesce(_optionalChain([record, 'optionalAccess', _43 => _43.trim, 'call', _44 => _44()]), () => ( ""));
|
|
1784
1844
|
const expected = verifiedFingerprint ? normalizeFingerprint(verifiedFingerprint) : null;
|
|
1785
1845
|
if (!raw) return { key: ENS_HINT_KEY, state: "unset", record: null, fingerprint: null, expected };
|
|
1786
1846
|
const fingerprint = normalizeFingerprint(raw);
|
|
@@ -1949,12 +2009,12 @@ function decodeRecord(hex) {
|
|
|
1949
2009
|
}
|
|
1950
2010
|
function parsePointer(text) {
|
|
1951
2011
|
const p = JSON.parse(text);
|
|
1952
|
-
if (_optionalChain([p, 'optionalAccess',
|
|
2012
|
+
if (_optionalChain([p, 'optionalAccess', _45 => _45.v]) !== 1 || !Array.isArray(p.releases)) throw new Error("Not a v1 thurin.pointer record");
|
|
1953
2013
|
return p;
|
|
1954
2014
|
}
|
|
1955
2015
|
function addPointer(existing, entry) {
|
|
1956
2016
|
if (!/^[0-9a-f]{64}$/i.test(entry.sha256)) throw new Error("sha256 must be 64 hex characters");
|
|
1957
|
-
const releases = [entry, ...(_nullishCoalesce(_optionalChain([existing, 'optionalAccess',
|
|
2017
|
+
const releases = [entry, ...(_nullishCoalesce(_optionalChain([existing, 'optionalAccess', _46 => _46.releases]), () => ( []))).filter((r) => r.name !== entry.name)];
|
|
1958
2018
|
const dropped = [];
|
|
1959
2019
|
const record = { v: 1, releases };
|
|
1960
2020
|
while (new TextEncoder().encode(JSON.stringify(record)).length > MAX_RECORD_BYTES && releases.length > 1) dropped.push(releases.pop());
|
|
@@ -1988,14 +2048,14 @@ async function parseRecord(kind, text, opts = {}) {
|
|
|
1988
2048
|
case "thurin.affiliation": {
|
|
1989
2049
|
try {
|
|
1990
2050
|
const j = JSON.parse(t);
|
|
1991
|
-
if (_optionalChain([j, 'optionalAccess',
|
|
2051
|
+
if (_optionalChain([j, 'optionalAccess', _47 => _47.v]) !== 1 || typeof j.with !== "string" || !j.with) return invalid('Not a v1 affiliation: expected {"v":1,"with":"<address or name>"}');
|
|
1992
2052
|
return { ...base, valid: true, data: { type: "affiliation", with: j.with, role: typeof j.role === "string" ? j.role : null } };
|
|
1993
|
-
} catch (
|
|
2053
|
+
} catch (e11) {
|
|
1994
2054
|
return invalid("Not JSON");
|
|
1995
2055
|
}
|
|
1996
2056
|
}
|
|
1997
2057
|
case "thurin.canary": {
|
|
1998
|
-
const date = _optionalChain([t, 'access',
|
|
2058
|
+
const date = _optionalChain([t, 'access', _48 => _48.match, 'call', _49 => _49(ISO_DATE), 'optionalAccess', _50 => _50[1]]);
|
|
1999
2059
|
if (!date) return invalid("No date in the statement");
|
|
2000
2060
|
const clearsigned = t.startsWith("-----BEGIN PGP SIGNED MESSAGE-----");
|
|
2001
2061
|
let statement = clearsigned ? (_nullishCoalesce(t.split(/\r?\n\r?\n/)[1], () => ( ""))).split("-----BEGIN PGP SIGNATURE-----")[0].trim() : t;
|
|
@@ -2015,7 +2075,7 @@ async function parseRecord(kind, text, opts = {}) {
|
|
|
2015
2075
|
try {
|
|
2016
2076
|
const { readMessage } = await Promise.resolve().then(() => _interopRequireWildcard(require("openpgp")));
|
|
2017
2077
|
recipients = (await readMessage({ armoredMessage: t })).getEncryptionKeyIDs().length;
|
|
2018
|
-
} catch (
|
|
2078
|
+
} catch (e12) {
|
|
2019
2079
|
return invalid("PGP message does not parse");
|
|
2020
2080
|
}
|
|
2021
2081
|
return { ...base, valid: true, data: { type: "encrypted", recipients } };
|
|
@@ -2023,7 +2083,7 @@ async function parseRecord(kind, text, opts = {}) {
|
|
|
2023
2083
|
case "thurin.pointer": {
|
|
2024
2084
|
try {
|
|
2025
2085
|
return { ...base, valid: true, data: { type: "pointer", releases: parsePointer(t).releases } };
|
|
2026
|
-
} catch (
|
|
2086
|
+
} catch (e13) {
|
|
2027
2087
|
return invalid("Not a v1 pointer record");
|
|
2028
2088
|
}
|
|
2029
2089
|
}
|
|
@@ -2041,6 +2101,103 @@ async function fetchRecords(client, registry, abi, owner, index, kinds = IDENTIT
|
|
|
2041
2101
|
return out;
|
|
2042
2102
|
}
|
|
2043
2103
|
|
|
2104
|
+
// src/core/claimStatus.ts
|
|
2105
|
+
var CLAIM_CHECK_LABEL = {
|
|
2106
|
+
verified: "pgp verified",
|
|
2107
|
+
expired: "key expired",
|
|
2108
|
+
"signing-key-expired": "signing key expired",
|
|
2109
|
+
revoked: "key revoked",
|
|
2110
|
+
compromised: "key compromised",
|
|
2111
|
+
"signing-key-revoked": "signing key revoked",
|
|
2112
|
+
unsupported: "not supported",
|
|
2113
|
+
"bad-signature": "doesn't verify"
|
|
2114
|
+
};
|
|
2115
|
+
function formatClaimDate(isoOrSeconds) {
|
|
2116
|
+
if (isoOrSeconds === null || isoOrSeconds === void 0) return "an unknown date";
|
|
2117
|
+
const d = typeof isoOrSeconds === "number" ? new Date(isoOrSeconds * 1e3) : new Date(isoOrSeconds);
|
|
2118
|
+
if (Number.isNaN(d.getTime())) return "an unknown date";
|
|
2119
|
+
return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
|
|
2120
|
+
}
|
|
2121
|
+
function shortFingerprint(fpr) {
|
|
2122
|
+
if (!fpr) return "unknown";
|
|
2123
|
+
const f = fpr.toUpperCase();
|
|
2124
|
+
return `${f.slice(0, 4)} ${f.slice(4, 8)} \u2026`;
|
|
2125
|
+
}
|
|
2126
|
+
function claimCheckText(v, formatDate = formatClaimDate) {
|
|
2127
|
+
const kind = _nullishCoalesce(v.kind, () => ( (v.verified ? "verified" : "bad-signature")));
|
|
2128
|
+
const at = formatDate(v.at);
|
|
2129
|
+
const t = (sentence, fix = null) => ({ kind, label: CLAIM_CHECK_LABEL[kind], sentence, fix });
|
|
2130
|
+
switch (kind) {
|
|
2131
|
+
case "verified":
|
|
2132
|
+
return t("The key signed a line naming this address, and the signature checks out.");
|
|
2133
|
+
case "expired":
|
|
2134
|
+
return t(
|
|
2135
|
+
`The key on this claim expired on ${at}, so the claim no longer counts.`,
|
|
2136
|
+
"Extend the key, then Update key. No new signature needed."
|
|
2137
|
+
);
|
|
2138
|
+
case "signing-key-expired":
|
|
2139
|
+
return t(
|
|
2140
|
+
`The subkey that signed this claim (${shortFingerprint(v.signingKey)}) expired on ${at}.`,
|
|
2141
|
+
"Extend that subkey, then Update key."
|
|
2142
|
+
);
|
|
2143
|
+
case "revoked":
|
|
2144
|
+
return t(
|
|
2145
|
+
`Its owner revoked this PGP key on ${at}${v.revocationReason ? ` (reason: ${v.revocationReason})` : ""}. The claim no longer counts.`,
|
|
2146
|
+
"Revoke this claim and attest your new key."
|
|
2147
|
+
);
|
|
2148
|
+
case "compromised":
|
|
2149
|
+
return t(
|
|
2150
|
+
`Its owner revoked this PGP key on ${at} as compromised. Don't trust anything it signed after that date.`,
|
|
2151
|
+
"Revoke this claim and attest a new key."
|
|
2152
|
+
);
|
|
2153
|
+
case "signing-key-revoked":
|
|
2154
|
+
return t(
|
|
2155
|
+
`The subkey that signed this claim was revoked on ${at}.`,
|
|
2156
|
+
"Replace the claim with a signature from a current key."
|
|
2157
|
+
);
|
|
2158
|
+
case "unsupported":
|
|
2159
|
+
return t(`This key uses an algorithm Thurin doesn't check${v.algorithm ? ` (${v.algorithm})` : ""}. The claim is on-chain, but no lookup will vouch for it.`);
|
|
2160
|
+
case "bad-signature":
|
|
2161
|
+
default:
|
|
2162
|
+
return t("The signature stored with this claim doesn't match its key, so nothing from this claim is shown.");
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
function expiresSoon(v, now = Date.now(), withinDays = 30) {
|
|
2166
|
+
if (!_optionalChain([v, 'optionalAccess', _51 => _51.verified]) || !v.expiresAt) return null;
|
|
2167
|
+
const ms = new Date(v.expiresAt).getTime() - now;
|
|
2168
|
+
if (!Number.isFinite(ms) || ms < 0 || ms > withinDays * 864e5) return null;
|
|
2169
|
+
return { days: Math.floor(ms / 864e5), at: v.expiresAt };
|
|
2170
|
+
}
|
|
2171
|
+
function expiresSoonText(soon, formatDate = formatClaimDate) {
|
|
2172
|
+
const when = soon.days === 0 ? "today" : soon.days === 1 ? "in 1 day" : `in ${soon.days} days`;
|
|
2173
|
+
return `Key expires ${when} (${formatDate(soon.at)}).`;
|
|
2174
|
+
}
|
|
2175
|
+
function claimFates(claims) {
|
|
2176
|
+
const out = /* @__PURE__ */ new Map();
|
|
2177
|
+
for (const c of claims) {
|
|
2178
|
+
if (!c.revokedAt) {
|
|
2179
|
+
out.set(c.index, { state: "active" });
|
|
2180
|
+
continue;
|
|
2181
|
+
}
|
|
2182
|
+
const successor = claims.filter((n) => n.index > c.index && n.createdAt === c.revokedAt).sort((a, b) => a.index - b.index)[0];
|
|
2183
|
+
out.set(c.index, successor ? { state: "replaced", at: c.revokedAt, by: successor.index } : { state: "revoked", at: c.revokedAt });
|
|
2184
|
+
}
|
|
2185
|
+
return out;
|
|
2186
|
+
}
|
|
2187
|
+
function claimFateText(fate, formatDate = (s) => formatClaimDate(s)) {
|
|
2188
|
+
if (fate.state === "revoked") return `Revoked by its owner on ${formatDate(fate.at)}.`;
|
|
2189
|
+
if (fate.state === "replaced") return `Replaced by claim #${fate.by} on ${formatDate(fate.at)}.`;
|
|
2190
|
+
return null;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
|
|
2194
|
+
|
|
2195
|
+
|
|
2196
|
+
|
|
2197
|
+
|
|
2198
|
+
|
|
2199
|
+
|
|
2200
|
+
|
|
2044
2201
|
|
|
2045
2202
|
|
|
2046
2203
|
|
|
@@ -2104,5 +2261,5 @@ async function fetchRecords(client, registry, abi, owner, index, kinds = IDENTIT
|
|
|
2104
2261
|
|
|
2105
2262
|
|
|
2106
2263
|
|
|
2107
|
-
exports.REGISTRY_ABI = REGISTRY_ABI; exports.REGISTRY_ADDRESS = REGISTRY_ADDRESS; exports.NETWORKS = NETWORKS; exports.isNetworkName = isNetworkName; exports.getRegistry = getRegistry; exports.chainFor = chainFor; exports.needsRpcProbe = needsRpcProbe; exports.identityErrorKind = identityErrorKind; exports.IDENTITY_ERROR_TEXT = IDENTITY_ERROR_TEXT; exports.normalizeFingerprint = normalizeFingerprint; exports.fingerprintToBytes = fingerprintToBytes; exports.bytesToFingerprint = bytesToFingerprint; exports.fingerprintHash = fingerprintHash; exports.keyIdOf = keyIdOf; exports.keyIdToBytes = keyIdToBytes; exports.parsePgpKey = parsePgpKey; exports.verifyClearsigned = verifyClearsigned; exports.verifyAttestation = verifyAttestation; exports.stripEmailUserIDs = stripEmailUserIDs; exports.hasEmailUserID = hasEmailUserID; exports.fetchEFPGraph = fetchEFPGraph; exports.identifyProof = identifyProof; exports.displayUrl = displayUrl; exports.proofHref = proofHref; exports.proofSecondaryHref = proofSecondaryHref; exports.verifyProof = verifyProof; exports.IPFS_GATEWAYS = IPFS_GATEWAYS; exports.IPFS_GATEWAY = IPFS_GATEWAY; exports.ARWEAVE_GATEWAY = ARWEAVE_GATEWAY; exports.avatarUrl = avatarUrl; exports.avatarFallbacks = avatarFallbacks; exports.parseNftAvatar = parseNftAvatar; exports.NFT_AVATAR_ABI = NFT_AVATAR_ABI; exports.nftAvatarImage = nftAvatarImage; exports.ENS_HINT_KEY = ENS_HINT_KEY; exports.ensHintFor = ensHintFor; exports.ensHintValue = ensHintValue; exports.ENS_TEXT_RESOLVER_ABI = ENS_TEXT_RESOLVER_ABI; exports.ensHintWrite = ensHintWrite; exports.fetchEnsHint = fetchEnsHint; exports.EIP712_NAME = EIP712_NAME; exports.EIP712_VERSION = EIP712_VERSION; exports.registryDomain = registryDomain; exports.AUTHORIZATION_TYPES = AUTHORIZATION_TYPES; exports.attestTypedData = attestTypedData; exports.reattestTypedData = reattestTypedData; exports.updateKeyTypedData = updateKeyTypedData; exports.revokeTypedData = revokeTypedData; exports.setRecordTypedData = setRecordTypedData; exports.authorizationDigest = authorizationDigest; exports.recordKind = recordKind; exports.MAX_RECORD_BYTES = MAX_RECORD_BYTES; exports.kindName = kindName; exports.KNOWN_KINDS = KNOWN_KINDS; exports.IDENTITY_KINDS = IDENTITY_KINDS; exports.encodeRecord = encodeRecord; exports.decodeRecord = decodeRecord; exports.parsePointer = parsePointer; exports.addPointer = addPointer; exports.renderPointer = renderPointer; exports.parseRecord = parseRecord; exports.fetchRecords = fetchRecords;
|
|
2108
|
-
//# sourceMappingURL=chunk-
|
|
2264
|
+
exports.REGISTRY_ABI = REGISTRY_ABI; exports.REGISTRY_ADDRESS = REGISTRY_ADDRESS; exports.NETWORKS = NETWORKS; exports.isNetworkName = isNetworkName; exports.getRegistry = getRegistry; exports.chainFor = chainFor; exports.needsRpcProbe = needsRpcProbe; exports.identityErrorKind = identityErrorKind; exports.IDENTITY_ERROR_TEXT = IDENTITY_ERROR_TEXT; exports.normalizeFingerprint = normalizeFingerprint; exports.fingerprintToBytes = fingerprintToBytes; exports.bytesToFingerprint = bytesToFingerprint; exports.fingerprintHash = fingerprintHash; exports.keyIdOf = keyIdOf; exports.keyIdToBytes = keyIdToBytes; exports.parsePgpKey = parsePgpKey; exports.verifyClearsigned = verifyClearsigned; exports.verifyAttestation = verifyAttestation; exports.stripEmailUserIDs = stripEmailUserIDs; exports.hasEmailUserID = hasEmailUserID; exports.fetchEFPGraph = fetchEFPGraph; exports.identifyProof = identifyProof; exports.displayUrl = displayUrl; exports.proofHref = proofHref; exports.proofSecondaryHref = proofSecondaryHref; exports.FARCASTER_HUB = FARCASTER_HUB; exports.verifyProof = verifyProof; exports.IPFS_GATEWAYS = IPFS_GATEWAYS; exports.IPFS_GATEWAY = IPFS_GATEWAY; exports.ARWEAVE_GATEWAY = ARWEAVE_GATEWAY; exports.avatarUrl = avatarUrl; exports.avatarFallbacks = avatarFallbacks; exports.parseNftAvatar = parseNftAvatar; exports.NFT_AVATAR_ABI = NFT_AVATAR_ABI; exports.nftAvatarImage = nftAvatarImage; exports.ENS_HINT_KEY = ENS_HINT_KEY; exports.ensHintFor = ensHintFor; exports.ensHintValue = ensHintValue; exports.ENS_TEXT_RESOLVER_ABI = ENS_TEXT_RESOLVER_ABI; exports.ensHintWrite = ensHintWrite; exports.fetchEnsHint = fetchEnsHint; exports.EIP712_NAME = EIP712_NAME; exports.EIP712_VERSION = EIP712_VERSION; exports.registryDomain = registryDomain; exports.AUTHORIZATION_TYPES = AUTHORIZATION_TYPES; exports.attestTypedData = attestTypedData; exports.reattestTypedData = reattestTypedData; exports.updateKeyTypedData = updateKeyTypedData; exports.revokeTypedData = revokeTypedData; exports.setRecordTypedData = setRecordTypedData; exports.authorizationDigest = authorizationDigest; exports.recordKind = recordKind; exports.MAX_RECORD_BYTES = MAX_RECORD_BYTES; exports.kindName = kindName; exports.KNOWN_KINDS = KNOWN_KINDS; exports.IDENTITY_KINDS = IDENTITY_KINDS; exports.encodeRecord = encodeRecord; exports.decodeRecord = decodeRecord; exports.parsePointer = parsePointer; exports.addPointer = addPointer; exports.renderPointer = renderPointer; exports.parseRecord = parseRecord; exports.fetchRecords = fetchRecords; exports.CLAIM_CHECK_LABEL = CLAIM_CHECK_LABEL; exports.formatClaimDate = formatClaimDate; exports.claimCheckText = claimCheckText; exports.expiresSoon = expiresSoon; exports.expiresSoonText = expiresSoonText; exports.claimFates = claimFates; exports.claimFateText = claimFateText;
|
|
2265
|
+
//# sourceMappingURL=chunk-M2OJDB7I.cjs.map
|