@x1id/resolve 0.7.0 → 0.9.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 +71 -1
- package/dist/agent.js +1 -1
- package/dist/attestation.d.ts +84 -8
- package/dist/attestation.js +120 -10
- package/dist/base58.d.ts +8 -0
- package/dist/base58.js +31 -0
- package/dist/gate.d.ts +43 -0
- package/dist/gate.js +38 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/signin.d.ts +318 -0
- package/dist/signin.js +0 -0
- package/dist/types.d.ts +48 -0
- package/dist/x402.d.ts +18 -11
- package/dist/x402.js +18 -11
- package/package.json +2 -9
- package/wasm/x1_resolve_wasm.wasm +0 -0
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -204,6 +204,76 @@ one: `ResolveError { code: "not-found", reason: "nft-burned" }`. The registry
|
|
|
204
204
|
account's stale `owner` field is never returned for a tokenized handle, on any
|
|
205
205
|
path.
|
|
206
206
|
|
|
207
|
+
## Per-platform verification ("verified by [platform]")
|
|
208
|
+
|
|
209
|
+
Beyond the per-chain `verification` above, a handle can carry **per-platform
|
|
210
|
+
verifications** — the blue-tick signal that a handle proved control of an X,
|
|
211
|
+
Discord, GitHub, Telegram account or a domain (#7145). A resolver that supplies
|
|
212
|
+
them (the tools/api REST `/resolve` response) attaches an optional
|
|
213
|
+
`verifications[]` to the `Resolved` result:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { isVerified, verifiedPlatforms, isVerifiedOn } from "@x1id/resolve";
|
|
217
|
+
|
|
218
|
+
// `resolved` is a Resolved carrying the resolver's `verifications[]`, e.g.
|
|
219
|
+
// verifications: [
|
|
220
|
+
// { platform: "x", value: "@michelle", verified: true, attested_at: 1730000000 },
|
|
221
|
+
// { platform: "github", value: "michelle", verified: true, attested_at: 1730000100 },
|
|
222
|
+
// { platform: "domain", value: "michelle.com", verified: true, attested_at: 1730000200 },
|
|
223
|
+
// ]
|
|
224
|
+
|
|
225
|
+
isVerified(resolved); // true — show the blue tick (any live signal)
|
|
226
|
+
verifiedPlatforms(resolved); // ["x", "github", "domain"]
|
|
227
|
+
isVerifiedOn(resolved, "github"); // true
|
|
228
|
+
isVerifiedOn(resolved, "telegram"); // false
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Rendering a badge.** Light the tick when `isVerified(resolved)` is true; on
|
|
232
|
+
hover/focus, list `verifiedPlatforms(resolved)`, and pull each platform's
|
|
233
|
+
account handle from the paired `value` (`@michelle` for `x`, `michelle.com` for
|
|
234
|
+
`domain`). Platform slugs are `x` / `discord` / `github` / `telegram`, plus
|
|
235
|
+
legacy `domain` (DNS) and `social` (pre-per-platform, no attribution).
|
|
236
|
+
|
|
237
|
+
All three helpers are **absence-tolerant**: an older resolver, the SDK's own
|
|
238
|
+
on-chain `resolve()` (which does not populate the field — see below), or a
|
|
239
|
+
handle with no live verifications all yield `false` / `[]`, never a throw. The
|
|
240
|
+
field is optional and additive, so existing `Resolved` consumers are unaffected.
|
|
241
|
+
|
|
242
|
+
> **Honest trust caveat — read before you render.** A verification is an
|
|
243
|
+
> **off-chain review** (a DNS lookup, an OAuth completion, a human check) that
|
|
244
|
+
> x1id's **attestor key** signed off on. It is **not** a trustless proof: that
|
|
245
|
+
> key is admin-rotatable, so the anchor is "whoever the registry admin currently
|
|
246
|
+
> designates". Verifications are also **epoch-bound** — the resolver emits an
|
|
247
|
+
> entry only while the underlying attestation is live (`attested_at >=
|
|
248
|
+
> registered_at` **and** `>= records_cleared_at`), so a previous owner's proof
|
|
249
|
+
> never leaks through. Render it as **"verified by x1id"**, never as more.
|
|
250
|
+
|
|
251
|
+
### Reading verification straight from chain
|
|
252
|
+
|
|
253
|
+
The SDK's own on-chain `resolve()` does **not** populate `verifications` (it
|
|
254
|
+
returns only what an RPC read proves). To compute the same signal from chain,
|
|
255
|
+
read the `Attestation` accounts directly — this applies the staleness rule
|
|
256
|
+
structurally:
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
import {
|
|
260
|
+
fetchAttestations, liveAttestations, isHandleVerified,
|
|
261
|
+
platformSlug, textRecordKeyFor,
|
|
262
|
+
} from "@x1id/resolve";
|
|
263
|
+
|
|
264
|
+
const atts = await fetchAttestations(rpc, programId, handleAccount, registeredAt, recordsClearedAt);
|
|
265
|
+
isHandleVerified(atts); // the #7145 tick, from chain
|
|
266
|
+
for (const a of liveAttestations(atts)) {
|
|
267
|
+
platformSlug(a.kind); // "x" | "discord" | "github" | "telegram" | null (legacy)
|
|
268
|
+
textRecordKeyFor(a.kind); // "com.x" | ... | "website" | null — where the value lives
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`platformSlug` / `textRecordKeyFor` mirror the on-chain
|
|
273
|
+
`Attestation::platform_slug` and the shared contract table exactly, so an
|
|
274
|
+
integrator joining a live attestation to its paired text record reads the same
|
|
275
|
+
mapping the program, resolver and app badge use.
|
|
276
|
+
|
|
207
277
|
## What works today
|
|
208
278
|
|
|
209
279
|
| | Status |
|
|
@@ -300,7 +370,7 @@ Also shipped, same build → sign → relay shape:
|
|
|
300
370
|
| Typed text records (website / avatar / socials) | `buildCreateTextRecordIx`, `buildUpdateTextRecordIx`, `buildCloseTextRecordIx` |
|
|
301
371
|
| Register a handle | `buildRegisterIx` (+ `fetchConfigTreasury`) |
|
|
302
372
|
| Verify a non-SVM address record | `buildCreateRecordIx`, `buildVerifyRecordEthIx`, `buildVerifyRecordSvmIx`, `buildVerifyRecordBtcIx` (+ `buildRecordChallenge`, `splitEthSignature`, `splitBtcSignature`) — see [Verifying a non-SVM address record](#verifying-a-non-svm-address-record) |
|
|
303
|
-
| Domain / social attestations | `buildCreateAttestationIx
|
|
373
|
+
| Domain / social / per-platform attestations | `buildCreateAttestationIx` (kinds 0–5), `buildCloseAttestationIx` (+ `fetchAttestations`, `isHandleVerified`, `platformSlug`, `textRecordKeyFor`) — see [Per-platform verification](#per-platform-verification-verified-by-platform) |
|
|
304
374
|
| Agent records (identity, manifest, x402 hints) | `validateManifest`, `agentVerificationLevel`, `x402AcceptsFromManifest`, `agentTextRecords` — see [Agent records](#agent-records) below |
|
|
305
375
|
| Subname records / revoke | `buildRevokeSubnameIx`, `buildCreateSubnameRecordIx`, `buildUpdateSubnameRecordIx`, `buildCloseSubnameRecordIx` |
|
|
306
376
|
| Voucher claim / refund | `buildClaimVoucherIx`, `buildRefundVoucherIx` |
|
package/dist/agent.js
CHANGED
|
@@ -192,7 +192,7 @@ export function x402AcceptsFromManifest(manifest, opts) {
|
|
|
192
192
|
// Mainnet has no such id yet — X1 mainnet doesn't exist — so a manifest
|
|
193
193
|
// claiming network: "mainnet" without its own explicit x402.network gets
|
|
194
194
|
// an honestly-provisional placeholder rather than a fabricated genesis hash.
|
|
195
|
-
const network = x402?.network ?? (manifest.x1id.network === "testnet" ? X1_TESTNET_NETWORK : "
|
|
195
|
+
const network = x402?.network ?? (manifest.x1id.network === "testnet" ? X1_TESTNET_NETWORK : "solana:mainnet-unassigned");
|
|
196
196
|
const payTo = opts?.payTo ?? manifest.payment?.addresses?.[0]?.address;
|
|
197
197
|
return x402Endpoints.map((e) => {
|
|
198
198
|
const hint = {
|
package/dist/attestation.d.ts
CHANGED
|
@@ -62,14 +62,35 @@
|
|
|
62
62
|
*/
|
|
63
63
|
import type { AddressLike, BuiltInstruction } from "./delegate.js";
|
|
64
64
|
import type { RpcFn } from "./accounts.js";
|
|
65
|
+
import type { HandleVerification } from "./types.js";
|
|
65
66
|
/** Seed prefix of an attestation PDA: `["attestation", handlePda, kindByte]`. */
|
|
66
67
|
export declare const ATTESTATION_SEED = "attestation";
|
|
67
68
|
/** Seed of the attestor-config singleton PDA: `["attestor_config"]`. */
|
|
68
69
|
export declare const ATTESTOR_CONFIG_SEED = "attestor_config";
|
|
69
|
-
/** `Attestation.kind` — domain control proven (DNS TXT challenge).
|
|
70
|
+
/** `Attestation.kind` — domain control proven (DNS TXT challenge). LEGACY-safe:
|
|
71
|
+
* kept valid forever. Pairs with the `website` text record. */
|
|
70
72
|
export declare const ATTESTATION_KIND_DNS = 0;
|
|
71
|
-
/** `Attestation.kind` — social-account control proven.
|
|
73
|
+
/** `Attestation.kind` — generic social-account control proven. LEGACY: the
|
|
74
|
+
* single pre-per-platform code, carrying no platform attribution. Kept valid
|
|
75
|
+
* forever so existing accounts are never orphaned; new attestations should use
|
|
76
|
+
* one of the per-platform codes below. */
|
|
72
77
|
export declare const ATTESTATION_KIND_SOCIAL = 1;
|
|
78
|
+
/** `Attestation.kind` — X (Twitter) control proven. Pairs with text record
|
|
79
|
+
* `com.x`; platform slug `x`. */
|
|
80
|
+
export declare const ATTESTATION_KIND_X = 2;
|
|
81
|
+
/** `Attestation.kind` — Discord control proven. Pairs with text record
|
|
82
|
+
* `com.discord`; platform slug `discord`. */
|
|
83
|
+
export declare const ATTESTATION_KIND_DISCORD = 3;
|
|
84
|
+
/** `Attestation.kind` — GitHub control proven. Pairs with text record
|
|
85
|
+
* `com.github`; platform slug `github`. */
|
|
86
|
+
export declare const ATTESTATION_KIND_GITHUB = 4;
|
|
87
|
+
/** `Attestation.kind` — Telegram control proven. Pairs with text record
|
|
88
|
+
* `org.telegram`; platform slug `telegram`. */
|
|
89
|
+
export declare const ATTESTATION_KIND_TELEGRAM = 5;
|
|
90
|
+
/** One past the highest valid `kind`. `create_attestation` refuses anything
|
|
91
|
+
* `>=` this on-chain; the SDK's builder enforces the same bound. Mirrors
|
|
92
|
+
* `Attestation::KIND_COUNT` (programs/x1-handles/src/state.rs). */
|
|
93
|
+
export declare const ATTESTATION_KIND_COUNT = 6;
|
|
73
94
|
/** Anchor account discriminator: `sha256("account:Attestation")[0..8]`.
|
|
74
95
|
* Pinned (this SDK is zero-dependency and cannot assume WebCrypto SHA-256
|
|
75
96
|
* everywhere it runs); asserted against a re-derivation in the test suite
|
|
@@ -90,19 +111,42 @@ export declare const CLOSE_ATTESTATION_DISCRIMINATOR: Uint8Array;
|
|
|
90
111
|
export declare const ATTESTATION_LEN = 114;
|
|
91
112
|
/** `AttestorConfig` account size: disc(8) + attestor(32) + bump(1). */
|
|
92
113
|
export declare const ATTESTOR_CONFIG_LEN = 41;
|
|
114
|
+
/** Human name an attestation `kind` byte carries. */
|
|
115
|
+
export type AttestationKindName = "dns" | "social" | "x" | "discord" | "github" | "telegram";
|
|
93
116
|
/** Which verification signal an attestation `kind` byte names, or null for a
|
|
94
|
-
* kind this SDK does not know (future program versions may add kinds).
|
|
95
|
-
|
|
117
|
+
* kind this SDK does not know (future program versions may add kinds). For the
|
|
118
|
+
* per-platform kinds this returns the platform slug; the two legacy kinds keep
|
|
119
|
+
* their historical names (`dns`, `social`). */
|
|
120
|
+
export declare function attestationKindName(kind: number): AttestationKindName | null;
|
|
121
|
+
/**
|
|
122
|
+
* The canonical platform slug for a `kind`, or null for the two legacy codes
|
|
123
|
+
* (0 = dns, 1 = social — neither names a single platform) and for any unknown
|
|
124
|
+
* kind. Mirrors `Attestation::platform_slug` (programs/x1-handles/src/state.rs)
|
|
125
|
+
* exactly: `Some` only for the per-platform kinds 2..5.
|
|
126
|
+
*/
|
|
127
|
+
export declare function platformSlug(kind: number): string | null;
|
|
128
|
+
/**
|
|
129
|
+
* The text-record key an attestation `kind` pairs with — where the actual
|
|
130
|
+
* account handle / domain for that platform lives — or null for the legacy
|
|
131
|
+
* generic-social kind (1) and any unknown kind. Mirrors the shared contract
|
|
132
|
+
* table: kind 0 → `website`, 2 → `com.x`, 3 → `com.discord`, 4 → `com.github`,
|
|
133
|
+
* 5 → `org.telegram`.
|
|
134
|
+
*/
|
|
135
|
+
export declare function textRecordKeyFor(kind: number): string | null;
|
|
96
136
|
/** A decoded verification attestation, staleness already judged. */
|
|
97
137
|
export interface HandleAttestation {
|
|
98
138
|
/** The Attestation account's address, base58. */
|
|
99
139
|
readonly account: string;
|
|
100
140
|
/** The Handle account this attestation is for, base58. */
|
|
101
141
|
readonly handle: string;
|
|
102
|
-
/** Raw kind byte (0 = dns, 1 = social
|
|
142
|
+
/** Raw kind byte (0 = dns, 1 = social, 2 = x, 3 = discord, 4 = github,
|
|
143
|
+
* 5 = telegram). */
|
|
103
144
|
readonly kind: number;
|
|
104
|
-
/** Human name for `kind`, or null for an unknown kind.
|
|
105
|
-
|
|
145
|
+
/** Human name for `kind`, or null for an unknown kind. Per-platform kinds
|
|
146
|
+
* resolve to their platform slug; see {@link platformSlug} /
|
|
147
|
+
* {@link textRecordKeyFor} to map a kind to its slug and paired text-record
|
|
148
|
+
* key. */
|
|
149
|
+
readonly kindName: AttestationKindName | null;
|
|
106
150
|
/** `sha256` commitment to the off-chain verdict-inputs bundle. */
|
|
107
151
|
readonly evidenceHash: Uint8Array;
|
|
108
152
|
/** Unix seconds the attestation was (last) stamped. */
|
|
@@ -189,7 +233,11 @@ export interface CreateAttestationParams {
|
|
|
189
233
|
/** The `["attestation", handle, kindByte]` PDA — derive per the module
|
|
190
234
|
* docs, with the SAME kind byte passed below. */
|
|
191
235
|
readonly attestation: AddressLike;
|
|
192
|
-
/**
|
|
236
|
+
/** One of the `ATTESTATION_KIND_*` codes (0..{@link ATTESTATION_KIND_COUNT}
|
|
237
|
+
* − 1): {@link ATTESTATION_KIND_DNS}, {@link ATTESTATION_KIND_SOCIAL}, or a
|
|
238
|
+
* per-platform kind ({@link ATTESTATION_KIND_X},
|
|
239
|
+
* {@link ATTESTATION_KIND_DISCORD}, {@link ATTESTATION_KIND_GITHUB},
|
|
240
|
+
* {@link ATTESTATION_KIND_TELEGRAM}). */
|
|
193
241
|
readonly kind: number;
|
|
194
242
|
/** `sha256` of the off-chain verdict-inputs bundle (32 bytes) — a
|
|
195
243
|
* commitment, never the raw evidence. */
|
|
@@ -224,3 +272,31 @@ export interface CloseAttestationParams {
|
|
|
224
272
|
* a rotated-away key's output).
|
|
225
273
|
*/
|
|
226
274
|
export declare function buildCloseAttestationIx(p: CloseAttestationParams): BuiltInstruction;
|
|
275
|
+
/** The minimal shape the verification helpers read — any {@link Resolved}
|
|
276
|
+
* satisfies it, as does a bare `{ verifications }` an integrator assembles
|
|
277
|
+
* from a raw REST payload. */
|
|
278
|
+
export interface HasVerifications {
|
|
279
|
+
readonly verifications?: readonly HandleVerification[];
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The platform slugs a handle is LIVE-verified on — e.g. `["x", "github"]` —
|
|
283
|
+
* de-duplicated, in first-seen order. Only entries with `verified === true`
|
|
284
|
+
* count. Returns `[]` when `verifications` is absent or empty.
|
|
285
|
+
*/
|
|
286
|
+
export declare function verifiedPlatforms(resolved: HasVerifications): string[];
|
|
287
|
+
/**
|
|
288
|
+
* Whether a handle carries a live verification for a specific platform slug
|
|
289
|
+
* (`"x"`, `"discord"`, `"github"`, `"telegram"`, `"domain"`, `"social"`).
|
|
290
|
+
* False when `verifications` is absent.
|
|
291
|
+
*/
|
|
292
|
+
export declare function isVerifiedOn(resolved: HasVerifications, platform: string): boolean;
|
|
293
|
+
/**
|
|
294
|
+
* The #7145 single-signal rule at the resolve layer: true when the handle has
|
|
295
|
+
* AT LEAST ONE live verification of any platform. This is the blue-tick
|
|
296
|
+
* condition. False when `verifications` is absent or empty.
|
|
297
|
+
*
|
|
298
|
+
* Trust caveat (see {@link HandleVerification}): a true here means x1id's
|
|
299
|
+
* rotatable attestor key signed off on an off-chain review that was still live
|
|
300
|
+
* at resolve time — render it as "verified by x1id", not as trustless proof.
|
|
301
|
+
*/
|
|
302
|
+
export declare function isVerified(resolved: HasVerifications): boolean;
|
package/dist/attestation.js
CHANGED
|
@@ -66,10 +66,30 @@ import { decodeBase58_32 } from "./base58.js";
|
|
|
66
66
|
export const ATTESTATION_SEED = "attestation";
|
|
67
67
|
/** Seed of the attestor-config singleton PDA: `["attestor_config"]`. */
|
|
68
68
|
export const ATTESTOR_CONFIG_SEED = "attestor_config";
|
|
69
|
-
/** `Attestation.kind` — domain control proven (DNS TXT challenge).
|
|
69
|
+
/** `Attestation.kind` — domain control proven (DNS TXT challenge). LEGACY-safe:
|
|
70
|
+
* kept valid forever. Pairs with the `website` text record. */
|
|
70
71
|
export const ATTESTATION_KIND_DNS = 0;
|
|
71
|
-
/** `Attestation.kind` — social-account control proven.
|
|
72
|
+
/** `Attestation.kind` — generic social-account control proven. LEGACY: the
|
|
73
|
+
* single pre-per-platform code, carrying no platform attribution. Kept valid
|
|
74
|
+
* forever so existing accounts are never orphaned; new attestations should use
|
|
75
|
+
* one of the per-platform codes below. */
|
|
72
76
|
export const ATTESTATION_KIND_SOCIAL = 1;
|
|
77
|
+
/** `Attestation.kind` — X (Twitter) control proven. Pairs with text record
|
|
78
|
+
* `com.x`; platform slug `x`. */
|
|
79
|
+
export const ATTESTATION_KIND_X = 2;
|
|
80
|
+
/** `Attestation.kind` — Discord control proven. Pairs with text record
|
|
81
|
+
* `com.discord`; platform slug `discord`. */
|
|
82
|
+
export const ATTESTATION_KIND_DISCORD = 3;
|
|
83
|
+
/** `Attestation.kind` — GitHub control proven. Pairs with text record
|
|
84
|
+
* `com.github`; platform slug `github`. */
|
|
85
|
+
export const ATTESTATION_KIND_GITHUB = 4;
|
|
86
|
+
/** `Attestation.kind` — Telegram control proven. Pairs with text record
|
|
87
|
+
* `org.telegram`; platform slug `telegram`. */
|
|
88
|
+
export const ATTESTATION_KIND_TELEGRAM = 5;
|
|
89
|
+
/** One past the highest valid `kind`. `create_attestation` refuses anything
|
|
90
|
+
* `>=` this on-chain; the SDK's builder enforces the same bound. Mirrors
|
|
91
|
+
* `Attestation::KIND_COUNT` (programs/x1-handles/src/state.rs). */
|
|
92
|
+
export const ATTESTATION_KIND_COUNT = 6;
|
|
73
93
|
/** Anchor account discriminator: `sha256("account:Attestation")[0..8]`.
|
|
74
94
|
* Pinned (this SDK is zero-dependency and cannot assume WebCrypto SHA-256
|
|
75
95
|
* everywhere it runs); asserted against a re-derivation in the test suite
|
|
@@ -120,13 +140,69 @@ function toBase58(v, what) {
|
|
|
120
140
|
return encodeBase58(toBytes32(v, what));
|
|
121
141
|
}
|
|
122
142
|
/** Which verification signal an attestation `kind` byte names, or null for a
|
|
123
|
-
* kind this SDK does not know (future program versions may add kinds).
|
|
143
|
+
* kind this SDK does not know (future program versions may add kinds). For the
|
|
144
|
+
* per-platform kinds this returns the platform slug; the two legacy kinds keep
|
|
145
|
+
* their historical names (`dns`, `social`). */
|
|
124
146
|
export function attestationKindName(kind) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
switch (kind) {
|
|
148
|
+
case ATTESTATION_KIND_DNS:
|
|
149
|
+
return "dns";
|
|
150
|
+
case ATTESTATION_KIND_SOCIAL:
|
|
151
|
+
return "social";
|
|
152
|
+
case ATTESTATION_KIND_X:
|
|
153
|
+
return "x";
|
|
154
|
+
case ATTESTATION_KIND_DISCORD:
|
|
155
|
+
return "discord";
|
|
156
|
+
case ATTESTATION_KIND_GITHUB:
|
|
157
|
+
return "github";
|
|
158
|
+
case ATTESTATION_KIND_TELEGRAM:
|
|
159
|
+
return "telegram";
|
|
160
|
+
default:
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The canonical platform slug for a `kind`, or null for the two legacy codes
|
|
166
|
+
* (0 = dns, 1 = social — neither names a single platform) and for any unknown
|
|
167
|
+
* kind. Mirrors `Attestation::platform_slug` (programs/x1-handles/src/state.rs)
|
|
168
|
+
* exactly: `Some` only for the per-platform kinds 2..5.
|
|
169
|
+
*/
|
|
170
|
+
export function platformSlug(kind) {
|
|
171
|
+
switch (kind) {
|
|
172
|
+
case ATTESTATION_KIND_X:
|
|
173
|
+
return "x";
|
|
174
|
+
case ATTESTATION_KIND_DISCORD:
|
|
175
|
+
return "discord";
|
|
176
|
+
case ATTESTATION_KIND_GITHUB:
|
|
177
|
+
return "github";
|
|
178
|
+
case ATTESTATION_KIND_TELEGRAM:
|
|
179
|
+
return "telegram";
|
|
180
|
+
default:
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The text-record key an attestation `kind` pairs with — where the actual
|
|
186
|
+
* account handle / domain for that platform lives — or null for the legacy
|
|
187
|
+
* generic-social kind (1) and any unknown kind. Mirrors the shared contract
|
|
188
|
+
* table: kind 0 → `website`, 2 → `com.x`, 3 → `com.discord`, 4 → `com.github`,
|
|
189
|
+
* 5 → `org.telegram`.
|
|
190
|
+
*/
|
|
191
|
+
export function textRecordKeyFor(kind) {
|
|
192
|
+
switch (kind) {
|
|
193
|
+
case ATTESTATION_KIND_DNS:
|
|
194
|
+
return "website";
|
|
195
|
+
case ATTESTATION_KIND_X:
|
|
196
|
+
return "com.x";
|
|
197
|
+
case ATTESTATION_KIND_DISCORD:
|
|
198
|
+
return "com.discord";
|
|
199
|
+
case ATTESTATION_KIND_GITHUB:
|
|
200
|
+
return "com.github";
|
|
201
|
+
case ATTESTATION_KIND_TELEGRAM:
|
|
202
|
+
return "org.telegram";
|
|
203
|
+
default:
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
130
206
|
}
|
|
131
207
|
/**
|
|
132
208
|
* Decode one Attestation account.
|
|
@@ -250,8 +326,9 @@ export function buildSetAttestorIx(p) {
|
|
|
250
326
|
* the (handle, kind) attestation.
|
|
251
327
|
*/
|
|
252
328
|
export function buildCreateAttestationIx(p) {
|
|
253
|
-
if (!Number.isInteger(p.kind) || p.kind < 0 || p.kind
|
|
254
|
-
throw new Error(
|
|
329
|
+
if (!Number.isInteger(p.kind) || p.kind < 0 || p.kind >= ATTESTATION_KIND_COUNT) {
|
|
330
|
+
throw new Error(`kind must be an integer in 0..${ATTESTATION_KIND_COUNT - 1} ` +
|
|
331
|
+
"(0 dns, 1 social, 2 x, 3 discord, 4 github, 5 telegram)");
|
|
255
332
|
}
|
|
256
333
|
if (p.evidenceHash.length !== 32) {
|
|
257
334
|
throw new Error("evidenceHash must be exactly 32 bytes (a sha256 digest)");
|
|
@@ -288,3 +365,36 @@ export function buildCloseAttestationIx(p) {
|
|
|
288
365
|
data: CLOSE_ATTESTATION_DISCRIMINATOR.slice(),
|
|
289
366
|
};
|
|
290
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* The platform slugs a handle is LIVE-verified on — e.g. `["x", "github"]` —
|
|
370
|
+
* de-duplicated, in first-seen order. Only entries with `verified === true`
|
|
371
|
+
* count. Returns `[]` when `verifications` is absent or empty.
|
|
372
|
+
*/
|
|
373
|
+
export function verifiedPlatforms(resolved) {
|
|
374
|
+
const out = [];
|
|
375
|
+
for (const v of resolved.verifications ?? []) {
|
|
376
|
+
if (v && v.verified && !out.includes(v.platform))
|
|
377
|
+
out.push(v.platform);
|
|
378
|
+
}
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Whether a handle carries a live verification for a specific platform slug
|
|
383
|
+
* (`"x"`, `"discord"`, `"github"`, `"telegram"`, `"domain"`, `"social"`).
|
|
384
|
+
* False when `verifications` is absent.
|
|
385
|
+
*/
|
|
386
|
+
export function isVerifiedOn(resolved, platform) {
|
|
387
|
+
return (resolved.verifications ?? []).some((v) => v && v.verified && v.platform === platform);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* The #7145 single-signal rule at the resolve layer: true when the handle has
|
|
391
|
+
* AT LEAST ONE live verification of any platform. This is the blue-tick
|
|
392
|
+
* condition. False when `verifications` is absent or empty.
|
|
393
|
+
*
|
|
394
|
+
* Trust caveat (see {@link HandleVerification}): a true here means x1id's
|
|
395
|
+
* rotatable attestor key signed off on an off-chain review that was still live
|
|
396
|
+
* at resolve time — render it as "verified by x1id", not as trustless proof.
|
|
397
|
+
*/
|
|
398
|
+
export function isVerified(resolved) {
|
|
399
|
+
return (resolved.verifications ?? []).some((v) => v && v.verified);
|
|
400
|
+
}
|
package/dist/base58.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
export declare function encodeBase58(bytes: Uint8Array): string;
|
|
2
|
+
/**
|
|
3
|
+
* Decode base58 to its exact bytes (standard leading-`1`→leading-zero-byte
|
|
4
|
+
* handling), or null for a character outside the alphabet. Unlike
|
|
5
|
+
* {@link decodeBase58_32} this preserves length, so it decodes a 64-byte
|
|
6
|
+
* ed25519 signature (`signin.ts`) as faithfully as a 32-byte key. Pass
|
|
7
|
+
* `expectedLen` to reject anything but that length up front.
|
|
8
|
+
*/
|
|
9
|
+
export declare function decodeBase58(s: string, expectedLen?: number): Uint8Array | null;
|
|
2
10
|
/** Decode to exactly 32 bytes, or null if the input is not a valid address. */
|
|
3
11
|
export declare function decodeBase58_32(s: string): Uint8Array | null;
|
package/dist/base58.js
CHANGED
|
@@ -16,6 +16,37 @@ export function encodeBase58(bytes) {
|
|
|
16
16
|
}
|
|
17
17
|
return out === "" ? "1" : out;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Decode base58 to its exact bytes (standard leading-`1`→leading-zero-byte
|
|
21
|
+
* handling), or null for a character outside the alphabet. Unlike
|
|
22
|
+
* {@link decodeBase58_32} this preserves length, so it decodes a 64-byte
|
|
23
|
+
* ed25519 signature (`signin.ts`) as faithfully as a 32-byte key. Pass
|
|
24
|
+
* `expectedLen` to reject anything but that length up front.
|
|
25
|
+
*/
|
|
26
|
+
export function decodeBase58(s, expectedLen) {
|
|
27
|
+
if (s.length === 0)
|
|
28
|
+
return null;
|
|
29
|
+
let zeros = 0;
|
|
30
|
+
while (zeros < s.length && s[zeros] === "1")
|
|
31
|
+
zeros++;
|
|
32
|
+
let n = 0n;
|
|
33
|
+
for (const c of s) {
|
|
34
|
+
const i = A.indexOf(c);
|
|
35
|
+
if (i === -1)
|
|
36
|
+
return null;
|
|
37
|
+
n = n * 58n + BigInt(i);
|
|
38
|
+
}
|
|
39
|
+
const tail = [];
|
|
40
|
+
while (n > 0n) {
|
|
41
|
+
tail.unshift(Number(n & 255n));
|
|
42
|
+
n >>= 8n;
|
|
43
|
+
}
|
|
44
|
+
const out = new Uint8Array(zeros + tail.length);
|
|
45
|
+
out.set(tail, zeros);
|
|
46
|
+
if (expectedLen !== undefined && out.length !== expectedLen)
|
|
47
|
+
return null;
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
19
50
|
/** Decode to exactly 32 bytes, or null if the input is not a valid address. */
|
|
20
51
|
export function decodeBase58_32(s) {
|
|
21
52
|
if (s.length === 0 || s.length > 44)
|
package/dist/gate.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attestation-gated allowlists (#8316) — gate access to your app by whether an
|
|
3
|
+
* `@handle` holds a verified on-chain attestation (any signal, or a SPECIFIC
|
|
4
|
+
* platform like "verified GitHub"). A project reuses X1ID's blue-tick oracle as
|
|
5
|
+
* its allowlist instead of running its own KYC.
|
|
6
|
+
*
|
|
7
|
+
* The gate operates on the attestations {@link fetchAttestations} returns; the
|
|
8
|
+
* staleness rule (an attestation only counts for the CURRENT owner —
|
|
9
|
+
* `attested_at >= handle.registered_at`) is already baked into `HandleAttestation.stale`,
|
|
10
|
+
* and this module always ignores stale ones. Nothing here trusts an off-chain
|
|
11
|
+
* claim: every input is an on-chain, attestor-signed `Attestation` account.
|
|
12
|
+
*/
|
|
13
|
+
import { type HandleAttestation } from "./attestation.js";
|
|
14
|
+
import type { RpcFn } from "./accounts.js";
|
|
15
|
+
/** What a handle must prove to pass the gate:
|
|
16
|
+
* - `"any-verified"` — at least one non-stale attestation of any kind.
|
|
17
|
+
* - `{ kind }` — a non-stale attestation of exactly that kind.
|
|
18
|
+
* - `{ anyOf }` — a non-stale attestation of any listed kind (e.g. X OR GitHub). */
|
|
19
|
+
export type Gate = "any-verified" | {
|
|
20
|
+
readonly kind: number;
|
|
21
|
+
} | {
|
|
22
|
+
readonly anyOf: readonly number[];
|
|
23
|
+
};
|
|
24
|
+
/** Named platform kinds, re-exported so callers gate without magic numbers. */
|
|
25
|
+
export declare const GateKind: {
|
|
26
|
+
readonly dns: 0;
|
|
27
|
+
readonly social: 1;
|
|
28
|
+
readonly x: 2;
|
|
29
|
+
readonly discord: 3;
|
|
30
|
+
readonly github: 4;
|
|
31
|
+
readonly telegram: 5;
|
|
32
|
+
};
|
|
33
|
+
/** Pure check: do these (already-fetched) attestations satisfy the gate?
|
|
34
|
+
* Stale attestations never count. */
|
|
35
|
+
export declare function attestationsPassGate(attestations: readonly HandleAttestation[], gate: Gate): boolean;
|
|
36
|
+
/** Fetch a handle's attestations and test the gate in one call. `handleAccount`,
|
|
37
|
+
* `registeredAt` and `recordsClearedAt` come from the handle you resolved
|
|
38
|
+
* (name → `deriveHandleAccount` → decode). Returns the pass/fail plus the
|
|
39
|
+
* attestations so a caller can also render WHICH signals matched. */
|
|
40
|
+
export declare function handlePassesGate(rpc: RpcFn, programId: string, handleAccount: string, registeredAt: bigint, recordsClearedAt: bigint, gate: Gate): Promise<{
|
|
41
|
+
readonly passed: boolean;
|
|
42
|
+
readonly attestations: HandleAttestation[];
|
|
43
|
+
}>;
|
package/dist/gate.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attestation-gated allowlists (#8316) — gate access to your app by whether an
|
|
3
|
+
* `@handle` holds a verified on-chain attestation (any signal, or a SPECIFIC
|
|
4
|
+
* platform like "verified GitHub"). A project reuses X1ID's blue-tick oracle as
|
|
5
|
+
* its allowlist instead of running its own KYC.
|
|
6
|
+
*
|
|
7
|
+
* The gate operates on the attestations {@link fetchAttestations} returns; the
|
|
8
|
+
* staleness rule (an attestation only counts for the CURRENT owner —
|
|
9
|
+
* `attested_at >= handle.registered_at`) is already baked into `HandleAttestation.stale`,
|
|
10
|
+
* and this module always ignores stale ones. Nothing here trusts an off-chain
|
|
11
|
+
* claim: every input is an on-chain, attestor-signed `Attestation` account.
|
|
12
|
+
*/
|
|
13
|
+
import { fetchAttestations, isHandleVerified, ATTESTATION_KIND_DNS, ATTESTATION_KIND_SOCIAL, ATTESTATION_KIND_X, ATTESTATION_KIND_DISCORD, ATTESTATION_KIND_GITHUB, ATTESTATION_KIND_TELEGRAM, } from "./attestation.js";
|
|
14
|
+
/** Named platform kinds, re-exported so callers gate without magic numbers. */
|
|
15
|
+
export const GateKind = {
|
|
16
|
+
dns: ATTESTATION_KIND_DNS,
|
|
17
|
+
social: ATTESTATION_KIND_SOCIAL,
|
|
18
|
+
x: ATTESTATION_KIND_X,
|
|
19
|
+
discord: ATTESTATION_KIND_DISCORD,
|
|
20
|
+
github: ATTESTATION_KIND_GITHUB,
|
|
21
|
+
telegram: ATTESTATION_KIND_TELEGRAM,
|
|
22
|
+
};
|
|
23
|
+
/** Pure check: do these (already-fetched) attestations satisfy the gate?
|
|
24
|
+
* Stale attestations never count. */
|
|
25
|
+
export function attestationsPassGate(attestations, gate) {
|
|
26
|
+
if (gate === "any-verified")
|
|
27
|
+
return isHandleVerified(attestations);
|
|
28
|
+
const kinds = "kind" in gate ? [gate.kind] : gate.anyOf;
|
|
29
|
+
return attestations.some((a) => !a.stale && kinds.includes(a.kind));
|
|
30
|
+
}
|
|
31
|
+
/** Fetch a handle's attestations and test the gate in one call. `handleAccount`,
|
|
32
|
+
* `registeredAt` and `recordsClearedAt` come from the handle you resolved
|
|
33
|
+
* (name → `deriveHandleAccount` → decode). Returns the pass/fail plus the
|
|
34
|
+
* attestations so a caller can also render WHICH signals matched. */
|
|
35
|
+
export async function handlePassesGate(rpc, programId, handleAccount, registeredAt, recordsClearedAt, gate) {
|
|
36
|
+
const attestations = await fetchAttestations(rpc, programId, handleAccount, registeredAt, recordsClearedAt);
|
|
37
|
+
return { passed: attestationsPassGate(attestations, gate), attestations };
|
|
38
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,13 +27,14 @@
|
|
|
27
27
|
export * from "./types.js";
|
|
28
28
|
export { normalizeHandle, parseName, looksLikeName, type ParsedName } from "./parse.js";
|
|
29
29
|
export { WasmResolver, type WasmTld } from "./wasm.js";
|
|
30
|
-
export { encodeBase58, decodeBase58_32 } from "./base58.js";
|
|
30
|
+
export { encodeBase58, decodeBase58, decodeBase58_32 } from "./base58.js";
|
|
31
31
|
export { decodeRecord, liveRecords, chainForCoinType, valueToAddress, fetchRecords, RECORD_LEN, RECORD_DISC, type HandleRecord, } from "./records.js";
|
|
32
32
|
export { buildControlChallenge, parseControlChallenge, generateControlNonce, createControlChallenge, verifyControlProof, verifyEd25519Strict, CONTROL_CHALLENGE_PREFIX, type ControlChallenge, type ControlConfig, type ControlProof, type ControlVerification, type ControlFailureReason, } from "./control.js";
|
|
33
33
|
export * from "./delegate.js";
|
|
34
34
|
export * from "./subname.js";
|
|
35
35
|
export * from "./recordCount.js";
|
|
36
36
|
export * from "./attestation.js";
|
|
37
|
+
export * from "./gate.js";
|
|
37
38
|
export * from "./textRecords.js";
|
|
38
39
|
export * from "./lock.js";
|
|
39
40
|
export * from "./integrator.js";
|
|
@@ -46,6 +47,7 @@ export * from "./recordWrite.js";
|
|
|
46
47
|
export * from "./clearRecords.js";
|
|
47
48
|
export * from "./commitReveal.js";
|
|
48
49
|
export * from "./pnftTransfer.js";
|
|
50
|
+
export * from "./signin.js";
|
|
49
51
|
import { type Chain, type Resolved } from "./types.js";
|
|
50
52
|
import { WasmResolver } from "./wasm.js";
|
|
51
53
|
import { type HandleRecord } from "./records.js";
|
package/dist/index.js
CHANGED
|
@@ -27,13 +27,14 @@
|
|
|
27
27
|
export * from "./types.js";
|
|
28
28
|
export { normalizeHandle, parseName, looksLikeName } from "./parse.js";
|
|
29
29
|
export { WasmResolver } from "./wasm.js";
|
|
30
|
-
export { encodeBase58, decodeBase58_32 } from "./base58.js";
|
|
30
|
+
export { encodeBase58, decodeBase58, decodeBase58_32 } from "./base58.js";
|
|
31
31
|
export { decodeRecord, liveRecords, chainForCoinType, valueToAddress, fetchRecords, RECORD_LEN, RECORD_DISC, } from "./records.js";
|
|
32
32
|
export { buildControlChallenge, parseControlChallenge, generateControlNonce, createControlChallenge, verifyControlProof, verifyEd25519Strict, CONTROL_CHALLENGE_PREFIX, } from "./control.js";
|
|
33
33
|
export * from "./delegate.js";
|
|
34
34
|
export * from "./subname.js";
|
|
35
35
|
export * from "./recordCount.js";
|
|
36
36
|
export * from "./attestation.js";
|
|
37
|
+
export * from "./gate.js";
|
|
37
38
|
export * from "./textRecords.js";
|
|
38
39
|
export * from "./lock.js";
|
|
39
40
|
export * from "./integrator.js";
|
|
@@ -53,6 +54,7 @@ export * from "./recordWrite.js";
|
|
|
53
54
|
export * from "./clearRecords.js";
|
|
54
55
|
export * from "./commitReveal.js";
|
|
55
56
|
export * from "./pnftTransfer.js";
|
|
57
|
+
export * from "./signin.js";
|
|
56
58
|
import { ResolveError, CHAIN_COIN_TYPE } from "./types.js";
|
|
57
59
|
import { parseName } from "./parse.js";
|
|
58
60
|
import { encodeBase58, decodeBase58_32 } from "./base58.js";
|
package/dist/signin.d.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Sign in with X1ID" — a client-side, wallet-signature proof of @handle
|
|
3
|
+
* ownership (WP #8313). An OAuth-style login *primitive*, framework-agnostic,
|
|
4
|
+
* that any third-party app can drop in to let a user authenticate as their
|
|
5
|
+
* `@handle`.
|
|
6
|
+
*
|
|
7
|
+
* # What this is — and, honestly, what it is not
|
|
8
|
+
*
|
|
9
|
+
* X1ID's app is a static export and `@x1id/resolve` is a pure client library:
|
|
10
|
+
* there is **no server-side token exchange** here. This module is therefore
|
|
11
|
+
* the SIWE-style ("Sign-In With Ethereum", EIP-4361) shape adapted to X1:
|
|
12
|
+
*
|
|
13
|
+
* 1. the relying app builds a **domain-bound** challenge naming the wallet,
|
|
14
|
+
* a fresh nonce, an issued-at and an expiry ({@link createSignInChallenge});
|
|
15
|
+
* 2. the wallet signs the raw UTF-8 bytes of that challenge (ed25519 — X1 and
|
|
16
|
+
* Solana share the curve and key format);
|
|
17
|
+
* 3. the relying app verifies the signature ({@link verifySignIn}) and then
|
|
18
|
+
* reverse-resolves the address to its primary `@handle` and verification
|
|
19
|
+
* summary ({@link resolveIdentity}).
|
|
20
|
+
*
|
|
21
|
+
* A single verified sign-in proves exactly: **"this wallet, which *currently*
|
|
22
|
+
* owns @handle, signed this challenge for this domain, before it expired."** It
|
|
23
|
+
* is NOT a session and NOT a bearer token — the relying app MUST bind the
|
|
24
|
+
* verified identity to its own session (a cookie, a JWT it mints, ...). And
|
|
25
|
+
* handle ownership can change (a sale, a transfer, `clear_primary`), so a long-
|
|
26
|
+
* lived session should re-run {@link resolveIdentity} periodically rather than
|
|
27
|
+
* trust a handle captured at login forever.
|
|
28
|
+
*
|
|
29
|
+
* A full hosted OAuth *authorization-code server* (real token issuance, a
|
|
30
|
+
* `/authorize` + `/token` pair, refresh tokens) needs a backend and is
|
|
31
|
+
* deliberately **out of scope** — see the README's "What needs a server".
|
|
32
|
+
*
|
|
33
|
+
* # Reuse, not reinvention
|
|
34
|
+
*
|
|
35
|
+
* - Signature verification is the SDK's existing RFC-8032-strict
|
|
36
|
+
* {@link import("./control.js").verifyEd25519Strict} (WebCrypto Ed25519),
|
|
37
|
+
* the same primitive the control-proof flow relies on. No new crypto.
|
|
38
|
+
* - Identity resolution is the SDK's own {@link import("./index.js").createResolver}
|
|
39
|
+
* `reverse()` (with all its staleness/authority trust rules) plus
|
|
40
|
+
* {@link import("./attestation.js").fetchAttestations} — never a re-derived
|
|
41
|
+
* PDA or a hand-rolled ownership check.
|
|
42
|
+
*
|
|
43
|
+
* # Cross-protocol separation
|
|
44
|
+
*
|
|
45
|
+
* A sign-in challenge is always a MULTI-LINE SIWE block (it contains `\n`).
|
|
46
|
+
* The record-verification challenge (`x1-handles:v1:...`) and the control-proof
|
|
47
|
+
* challenge (`x1-handles:ctl:v1:...`) are both single lines with no newline, so
|
|
48
|
+
* a signature obtained for one can never validate as another — the byte spaces
|
|
49
|
+
* are structurally disjoint.
|
|
50
|
+
*/
|
|
51
|
+
/** Why a verification or a convenience sign-in failed. Mirrors `ResolveError`'s
|
|
52
|
+
* code-not-string style so a UI can branch on the cause. */
|
|
53
|
+
export type SignInFailureReason =
|
|
54
|
+
/** The challenge string is not a well-formed sign-in challenge. */
|
|
55
|
+
"invalid-challenge"
|
|
56
|
+
/** The signature is malformed (not base58, or not 64 bytes). */
|
|
57
|
+
| "invalid-signature"
|
|
58
|
+
/** The address argument is not a valid base58 X1 address. */
|
|
59
|
+
| "invalid-address"
|
|
60
|
+
/** The address argument does not match the address bound in the challenge. */
|
|
61
|
+
| "address-mismatch"
|
|
62
|
+
/** An `expectedDomain` was given and does not match the challenge's domain. */
|
|
63
|
+
| "domain-mismatch"
|
|
64
|
+
/** An `expectedNonce` was given and does not match the challenge's nonce. */
|
|
65
|
+
| "nonce-mismatch"
|
|
66
|
+
/** `now` is before the challenge's issued-at (minus skew) — clock problem
|
|
67
|
+
* or a challenge minted for the future. */
|
|
68
|
+
| "not-yet-valid"
|
|
69
|
+
/** `now` is past the challenge's expiry (plus skew). Replay window closed. */
|
|
70
|
+
| "expired"
|
|
71
|
+
/** The signature does not verify over the challenge bytes for this address. */
|
|
72
|
+
| "bad-signature";
|
|
73
|
+
/** Codes a thrown {@link SignInError} carries — every {@link SignInFailureReason}
|
|
74
|
+
* plus the convenience-flow-only outcomes. */
|
|
75
|
+
export type SignInErrorCode = SignInFailureReason | "no-handle" | "wallet-error" | "invalid-params";
|
|
76
|
+
/**
|
|
77
|
+
* Thrown by {@link signInWithX1ID} (the convenience flow) and by the argument
|
|
78
|
+
* validation in {@link createSignInChallenge}. Pure verification via
|
|
79
|
+
* {@link verifySignIn} does NOT throw for a verification outcome — it returns
|
|
80
|
+
* `{ ok: false, reason }` — this class is for the throw-based login call.
|
|
81
|
+
*/
|
|
82
|
+
export declare class SignInError extends Error {
|
|
83
|
+
readonly code: SignInErrorCode;
|
|
84
|
+
readonly cause?: unknown | undefined;
|
|
85
|
+
constructor(code: SignInErrorCode, message: string, cause?: unknown | undefined);
|
|
86
|
+
}
|
|
87
|
+
export interface SignInChallengeParams {
|
|
88
|
+
/** The relying party's domain, e.g. `"app.example.com"`. Bound into the
|
|
89
|
+
* challenge; a signature for one domain never validates for another. */
|
|
90
|
+
readonly domain: string;
|
|
91
|
+
/** The X1 wallet address (base58) that will sign. Bound into the challenge so
|
|
92
|
+
* a signature cannot be lifted onto a different account's session. */
|
|
93
|
+
readonly address: string;
|
|
94
|
+
/** Optional human-readable statement shown to the user in their wallet. Single
|
|
95
|
+
* line only (a newline would corrupt the challenge structure). */
|
|
96
|
+
readonly statement?: string;
|
|
97
|
+
/** Resource URI. Defaults to `https://<domain>`. */
|
|
98
|
+
readonly uri?: string;
|
|
99
|
+
/** Single-use nonce. Generated ({@link generateSignInNonce}) when omitted. */
|
|
100
|
+
readonly nonce?: string;
|
|
101
|
+
/** Issued-at, ms since epoch. Defaults to `Date.now()`. */
|
|
102
|
+
readonly issuedAt?: number;
|
|
103
|
+
/** Lifetime in seconds from issued-at. Defaults to 300 (5 minutes). */
|
|
104
|
+
readonly expiresInSec?: number;
|
|
105
|
+
}
|
|
106
|
+
/** A built challenge: the exact string to sign, plus its parsed fields for the
|
|
107
|
+
* relying party to store (nonce single-use, expiry enforcement are the RP's). */
|
|
108
|
+
export interface SignInChallenge {
|
|
109
|
+
/** The exact string whose raw UTF-8 bytes the wallet signs. */
|
|
110
|
+
readonly challenge: string;
|
|
111
|
+
readonly domain: string;
|
|
112
|
+
readonly address: string;
|
|
113
|
+
readonly uri: string;
|
|
114
|
+
readonly statement: string | null;
|
|
115
|
+
readonly nonce: string;
|
|
116
|
+
/** Issued-at, ms since epoch. */
|
|
117
|
+
readonly issuedAt: number;
|
|
118
|
+
/** Expiry, ms since epoch. */
|
|
119
|
+
readonly expiresAt: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Generate a single-use sign-in nonce: `bytes` bytes (>= 8) from the platform
|
|
123
|
+
* CSPRNG, hex-encoded to stay inside the nonce charset. SINGLE-USE — the
|
|
124
|
+
* relying party stores it with the issued challenge and rejects a second proof
|
|
125
|
+
* against it (client-side code cannot enforce single use; the RP must).
|
|
126
|
+
*/
|
|
127
|
+
export declare function generateSignInNonce(bytes?: number): string;
|
|
128
|
+
/**
|
|
129
|
+
* Build a deterministic, human-readable SIWE-style sign-in challenge.
|
|
130
|
+
*
|
|
131
|
+
* The exact bytes signed are the UTF-8 encoding of this string (no wallet
|
|
132
|
+
* prefix — the wallet's `signMessage` signs the message as given):
|
|
133
|
+
*
|
|
134
|
+
* <domain> wants you to sign in with your X1 account:
|
|
135
|
+
* <address>
|
|
136
|
+
*
|
|
137
|
+
* <statement> ← omitted, with its blank line, when no statement
|
|
138
|
+
*
|
|
139
|
+
* URI: <uri>
|
|
140
|
+
* Version: 1
|
|
141
|
+
* Chain: X1
|
|
142
|
+
* Nonce: <nonce>
|
|
143
|
+
* Issued At: <ISO-8601>
|
|
144
|
+
* Expiration Time: <ISO-8601>
|
|
145
|
+
*
|
|
146
|
+
* @throws {SignInError} `invalid-params` for a bad domain, address, statement,
|
|
147
|
+
* nonce, or time.
|
|
148
|
+
*/
|
|
149
|
+
export declare function createSignInChallenge(params: SignInChallengeParams): SignInChallenge;
|
|
150
|
+
/** The fields recovered from a challenge string. */
|
|
151
|
+
export interface ParsedSignInChallenge {
|
|
152
|
+
readonly domain: string;
|
|
153
|
+
readonly address: string;
|
|
154
|
+
readonly uri: string;
|
|
155
|
+
readonly statement: string | null;
|
|
156
|
+
readonly nonce: string;
|
|
157
|
+
readonly issuedAt: number;
|
|
158
|
+
readonly expiresAt: number;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Parse a sign-in challenge string back to its fields, or null for anything
|
|
162
|
+
* that is not one — including a record-verification or control-proof challenge
|
|
163
|
+
* (both single-line, so they fail the header match). This is a pure structural
|
|
164
|
+
* parse; it proves nothing about the signature.
|
|
165
|
+
*/
|
|
166
|
+
export declare function parseSignInChallenge(challenge: string): ParsedSignInChallenge | null;
|
|
167
|
+
export interface VerifySignInParams {
|
|
168
|
+
/** The challenge string the wallet signed (built by {@link createSignInChallenge}). */
|
|
169
|
+
readonly challenge: string;
|
|
170
|
+
/** The address that supposedly signed, base58. Must match the challenge's. */
|
|
171
|
+
readonly address: string;
|
|
172
|
+
/** The 64-byte ed25519 signature, base58-encoded (what a wallet's
|
|
173
|
+
* `signMessage` output looks like once base58'd, the common wire form). */
|
|
174
|
+
readonly signatureBase58: string;
|
|
175
|
+
/** If given, require the challenge's domain to equal this — the relying
|
|
176
|
+
* party's own domain. Strongly recommended: it turns the signed-in domain
|
|
177
|
+
* binding into an *enforced* check against phishing. */
|
|
178
|
+
readonly expectedDomain?: string;
|
|
179
|
+
/** If given, require the challenge's nonce to equal this — the nonce the RP
|
|
180
|
+
* issued and is about to burn. */
|
|
181
|
+
readonly expectedNonce?: string;
|
|
182
|
+
/** `Date.now()` override, ms — for tests and deterministic demos. */
|
|
183
|
+
readonly now?: number;
|
|
184
|
+
/** Allowed clock skew in seconds for issued-at/expiry. Default 60. */
|
|
185
|
+
readonly clockSkewSec?: number;
|
|
186
|
+
/** Ed25519 verifier override for runtimes whose WebCrypto lacks Ed25519.
|
|
187
|
+
* Defaults to the SDK's RFC-8032-strict {@link verifyEd25519Strict}. */
|
|
188
|
+
readonly verifyEd25519?: (publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array) => Promise<boolean>;
|
|
189
|
+
}
|
|
190
|
+
export type SignInVerification = {
|
|
191
|
+
readonly ok: true;
|
|
192
|
+
/** The verified signer, base58 (canonical spelling). */
|
|
193
|
+
readonly address: string;
|
|
194
|
+
readonly domain: string;
|
|
195
|
+
readonly nonce: string;
|
|
196
|
+
readonly issuedAt: number;
|
|
197
|
+
readonly expiresAt: number;
|
|
198
|
+
} | {
|
|
199
|
+
readonly ok: false;
|
|
200
|
+
readonly reason: SignInFailureReason;
|
|
201
|
+
readonly message: string;
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Verify a sign-in proof: the signature is a valid ed25519 signature over the
|
|
205
|
+
* exact challenge bytes for `address`, the address is the one bound in the
|
|
206
|
+
* challenge, and `now` is inside `[issuedAt, expiresAt]` (± skew). Optionally
|
|
207
|
+
* enforces `expectedDomain` / `expectedNonce`.
|
|
208
|
+
*
|
|
209
|
+
* Verification outcomes come back as `{ ok: false, reason }` — this function
|
|
210
|
+
* never throws for one. It does NOT touch the chain: it proves only that the
|
|
211
|
+
* wallet signed. Whether that wallet currently owns a handle is a separate,
|
|
212
|
+
* on-chain question answered by {@link resolveIdentity}.
|
|
213
|
+
*
|
|
214
|
+
* Replay protection is nonce + expiry, and both need the relying party: pass
|
|
215
|
+
* `expectedNonce` (and never accept the same nonce twice), and keep the expiry
|
|
216
|
+
* window short. The domain binding needs `expectedDomain` to become a check.
|
|
217
|
+
*/
|
|
218
|
+
export declare function verifySignIn(params: VerifySignInParams): Promise<SignInVerification>;
|
|
219
|
+
/** One live "verified by [platform]" signal on the signed-in handle. */
|
|
220
|
+
export interface IdentityVerification {
|
|
221
|
+
/** Platform slug (`"x"`, `"discord"`, `"github"`, `"telegram"`) or the legacy
|
|
222
|
+
* `"dns"` / `"social"` kind names, or null for a kind this SDK does not know. */
|
|
223
|
+
readonly platform: string | null;
|
|
224
|
+
/** Raw attestation kind byte. */
|
|
225
|
+
readonly kind: number;
|
|
226
|
+
/** Unix seconds the attestation was (last) stamped. */
|
|
227
|
+
readonly attestedAt: number;
|
|
228
|
+
}
|
|
229
|
+
/** The friendly identity behind an address. */
|
|
230
|
+
export interface ResolvedIdentity {
|
|
231
|
+
/** The address resolved, base58 (canonical). */
|
|
232
|
+
readonly address: string;
|
|
233
|
+
/** The address's primary `@handle` (canonical, no `@`), or null when it has
|
|
234
|
+
* no trustworthy handle primary. An X1NS-only primary comes back as null
|
|
235
|
+
* here — this is the @handle identity surface. */
|
|
236
|
+
readonly handle: string | null;
|
|
237
|
+
/** Ready-to-render label: `@<handle>` or null. */
|
|
238
|
+
readonly display: string | null;
|
|
239
|
+
/** Whether the handle carries at least one LIVE verification (the blue-tick
|
|
240
|
+
* condition, #7145). False when there is no handle. Trust caveat: a true
|
|
241
|
+
* means x1id's rotatable attestor key vouched for an off-chain review — read
|
|
242
|
+
* it as "verified by x1id", not trustless proof. */
|
|
243
|
+
readonly verified: boolean;
|
|
244
|
+
/** The live verifications, if any. */
|
|
245
|
+
readonly verifications: readonly IdentityVerification[];
|
|
246
|
+
}
|
|
247
|
+
export interface IdentityConfig {
|
|
248
|
+
/** X1 RPC endpoint. */
|
|
249
|
+
readonly rpcUrl: string;
|
|
250
|
+
/** The WASM resolver the app already loaded (needed for PDA derivation). */
|
|
251
|
+
readonly wasm: import("./wasm.js").WasmResolver;
|
|
252
|
+
/** Optional fetch override for testing or custom transport. */
|
|
253
|
+
readonly fetchImpl?: typeof fetch;
|
|
254
|
+
/** @handle registry program id. Defaults to the canonical X1 deployment. */
|
|
255
|
+
readonly handleProgramId?: string;
|
|
256
|
+
/** Resolver cache TTL in ms (passed through to `createResolver`). */
|
|
257
|
+
readonly cacheTtlMs?: number;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Reverse-resolve an address to its primary `@handle` and verification summary.
|
|
261
|
+
*
|
|
262
|
+
* Reuses the SDK's own `createResolver().reverse()` — with every staleness and
|
|
263
|
+
* current-authority rule it enforces — for the handle, then
|
|
264
|
+
* {@link import("./attestation.js").fetchAttestations} for the "verified by"
|
|
265
|
+
* summary, read with the same epoch/`records_cleared_at` staleness rule the
|
|
266
|
+
* rest of the SDK applies. Nothing here re-derives a PDA or re-checks ownership
|
|
267
|
+
* by hand.
|
|
268
|
+
*
|
|
269
|
+
* @throws {import("./types.js").ResolveError} `rpc-error` for transport
|
|
270
|
+
* problems (a handle with no primary is not an error — `handle` is null).
|
|
271
|
+
*/
|
|
272
|
+
export declare function resolveIdentity(cfg: IdentityConfig, address: string): Promise<ResolvedIdentity>;
|
|
273
|
+
export interface SignInWithX1IDParams extends IdentityConfig {
|
|
274
|
+
/** The connected wallet's `signMessage`: signs the raw message bytes and
|
|
275
|
+
* returns the 64-byte ed25519 signature. Matches the X1ID app's wallet
|
|
276
|
+
* adapter (`app/src/lib/wallet` normalizes every provider to this shape). */
|
|
277
|
+
readonly signMessage: (message: Uint8Array) => Promise<Uint8Array>;
|
|
278
|
+
/** The connected wallet's address, base58 — known from wallet-connect before
|
|
279
|
+
* signing, bound into the challenge and used to verify. */
|
|
280
|
+
readonly address: string;
|
|
281
|
+
/** The relying party's domain. */
|
|
282
|
+
readonly domain: string;
|
|
283
|
+
readonly statement?: string;
|
|
284
|
+
readonly uri?: string;
|
|
285
|
+
readonly nonce?: string;
|
|
286
|
+
readonly issuedAt?: number;
|
|
287
|
+
readonly expiresInSec?: number;
|
|
288
|
+
/** Throw `no-handle` when the wallet has no primary @handle. Default true —
|
|
289
|
+
* "Sign in with X1ID" implies an X1ID. Set false to allow handle-less
|
|
290
|
+
* sign-in (returns `handle: null`). */
|
|
291
|
+
readonly requireHandle?: boolean;
|
|
292
|
+
readonly clockSkewSec?: number;
|
|
293
|
+
readonly now?: number;
|
|
294
|
+
readonly verifyEd25519?: VerifySignInParams["verifyEd25519"];
|
|
295
|
+
}
|
|
296
|
+
export interface SignInIdentity extends ResolvedIdentity {
|
|
297
|
+
/** The exact challenge that was signed — store it with the session if you
|
|
298
|
+
* want an audit trail of what the user agreed to. */
|
|
299
|
+
readonly challenge: string;
|
|
300
|
+
readonly nonce: string;
|
|
301
|
+
readonly issuedAt: number;
|
|
302
|
+
readonly expiresAt: number;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* The whole flow in one call: build a domain-bound challenge, have the wallet
|
|
306
|
+
* sign it, verify the signature, and resolve the address to its `@handle`
|
|
307
|
+
* identity. Returns the resolved identity on success.
|
|
308
|
+
*
|
|
309
|
+
* The relying party still owns the *session*: bind the returned identity to
|
|
310
|
+
* your own session and re-check ownership periodically — this call proves the
|
|
311
|
+
* signature at THIS moment, nothing longer-lived.
|
|
312
|
+
*
|
|
313
|
+
* @throws {SignInError} `wallet-error` if the wallet rejects/fails signing, a
|
|
314
|
+
* verification `SignInFailureReason` code if the signature does not verify, or
|
|
315
|
+
* `no-handle` when `requireHandle` (default) and the wallet has no primary
|
|
316
|
+
* handle.
|
|
317
|
+
*/
|
|
318
|
+
export declare function signInWithX1ID(params: SignInWithX1IDParams): Promise<SignInIdentity>;
|
package/dist/signin.js
ADDED
|
Binary file
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,43 @@ export type Verification =
|
|
|
11
11
|
"verified"
|
|
12
12
|
/** Record exists but ownership was never proved. Show a warning. */
|
|
13
13
|
| "unverified";
|
|
14
|
+
/**
|
|
15
|
+
* One per-platform verification a handle carries — the "verified by
|
|
16
|
+
* [platform]" signal (#7145). Shape mirrors the resolver's REST `/resolve`
|
|
17
|
+
* `verifications[]` entries exactly (snake_case `attested_at`), so an
|
|
18
|
+
* integrator can drop a decoded REST array straight onto
|
|
19
|
+
* {@link Resolved.verifications} with no transformation.
|
|
20
|
+
*
|
|
21
|
+
* # What this proves — and, honestly, what it does not
|
|
22
|
+
*
|
|
23
|
+
* A verification is an off-chain review (a DNS lookup, an OAuth completion, a
|
|
24
|
+
* human check) that x1id's **attestor key** signed off on at `attested_at`.
|
|
25
|
+
* It is NOT a trustless proof: the attestor key is admin-rotatable, so the
|
|
26
|
+
* trust anchor is "whoever the registry admin currently designates". Entries
|
|
27
|
+
* are also epoch-bound — the resolver emits an entry only while the underlying
|
|
28
|
+
* attestation is LIVE (not stale): `attested_at >= handle.registered_at` AND
|
|
29
|
+
* `>= handle.records_cleared_at`. Render it as "verified by x1id", never as
|
|
30
|
+
* more than that.
|
|
31
|
+
*/
|
|
32
|
+
export interface HandleVerification {
|
|
33
|
+
/**
|
|
34
|
+
* Platform slug: `"x"`, `"discord"`, `"github"`, `"telegram"`, `"domain"`
|
|
35
|
+
* (legacy DNS/kind 0), or `"social"` (legacy generic/kind 1). Match this
|
|
36
|
+
* against {@link platformSlug}'s output when joining to an attestation kind.
|
|
37
|
+
*/
|
|
38
|
+
readonly platform: string;
|
|
39
|
+
/**
|
|
40
|
+
* The paired text-record value for a live verification (e.g. `"@michelle"`
|
|
41
|
+
* for `x`, `"michelle.com"` for `domain`), or `null` when the text record
|
|
42
|
+
* is missing/stale or the platform carries none (legacy `social`).
|
|
43
|
+
*/
|
|
44
|
+
readonly value: string | null;
|
|
45
|
+
/** Whether this verification is live. The resolver only emits live ones,
|
|
46
|
+
* but readers must still honour the flag. */
|
|
47
|
+
readonly verified: boolean;
|
|
48
|
+
/** Unix seconds the attestation was (last) stamped. */
|
|
49
|
+
readonly attested_at: number;
|
|
50
|
+
}
|
|
14
51
|
/**
|
|
15
52
|
* A successfully resolved name.
|
|
16
53
|
*
|
|
@@ -31,6 +68,17 @@ export interface Resolved {
|
|
|
31
68
|
readonly chain: Chain;
|
|
32
69
|
/** Whether ownership of `address` was proved. */
|
|
33
70
|
readonly verification: Verification;
|
|
71
|
+
/**
|
|
72
|
+
* Per-platform "verified by [platform]" signals for this handle (#7145),
|
|
73
|
+
* when the producing resolver supplies them (the tools/api REST `/resolve`
|
|
74
|
+
* response carries a `verifications[]` field; the SDK's own on-chain
|
|
75
|
+
* `resolve()` does not populate this — read attestations directly via
|
|
76
|
+
* {@link fetchAttestations}). **Optional and additive**: an older resolver,
|
|
77
|
+
* or a handle with no live verifications, omits it. Never index into it
|
|
78
|
+
* blindly — use {@link isVerified} / {@link verifiedPlatforms} /
|
|
79
|
+
* {@link isVerifiedOn}, which tolerate its absence.
|
|
80
|
+
*/
|
|
81
|
+
readonly verifications?: readonly HandleVerification[];
|
|
34
82
|
}
|
|
35
83
|
/** Chains a handle can carry a record for. SLIP-44 based. */
|
|
36
84
|
export type Chain = "X1" | "SOL" | "ETH" | "BTC";
|
package/dist/x402.d.ts
CHANGED
|
@@ -14,19 +14,26 @@
|
|
|
14
14
|
* 2026-09-22 — NOT invented. See `docs/x402.md` for the full design,
|
|
15
15
|
* including what's explicitly OUT of scope here (a facilitator).
|
|
16
16
|
*
|
|
17
|
-
* # The
|
|
17
|
+
* # The network identifier — CAIP-30, for OSS interop (#8325)
|
|
18
18
|
*
|
|
19
|
-
* x402's
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
19
|
+
* x402's SVM networks use `"solana:<first-32-of-genesis>"` (CAIP-30: the
|
|
20
|
+
* `solana:` namespace + the first 32 base58 chars of the genesis hash). X1 is
|
|
21
|
+
* its own SVM chain, but `@x402/core`'s reference client/facilitator only route
|
|
22
|
+
* the `solana:*` scheme family — so to let ANY standard, unmodified x402 client
|
|
23
|
+
* or wallet pay an x1id agent out of the box, X1 testnet uses the CAIP-30 form
|
|
24
|
+
* keyed on X1's genesis. (Fortiswap's self-hosted X1 facilitator uses the
|
|
25
|
+
* identical id.) An earlier release used a principled `"x1:<full-genesis>"`
|
|
26
|
+
* extension; it was OSS-incompatible, so this migrated to CAIP-30 —
|
|
27
|
+
* {@link X1_LEGACY_TESTNET_NETWORK} is kept only for reading older manifests.
|
|
28
|
+
* Verified against `getGenesisHash` (full hash
|
|
29
|
+
* `C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWwT5iTdeaviDho`).
|
|
26
30
|
*/
|
|
27
|
-
/** X1 testnet's x402 network
|
|
28
|
-
*
|
|
29
|
-
export declare const X1_TESTNET_NETWORK = "
|
|
31
|
+
/** X1 testnet's x402 network id — CAIP-30 `"solana:" + first 32 chars of the
|
|
32
|
+
* genesis hash — so unmodified OSS x402 clients route to it (#8325). */
|
|
33
|
+
export declare const X1_TESTNET_NETWORK = "solana:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWw";
|
|
34
|
+
/** The pre-#8325 `"x1:<full-genesis>"` id. DEPRECATED — kept only so a reader
|
|
35
|
+
* can still recognise manifests published before the CAIP-30 migration. */
|
|
36
|
+
export declare const X1_LEGACY_TESTNET_NETWORK = "x1:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWwT5iTdeaviDho";
|
|
30
37
|
/** The x402 scheme x1id implements — "exact": pay a fixed amount of a named
|
|
31
38
|
* asset to a named recipient. (x402 also defines "upto" for usage-based
|
|
32
39
|
* pricing; not implemented here.) */
|
package/dist/x402.js
CHANGED
|
@@ -14,19 +14,26 @@
|
|
|
14
14
|
* 2026-09-22 — NOT invented. See `docs/x402.md` for the full design,
|
|
15
15
|
* including what's explicitly OUT of scope here (a facilitator).
|
|
16
16
|
*
|
|
17
|
-
* # The
|
|
17
|
+
* # The network identifier — CAIP-30, for OSS interop (#8325)
|
|
18
18
|
*
|
|
19
|
-
* x402's
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
19
|
+
* x402's SVM networks use `"solana:<first-32-of-genesis>"` (CAIP-30: the
|
|
20
|
+
* `solana:` namespace + the first 32 base58 chars of the genesis hash). X1 is
|
|
21
|
+
* its own SVM chain, but `@x402/core`'s reference client/facilitator only route
|
|
22
|
+
* the `solana:*` scheme family — so to let ANY standard, unmodified x402 client
|
|
23
|
+
* or wallet pay an x1id agent out of the box, X1 testnet uses the CAIP-30 form
|
|
24
|
+
* keyed on X1's genesis. (Fortiswap's self-hosted X1 facilitator uses the
|
|
25
|
+
* identical id.) An earlier release used a principled `"x1:<full-genesis>"`
|
|
26
|
+
* extension; it was OSS-incompatible, so this migrated to CAIP-30 —
|
|
27
|
+
* {@link X1_LEGACY_TESTNET_NETWORK} is kept only for reading older manifests.
|
|
28
|
+
* Verified against `getGenesisHash` (full hash
|
|
29
|
+
* `C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWwT5iTdeaviDho`).
|
|
26
30
|
*/
|
|
27
|
-
/** X1 testnet's x402 network
|
|
28
|
-
*
|
|
29
|
-
export const X1_TESTNET_NETWORK = "
|
|
31
|
+
/** X1 testnet's x402 network id — CAIP-30 `"solana:" + first 32 chars of the
|
|
32
|
+
* genesis hash — so unmodified OSS x402 clients route to it (#8325). */
|
|
33
|
+
export const X1_TESTNET_NETWORK = "solana:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWw";
|
|
34
|
+
/** The pre-#8325 `"x1:<full-genesis>"` id. DEPRECATED — kept only so a reader
|
|
35
|
+
* can still recognise manifests published before the CAIP-30 migration. */
|
|
36
|
+
export const X1_LEGACY_TESTNET_NETWORK = "x1:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWwT5iTdeaviDho";
|
|
30
37
|
/** The x402 scheme x1id implements — "exact": pay a fixed amount of a named
|
|
31
38
|
* asset to a named recipient. (x402 also defines "upto" for usage-based
|
|
32
39
|
* pricing; not implemented here.) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x1id/resolve",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Resolve @handles and X1NS names on X1. Never silently picks between namespaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"build": "tsc -p tsconfig.json",
|
|
26
26
|
"test": "node --test test/*.test.js",
|
|
27
27
|
"lint:package": "publint",
|
|
28
|
-
"prepublishOnly": "npm run build"
|
|
28
|
+
"prepublishOnly": "cargo build --release -p x1-resolve-wasm --target wasm32-unknown-unknown --manifest-path ../Cargo.toml && node ../web/verify-wasm.mjs wasm/x1_resolve_wasm.wasm ../target/wasm32-unknown-unknown/release/x1_resolve_wasm.wasm && npm run build"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
31
|
"x1",
|
|
@@ -59,12 +59,5 @@
|
|
|
59
59
|
"@solana/web3.js": {
|
|
60
60
|
"optional": true
|
|
61
61
|
}
|
|
62
|
-
},
|
|
63
|
-
"repository": {
|
|
64
|
-
"type": "git",
|
|
65
|
-
"url": "git+https://github.com/fortiblox/x1id-sdk.git"
|
|
66
|
-
},
|
|
67
|
-
"bugs": {
|
|
68
|
-
"url": "https://x1id.io"
|
|
69
62
|
}
|
|
70
63
|
}
|
|
Binary file
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Fortiblox
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|