@x1id/resolve 0.19.2 → 0.22.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 +83 -1
- package/dist/adminConfig.d.ts +17 -3
- package/dist/adminConfig.js +24 -1
- package/dist/connect.d.ts +20 -8
- package/dist/connect.js +34 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/namespacePricing.d.ts +207 -0
- package/dist/namespacePricing.js +378 -0
- package/dist/sha256.d.ts +10 -0
- package/dist/sha256.js +77 -0
- package/dist/siws.d.ts +310 -0
- package/dist/siws.js +607 -0
- package/dist/voucher.d.ts +27 -9
- package/dist/voucher.js +37 -16
- package/dist/x402.d.ts +70 -6
- package/dist/x402.js +158 -7
- package/package.json +1 -1
package/dist/siws.d.ts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign In With Solana (SIWS, the Wallet Standard `solana:signIn` feature)
|
|
3
|
+
* against the hosted X1ID Connect API (`POST /v1/auth/challenge` +
|
|
4
|
+
* `POST /v1/auth/verify-signin`).
|
|
5
|
+
*
|
|
6
|
+
* # Why SIWS
|
|
7
|
+
*
|
|
8
|
+
* `solana:signIn` lets the WALLET build and display a structured sign-in
|
|
9
|
+
* message (domain, address, statement, nonce, ...) instead of an opaque blob.
|
|
10
|
+
* It is also the most reliable wallet-control proof on X1 in practice:
|
|
11
|
+
* Backpack's injected script hijacks OTHER wallets' `signMessage` but not
|
|
12
|
+
* `signIn`. This is the same path developers.x1id.io uses
|
|
13
|
+
* (portal/src/lib/wallet/standardWallet.ts) — the input fields here mirror it.
|
|
14
|
+
*
|
|
15
|
+
* # Flow
|
|
16
|
+
*
|
|
17
|
+
* 1. `requestSignInChallenge` — `POST /v1/auth/challenge {wallet, mode:"signin"}`
|
|
18
|
+
* returns a single-use `nonce` bound to your API key and the wallet.
|
|
19
|
+
* 2. `buildSignInInput` — the `solana:signIn` input naming YOUR domain, the
|
|
20
|
+
* wallet address and that nonce.
|
|
21
|
+
* 3. the wallet signs the SIWS message it renders from that input.
|
|
22
|
+
* 4. `verifySolanaSignIn` — `POST /v1/auth/verify-signin` with the exact
|
|
23
|
+
* signed bytes (base64) and the 64-byte signature (base58). dev-api checks
|
|
24
|
+
* line 1 (`<domain> wants you to sign in with your Solana account:`, domain
|
|
25
|
+
* on its allowlist), line 2 (the challenged wallet), a `Nonce: <nonce>`
|
|
26
|
+
* line, and the ed25519 signature (raw bytes or the SIMD-0048 v0
|
|
27
|
+
* off-chain envelope). It returns a Bearer {@link ConnectSession}.
|
|
28
|
+
*
|
|
29
|
+
* `signInWithSolana` does all four. Framework-agnostic: it takes any Wallet
|
|
30
|
+
* Standard wallet object, with no wallet-adapter or web3.js dependency.
|
|
31
|
+
*
|
|
32
|
+
* # Fallbacks: `signInWithWallet`
|
|
33
|
+
*
|
|
34
|
+
* Not every wallet implements `solana:signIn`, and some implement it badly.
|
|
35
|
+
* `signInWithWallet` tries, in the same order as developers.x1id.io
|
|
36
|
+
* (portal/src/lib/wallet/standardWallet.ts `proveForPortalSession`):
|
|
37
|
+
*
|
|
38
|
+
* 1. SIWS (`solana:signIn` → `/v1/auth/verify-signin`);
|
|
39
|
+
* 2. a signed, NEVER-SENT memo transaction (`solana:signTransaction` →
|
|
40
|
+
* `/v1/auth/verify-tx`) whose only instruction is an SPL-Memo carrying the
|
|
41
|
+
* server's audience-bound `x1id-verify:<audience>:<nonce>` verbatim. With
|
|
42
|
+
* `signinFeePayer` set it uses dev-api's no-fee layout (display-only fee
|
|
43
|
+
* payer at signer index 0, the user at index 1); otherwise the user is the
|
|
44
|
+
* fee payer. Works for Ledger, which refuses message signing;
|
|
45
|
+
* 3. `solana:signMessage` over the server's v2 challenge string
|
|
46
|
+
* (`x1id-connect:v2:wallet-control:<audience>:<address>:<nonce>`) →
|
|
47
|
+
* `/v1/auth/verify`.
|
|
48
|
+
*
|
|
49
|
+
* A user rejection or a wallet timeout stops the chain (the user said no; a
|
|
50
|
+
* second prompt would be hostile). Any other failure moves to the next path.
|
|
51
|
+
* Challenge strings and memos are always signed exactly as the server issued
|
|
52
|
+
* them, never rebuilt here (R3-L2P-1 audience binding).
|
|
53
|
+
*
|
|
54
|
+
* The session proves WALLET CONTROL (source `"wallet"`); the wallet's primary
|
|
55
|
+
* `@handle`, if any, is reported as metadata and re-resolved on every
|
|
56
|
+
* `GET /v1/auth/session`.
|
|
57
|
+
*
|
|
58
|
+
* Not to be confused with {@link import("./signin.js").signInWithX1ID}: that is
|
|
59
|
+
* the server-less, relying-party-verified `@handle` proof. This module talks to
|
|
60
|
+
* the hosted API and gets a session back.
|
|
61
|
+
*/
|
|
62
|
+
import { type ConnectSession } from "./connect.js";
|
|
63
|
+
/**
|
|
64
|
+
* - `wallet-unsupported` — the wallet does not implement `solana:signIn` (or,
|
|
65
|
+
* for `signInWithWallet`, none of signIn / signTransaction / signMessage).
|
|
66
|
+
* - `user-rejected` — the user declined in the wallet.
|
|
67
|
+
* - `domain-not-allowed` — dev-api rejected the domain named in the message
|
|
68
|
+
* (it is not on the deployment's SIWS allowlist).
|
|
69
|
+
* - `nonce-expired` — the challenge expired, was already used, or is unknown;
|
|
70
|
+
* request a fresh one and retry.
|
|
71
|
+
* - `invalid-params` — bad input (missing apiKey/domain, injection in a field).
|
|
72
|
+
* - `wallet-timeout` — the wallet never answered.
|
|
73
|
+
* - `wallet-error` — the wallet failed or returned an unusable result.
|
|
74
|
+
* - `network` — the request did not complete (no fetch, timeout, non-JSON).
|
|
75
|
+
* - `api-error` — any other dev-api error; see `serverCode` / `status`.
|
|
76
|
+
*/
|
|
77
|
+
export type SiwsErrorCode = "wallet-unsupported" | "user-rejected" | "domain-not-allowed" | "nonce-expired" | "invalid-params" | "wallet-timeout" | "wallet-error" | "network" | "api-error";
|
|
78
|
+
export declare class SiwsError extends Error {
|
|
79
|
+
readonly code: SiwsErrorCode;
|
|
80
|
+
/** dev-api's own error code (e.g. `CHALLENGE_EXPIRED`), when it sent one. */
|
|
81
|
+
readonly serverCode?: string | undefined;
|
|
82
|
+
/** HTTP status, when the error came from a response. */
|
|
83
|
+
readonly status?: number | undefined;
|
|
84
|
+
constructor(code: SiwsErrorCode, message: string,
|
|
85
|
+
/** dev-api's own error code (e.g. `CHALLENGE_EXPIRED`), when it sent one. */
|
|
86
|
+
serverCode?: string | undefined,
|
|
87
|
+
/** HTTP status, when the error came from a response. */
|
|
88
|
+
status?: number | undefined);
|
|
89
|
+
}
|
|
90
|
+
/** Map a dev-api error response onto a {@link SiwsErrorCode}. */
|
|
91
|
+
export declare function siwsErrorCodeFor(serverCode: string | undefined, message: string | undefined): SiwsErrorCode;
|
|
92
|
+
/** `solana:signIn` input (Phantom's SIWS spec / @solana/wallet-standard-features). */
|
|
93
|
+
export interface SolanaSignInInput {
|
|
94
|
+
readonly domain?: string;
|
|
95
|
+
readonly address?: string;
|
|
96
|
+
readonly statement?: string;
|
|
97
|
+
readonly uri?: string;
|
|
98
|
+
readonly version?: string;
|
|
99
|
+
readonly chainId?: string;
|
|
100
|
+
readonly nonce?: string;
|
|
101
|
+
readonly issuedAt?: string;
|
|
102
|
+
readonly expirationTime?: string;
|
|
103
|
+
readonly notBefore?: string;
|
|
104
|
+
readonly requestId?: string;
|
|
105
|
+
readonly resources?: readonly string[];
|
|
106
|
+
}
|
|
107
|
+
export interface WalletStandardAccount {
|
|
108
|
+
readonly address: string;
|
|
109
|
+
readonly publicKey?: Uint8Array;
|
|
110
|
+
}
|
|
111
|
+
export interface SolanaSignInOutput {
|
|
112
|
+
readonly account: WalletStandardAccount;
|
|
113
|
+
readonly signedMessage: Uint8Array;
|
|
114
|
+
readonly signature: Uint8Array;
|
|
115
|
+
readonly signatureType?: string;
|
|
116
|
+
}
|
|
117
|
+
/** The slice of a Wallet Standard wallet this module uses. */
|
|
118
|
+
export interface SignInCapableWallet {
|
|
119
|
+
readonly name?: string;
|
|
120
|
+
readonly accounts?: readonly WalletStandardAccount[];
|
|
121
|
+
readonly features: Record<string, unknown>;
|
|
122
|
+
}
|
|
123
|
+
/** True if `wallet` implements `solana:signIn`. */
|
|
124
|
+
export declare function walletSupportsSignIn(wallet: SignInCapableWallet | null | undefined): boolean;
|
|
125
|
+
/** The SIWS message line-1 suffix dev-api's verifier matches on. */
|
|
126
|
+
export declare const SIWS_HEADER_SUFFIX: " wants you to sign in with your Solana account:";
|
|
127
|
+
/** Default statement shown in the wallet. */
|
|
128
|
+
export declare const DEFAULT_SIWS_STATEMENT: "Sign in with your wallet. This does not send a transaction.";
|
|
129
|
+
export interface BuildSignInInputParams {
|
|
130
|
+
/** The host the user is signing in to (e.g. `app.example.com`). Must be on
|
|
131
|
+
* your deployment's SIWS allowlist. Defaults to `globalThis.location.host`. */
|
|
132
|
+
readonly domain?: string;
|
|
133
|
+
/** The wallet address (base58) the challenge was issued for. */
|
|
134
|
+
readonly address: string;
|
|
135
|
+
/** The challenge nonce from {@link requestSignInChallenge}. */
|
|
136
|
+
readonly nonce: string;
|
|
137
|
+
/** Human-readable statement the wallet displays. Defaults to
|
|
138
|
+
* {@link DEFAULT_SIWS_STATEMENT}; pass `""` to omit it. */
|
|
139
|
+
readonly statement?: string;
|
|
140
|
+
/** Defaults to `globalThis.location.origin`, else `https://<domain>`. */
|
|
141
|
+
readonly uri?: string;
|
|
142
|
+
/** ISO-8601; defaults to now. */
|
|
143
|
+
readonly issuedAt?: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Build the Wallet Standard `solana:signIn` input for an X1ID challenge —
|
|
147
|
+
* exactly the fields developers.x1id.io sends: domain, address, statement, uri,
|
|
148
|
+
* version `"1"`, nonce, issuedAt.
|
|
149
|
+
*/
|
|
150
|
+
export declare function buildSignInInput(params: BuildSignInInputParams): SolanaSignInInput;
|
|
151
|
+
/**
|
|
152
|
+
* Render a `solana:signIn` input as SIWS message text, per the SIWS spec
|
|
153
|
+
* (identical to `createSignInMessageText` in @solana/wallet-standard-util).
|
|
154
|
+
* Wallets render this themselves; it is exported for tests, previews, and
|
|
155
|
+
* server-side expectations.
|
|
156
|
+
*/
|
|
157
|
+
export declare function buildSignInMessageText(input: SolanaSignInInput & {
|
|
158
|
+
domain: string;
|
|
159
|
+
address: string;
|
|
160
|
+
}): string;
|
|
161
|
+
export interface SiwsApiConfig {
|
|
162
|
+
/** Your X1ID integrator API key (sent as `x-api-key`). */
|
|
163
|
+
readonly apiKey: string;
|
|
164
|
+
/** API origin. Defaults to `https://api.x1id.io`; a trailing `/v1` is tolerated. */
|
|
165
|
+
readonly apiBase?: string;
|
|
166
|
+
/** Inject a `fetch` (tests, non-global-fetch runtimes). */
|
|
167
|
+
readonly fetchImpl?: typeof fetch;
|
|
168
|
+
/** Per-request timeout in ms (default 15_000). */
|
|
169
|
+
readonly timeoutMs?: number;
|
|
170
|
+
}
|
|
171
|
+
/** `POST /v1/auth/challenge` response for a `mode: "signin"` wallet challenge. */
|
|
172
|
+
export interface SignInChallengeResponse {
|
|
173
|
+
readonly nonce: string;
|
|
174
|
+
readonly handle: string | null;
|
|
175
|
+
/** The exact string to sign for the signMessage path
|
|
176
|
+
* (`x1id-connect:v2:wallet-control:<audience>:<address>:<nonce>`). */
|
|
177
|
+
readonly challenge: string;
|
|
178
|
+
/** The audience the signed bytes name (R3-L2P-1): your API key id, or the
|
|
179
|
+
* portal's domain for the portal key. Absent from a pre-#608 API. */
|
|
180
|
+
readonly audience?: string;
|
|
181
|
+
/** Tx mode only: the exact memo (`x1id-verify:<audience>:<nonce>`). */
|
|
182
|
+
readonly memo?: string;
|
|
183
|
+
readonly signature_encoding: string;
|
|
184
|
+
readonly expires_at: number;
|
|
185
|
+
readonly proves: string;
|
|
186
|
+
readonly disclaimer: string;
|
|
187
|
+
}
|
|
188
|
+
/** Body of `POST /v1/auth/verify-signin`. */
|
|
189
|
+
export interface VerifySignInBody {
|
|
190
|
+
readonly nonce: string;
|
|
191
|
+
/** base64 of the EXACT bytes the wallet signed. */
|
|
192
|
+
readonly signedMessage: string;
|
|
193
|
+
/** base58 of the 64-byte ed25519 signature. */
|
|
194
|
+
readonly signature: string;
|
|
195
|
+
}
|
|
196
|
+
/** `POST /v1/auth/challenge {wallet, mode: "signin"}` — a single-use nonce. */
|
|
197
|
+
export declare function requestSignInChallenge(params: SiwsApiConfig & {
|
|
198
|
+
readonly wallet: string;
|
|
199
|
+
}): Promise<SignInChallengeResponse>;
|
|
200
|
+
/**
|
|
201
|
+
* `POST /v1/auth/challenge {wallet, mode?}` for any wallet-proof mode:
|
|
202
|
+
* `"signin"` (SIWS), `"tx"` (memo transaction; the response carries `memo`),
|
|
203
|
+
* or omitted (signMessage over `challenge`).
|
|
204
|
+
*/
|
|
205
|
+
export declare function requestConnectChallenge(params: SiwsApiConfig & {
|
|
206
|
+
readonly wallet: string;
|
|
207
|
+
readonly mode?: "signin" | "tx";
|
|
208
|
+
}): Promise<SignInChallengeResponse>;
|
|
209
|
+
/**
|
|
210
|
+
* `POST /v1/auth/verify-signin` — the raw verify call. Accepts the wallet's
|
|
211
|
+
* byte outputs and encodes them as dev-api expects (signedMessage base64,
|
|
212
|
+
* signature base58), or pre-encoded strings passed through as-is.
|
|
213
|
+
*/
|
|
214
|
+
export declare function verifySolanaSignIn(params: SiwsApiConfig & {
|
|
215
|
+
readonly nonce: string;
|
|
216
|
+
readonly signedMessage: Uint8Array | string;
|
|
217
|
+
readonly signature: Uint8Array | string;
|
|
218
|
+
}): Promise<ConnectSession>;
|
|
219
|
+
/** `POST /v1/auth/verify-tx` with the fully signed memo transaction (base64). */
|
|
220
|
+
export declare function verifySignInTransaction(params: SiwsApiConfig & {
|
|
221
|
+
readonly nonce: string;
|
|
222
|
+
readonly signedTransaction: Uint8Array | string;
|
|
223
|
+
}): Promise<ConnectSession>;
|
|
224
|
+
/** `POST /v1/auth/verify` with a signMessage signature over the challenge
|
|
225
|
+
* string (signature base58, `signer` = the wallet address). */
|
|
226
|
+
export declare function verifySignInMessage(params: SiwsApiConfig & {
|
|
227
|
+
readonly nonce: string;
|
|
228
|
+
readonly signature: Uint8Array | string;
|
|
229
|
+
readonly signer: string;
|
|
230
|
+
}): Promise<ConnectSession>;
|
|
231
|
+
/** X1ID's display-only sign-in fee payer on X1 MAINNET (System-owned, tiny
|
|
232
|
+
* balance so wallet simulations pass; it never signs and the tx is never
|
|
233
|
+
* sent). Pass it as `signinFeePayer` only against a deployment whose
|
|
234
|
+
* `CONNECT_SIGNIN_FEE_PAYER` is this address (api.x1id.io on mainnet). */
|
|
235
|
+
export declare const X1ID_MAINNET_SIGNIN_FEE_PAYER: "9kFEzXbNBJLTsvZwrTAcpxr6iLRqHPyu9yp8K4m2YwC3";
|
|
236
|
+
export interface BuildSignInMemoTransactionParams {
|
|
237
|
+
/** The signing wallet (base58). */
|
|
238
|
+
readonly user: string;
|
|
239
|
+
/** The memo, verbatim from the challenge (`x1id-verify:<audience>:<nonce>`). */
|
|
240
|
+
readonly memo: string;
|
|
241
|
+
/** A recent blockhash; defaults to the all-ones placeholder (never sent). */
|
|
242
|
+
readonly blockhash?: string;
|
|
243
|
+
/** Display-only fee payer for dev-api's no-fee layout (user = signer index 1).
|
|
244
|
+
* Null/omitted = the user pays and signs alone (always accepted). */
|
|
245
|
+
readonly feePayer?: string | null;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* The unsigned, serialized LEGACY memo-only transaction the wallet signs for
|
|
249
|
+
* `/v1/auth/verify-tx`: exactly one SPL-Memo instruction carrying `memo`, with
|
|
250
|
+
* the user as the memo's signer. Byte-identical to the portal's
|
|
251
|
+
* `buildMemoTx` (portal/src/lib/wallet/memoTx.ts). It is never broadcast.
|
|
252
|
+
*/
|
|
253
|
+
export declare function buildSignInMemoTransaction(params: BuildSignInMemoTransactionParams): Uint8Array;
|
|
254
|
+
export interface SignInWithSolanaParams extends SiwsApiConfig {
|
|
255
|
+
/** A Wallet Standard wallet implementing `solana:signIn`. */
|
|
256
|
+
readonly wallet: SignInCapableWallet;
|
|
257
|
+
/** The account to sign in with. Defaults to the wallet's first account
|
|
258
|
+
* (connecting via `standard:connect` first if it has none). */
|
|
259
|
+
readonly account?: WalletStandardAccount;
|
|
260
|
+
/** Defaults to `globalThis.location.host`. */
|
|
261
|
+
readonly domain?: string;
|
|
262
|
+
readonly statement?: string;
|
|
263
|
+
readonly uri?: string;
|
|
264
|
+
/** How long to wait for the wallet, ms (default 60_000). */
|
|
265
|
+
readonly signTimeoutMs?: number;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Sign in with a Wallet Standard wallet via `solana:signIn` ONLY and return the
|
|
269
|
+
* X1ID Connect session. Throws {@link SiwsError} (`wallet-unsupported` when the
|
|
270
|
+
* wallet lacks `solana:signIn`). For automatic fallbacks use
|
|
271
|
+
* {@link signInWithWallet}.
|
|
272
|
+
*
|
|
273
|
+
* ```ts
|
|
274
|
+
* const session = await signInWithSolana({ wallet, apiKey: "x1id_..." });
|
|
275
|
+
* // session.token → `Authorization: Bearer …`; session.address; session.handle
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
export declare function signInWithSolana(params: SignInWithSolanaParams): Promise<ConnectSession>;
|
|
279
|
+
export type SignInMethod = "siws" | "memo-tx" | "sign-message";
|
|
280
|
+
export interface SignInWithWalletParams extends SignInWithSolanaParams {
|
|
281
|
+
/** Paths to try, in order. Default: all three, SIWS → memo tx → signMessage
|
|
282
|
+
* (the developers.x1id.io order). */
|
|
283
|
+
readonly methods?: readonly SignInMethod[];
|
|
284
|
+
/** Memo path: display-only fee payer for dev-api's no-fee layout. Must equal
|
|
285
|
+
* the deployment's CONNECT_SIGNIN_FEE_PAYER (on api.x1id.io mainnet:
|
|
286
|
+
* {@link X1ID_MAINNET_SIGNIN_FEE_PAYER}). Default null: the user is the fee
|
|
287
|
+
* payer (accepted by every deployment; the tx is never sent either way). */
|
|
288
|
+
readonly signinFeePayer?: string | null;
|
|
289
|
+
/** Memo path: an RPC to fetch a recent blockhash from (wallets simulate the
|
|
290
|
+
* transaction before signing, and some refuse a stale one). */
|
|
291
|
+
readonly rpcUrl?: string;
|
|
292
|
+
/** Memo path: a blockhash to use as-is (overrides `rpcUrl`). Default: the
|
|
293
|
+
* all-ones placeholder. */
|
|
294
|
+
readonly blockhash?: string;
|
|
295
|
+
/** Called before each path is tried (for UI copy / telemetry). */
|
|
296
|
+
readonly onAttempt?: (method: SignInMethod) => void;
|
|
297
|
+
}
|
|
298
|
+
/** A session plus the path that produced it. */
|
|
299
|
+
export type WalletSignInResult = ConnectSession & {
|
|
300
|
+
readonly method: SignInMethod;
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* Sign in with any Wallet Standard wallet, falling back across the three
|
|
304
|
+
* wallet-control proofs the hosted API accepts: SIWS → never-sent memo
|
|
305
|
+
* transaction → signMessage (see the module docs). A user rejection or a
|
|
306
|
+
* wallet timeout stops immediately; otherwise the LAST path's error is thrown
|
|
307
|
+
* if every path fails. `wallet-unsupported` when the wallet implements none of
|
|
308
|
+
* `solana:signIn`, `solana:signTransaction`, `solana:signMessage`.
|
|
309
|
+
*/
|
|
310
|
+
export declare function signInWithWallet(params: SignInWithWalletParams): Promise<WalletSignInResult>;
|