@x1id/resolve 0.15.0 → 0.16.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/dist/signin.js CHANGED
@@ -78,6 +78,14 @@ export class SignInError extends Error {
78
78
  * belt-and-suspenders CR/LF guard in {@link createSignInChallenge} mirrors the
79
79
  * `uri`/`statement` fields and is the real anti-injection control. */
80
80
  const DOMAIN_RE = /^[A-Za-z0-9.-]{1,253}(:\d{1,5})?$/;
81
+ /** Any character a line-oriented parser could mistake for a boundary: every C0
82
+ * control (so `\n`, `\r`, `\t`, and the rest), DEL, and — the trap the old
83
+ * `\n`/`\r`-only guard missed — the Unicode line/paragraph separators
84
+ * U+2028/U+2029, which `^`/`$` under the regex `m` flag honor just like `\n`
85
+ * (see {@link parseSignInChallenge}'s field parser). FORBIDDEN in every value
86
+ * interpolated into a challenge line (domain, statement, uri) so a crafted
87
+ * value can never forge a second line — challenge-line injection (audit L5). */
88
+ const FORBIDDEN_LINE_CHAR_RE = /[\u0000-\u001f\u007f\u2028\u2029]/;
81
89
  /** Nonce charset — ASCII alphanumeric, matching the rest of the SDK's nonces so
82
90
  * it can never contain a separator or a newline. SIWE requires >= 8 chars. */
83
91
  const NONCE_RE = /^[0-9A-Za-z]{8,128}$/;
@@ -124,11 +132,14 @@ export function generateSignInNonce(bytes = 16) {
124
132
  */
125
133
  export function createSignInChallenge(params) {
126
134
  const { domain } = params;
127
- // Explicit CR/LF guard first — mirrors the `uri`/`statement` fields below and
128
- // makes the anti-challenge-injection intent impossible to regress even if the
129
- // charset in DOMAIN_RE is ever loosened.
130
- if (typeof domain !== "string" || domain.includes("\n") || domain.includes("\r") || !DOMAIN_RE.test(domain)) {
131
- throw new SignInError("invalid-params", "domain must be a single DNS host (letters, digits, '.', '-'), no newlines");
135
+ // Explicit control/line-terminator guard first — mirrors the `uri`/`statement`
136
+ // fields below and makes the anti-challenge-injection intent impossible to
137
+ // regress even if the charset in DOMAIN_RE is ever loosened. DOMAIN_RE already
138
+ // admits only `[A-Za-z0-9.-]` (+ optional `:port`), so this is belt-and-
139
+ // suspenders — but it is the control that catches EVERY forbidden boundary
140
+ // char (incl. `\t`, U+2028/U+2029), not just `\n`/`\r` (audit L5).
141
+ if (typeof domain !== "string" || FORBIDDEN_LINE_CHAR_RE.test(domain) || !DOMAIN_RE.test(domain)) {
142
+ throw new SignInError("invalid-params", "domain must be a single DNS host (letters, digits, '.', '-'), no control characters");
132
143
  }
133
144
  if (!decodeBase58_32(params.address)) {
134
145
  throw new SignInError("invalid-params", `"${params.address}" is not a valid X1 address`);
@@ -137,12 +148,12 @@ export function createSignInChallenge(params) {
137
148
  // the signer and the verifier both compare against.
138
149
  const address = encodeBase58(decodeBase58_32(params.address));
139
150
  const statement = params.statement ?? null;
140
- if (statement !== null && (statement.includes("\n") || statement.includes("\r") || statement.length === 0)) {
141
- throw new SignInError("invalid-params", "statement must be a single non-empty line");
151
+ if (statement !== null && (FORBIDDEN_LINE_CHAR_RE.test(statement) || statement.length === 0)) {
152
+ throw new SignInError("invalid-params", "statement must be a single non-empty line (no control characters)");
142
153
  }
143
154
  const uri = params.uri ?? `https://${domain}`;
144
- if (uri.includes("\n") || uri.includes("\r")) {
145
- throw new SignInError("invalid-params", "uri must be a single line");
155
+ if (FORBIDDEN_LINE_CHAR_RE.test(uri)) {
156
+ throw new SignInError("invalid-params", "uri must be a single line (no control characters)");
146
157
  }
147
158
  const nonce = params.nonce ?? generateSignInNonce();
148
159
  if (!NONCE_RE.test(nonce)) {
@@ -362,7 +373,7 @@ export async function resolveIdentity(cfg, address) {
362
373
  const info = await reader.accountInfo(pda);
363
374
  const handleAcct = info ? parseHandleAccount(info.data) : null;
364
375
  if (handleAcct) {
365
- const all = await fetchAttestations(reader.rpc, encodeBase58(programBytes), pda, handleAcct.registeredAt, handleAcct.recordsClearedAt);
376
+ const all = await fetchAttestations(reader.rpc, encodeBase58(programBytes), pda, handleAcct.registeredAt, handleAcct.recordsClearedAt, handleAcct.holderEpoch);
366
377
  const live = liveAttestations(all);
367
378
  verified = live.length > 0;
368
379
  verifications = live.map((a) => ({
package/dist/voucher.d.ts CHANGED
@@ -18,11 +18,12 @@
18
18
  *
19
19
  * # Lifecycle (mirror of lib.rs)
20
20
  *
21
- * - `create_voucher(name, handleType, recipient, expiresAt)` escrows the mint
22
- * price (a snapshot of the live price) into `["voucher", name]` and reserves
23
- * the voucher slot. `recipient` bound ⇒ the claim registers the handle to
24
- * that wallet no matter who submits; `null` ⇒ open-code (the claimer owns
25
- * it). An optional integrator referrer (the same trailing accounts as
21
+ * - `create_voucher(name, handleType, recipient, expiresAt, claimSecretHash)`
22
+ * escrows the mint price (a snapshot of the live price) into
23
+ * `["voucher", name]` and reserves the voucher slot. `recipient` bound ⇒ the
24
+ * claim registers the handle to that wallet no matter who submits; `null` ⇒
25
+ * open-code (the claimer owns it) and `claimSecretHash` (= `sha256(secret)`)
26
+ * is REQUIRED (M72) — the claimer must present the matching `secret`. An optional integrator referrer (the same trailing accounts as
26
27
  * `register`, from `integrator.ts`) is snapshotted so the claim can split
27
28
  * the escrow. `expiresAt` must be strictly in the future.
28
29
  * - `claim_voucher(name)` registers the handle, drawing the fee from the
@@ -52,6 +53,20 @@ export declare const HandleType: {
52
53
  readonly Agent: 3;
53
54
  };
54
55
  export type HandleTypeValue = (typeof HandleType)[keyof typeof HandleType];
56
+ /**
57
+ * 32 bytes of fresh CSPRNG output — an OPEN gift voucher's claim secret (M72).
58
+ * Keep it OUT of the path/query (it belongs in the gift link's URL *fragment*,
59
+ * which is never sent to a server); its `sha256` ({@link giftSecretHash}) is
60
+ * what goes on chain as `claim_secret_hash`.
61
+ */
62
+ export declare function giftSecret(): Uint8Array;
63
+ /**
64
+ * `sha256(secret)` — the 32-byte `claim_secret_hash` an OPEN voucher stores at
65
+ * create and the program recomputes from the claimer's `claim_secret` (M72;
66
+ * mismatch ⇒ `BadClaimSecret`, 6105). WebCrypto, so async — mirrors
67
+ * commitReveal's `commitmentHash`.
68
+ */
69
+ export declare function giftSecretHash(secret: Uint8Array): Promise<Uint8Array>;
55
70
  /** Decoded `PrepaidVoucher` account. */
56
71
  export interface PrepaidVoucher {
57
72
  /** Who funded it and gets the refund (base58). */
@@ -69,12 +84,19 @@ export interface PrepaidVoucher {
69
84
  readonly integratorRateBps: number;
70
85
  readonly expiresAt: bigint;
71
86
  readonly createdAt: bigint;
87
+ /** The `sha256(claim_secret)` an OPEN voucher stores (M72), or null for a
88
+ * bound voucher (and for legacy pre-M72 vouchers). An open voucher's claim
89
+ * must present a `claim_secret` whose sha256 equals this. */
90
+ readonly claimSecretHash: Uint8Array | null;
72
91
  readonly bump: number;
73
92
  }
74
93
  /**
75
94
  * Decode a `PrepaidVoucher` account's raw data, or null if not one. Parsed
76
- * sequentially (Borsh): the two `Option<Pubkey>` fields are 1 byte when
77
- * absent, 33 when present, so fixed offsets after them would be wrong.
95
+ * sequentially (Borsh): the two `Option<Pubkey>` fields AND the M72
96
+ * `Option<[u8;32]> claim_secret_hash` are 1 byte when absent, 33 when present,
97
+ * so fixed offsets after them would be wrong. `claim_secret_hash` sits AFTER
98
+ * `created_at` and BEFORE `bump` (+33 bytes at its Some size — the Anchor
99
+ * INIT_SPACE voucher grew 167 → 200 B).
78
100
  */
79
101
  export declare function decodePrepaidVoucher(data: Uint8Array): PrepaidVoucher | null;
80
102
  export interface CreateVoucherParams {
@@ -94,10 +116,17 @@ export interface CreateVoucherParams {
94
116
  readonly recipient?: AddressLike | null;
95
117
  /** Unix seconds; must be strictly in the future. */
96
118
  readonly expiresAt: bigint | number;
119
+ /** `sha256(claim_secret)` (32 bytes) for an OPEN gift (M72) — REQUIRED when
120
+ * `recipient` is null (else the program fails `OpenVoucherNeedsSecret`,
121
+ * 6104). Ignored/stored None for a bound recipient; pass null (the default)
122
+ * for a bound gift. Compute it with {@link giftSecretHash}. */
123
+ readonly claimSecretHash?: Uint8Array | null;
97
124
  /** Optional integrator pair from `integratorAccountsForCreateVoucher`. */
98
125
  readonly integratorAccounts?: readonly InstructionKey[];
99
126
  }
100
- /** Build `create_voucher`. */
127
+ /** Build `create_voucher`. Args (Borsh): `name, handle_type, recipient:
128
+ * Option<Pubkey>, expires_at: i64, claim_secret_hash: Option<[u8;32]>` — the
129
+ * M72 hash is APPENDED LAST. */
101
130
  export declare function buildCreateVoucherIx(p: CreateVoucherParams): BuiltInstruction;
102
131
  export interface ClaimVoucherParams {
103
132
  readonly programId: AddressLike;
@@ -116,8 +145,14 @@ export interface ClaimVoucherParams {
116
145
  /** The integrator wallet — REQUIRED iff the voucher carries one; must equal
117
146
  * the voucher's `integrator`. Omit otherwise. */
118
147
  readonly integrator?: AddressLike;
148
+ /** The 32-byte claim secret for an OPEN voucher (M72) — its `sha256` must
149
+ * equal the voucher's stored `claim_secret_hash` (else `BadClaimSecret`,
150
+ * 6105). Read it from the gift link's URL fragment. Null/omit for a bound
151
+ * voucher (the program ignores it; sent as Borsh `None`). */
152
+ readonly claimSecret?: Uint8Array | null;
119
153
  }
120
- /** Build `claim_voucher`. */
154
+ /** Build `claim_voucher`. Args (Borsh): `name, claim_secret: Option<[u8;32]>`
155
+ * — the M72 secret is APPENDED LAST. */
121
156
  export declare function buildClaimVoucherIx(p: ClaimVoucherParams): BuiltInstruction;
122
157
  export interface RefundVoucherParams {
123
158
  readonly programId: AddressLike;
@@ -166,6 +201,11 @@ export interface ClaimVoucherTokenizedParams {
166
201
  /** The integrator wallet — REQUIRED iff the voucher carries one; must equal
167
202
  * the voucher's `integrator`. Omit otherwise. */
168
203
  readonly integrator?: AddressLike;
204
+ /** The 32-byte claim secret for an OPEN voucher (M72) — its `sha256` must
205
+ * equal the voucher's stored `claim_secret_hash` (else `BadClaimSecret`,
206
+ * 6105). Read it from the gift link's URL fragment. Null/omit for a bound
207
+ * voucher (sent as Borsh `None`). */
208
+ readonly claimSecret?: Uint8Array | null;
169
209
  }
170
210
  /**
171
211
  * Build `claim_voucher_tokenized` (#8335) — the tokenized sibling of
package/dist/voucher.js CHANGED
@@ -18,11 +18,12 @@
18
18
  *
19
19
  * # Lifecycle (mirror of lib.rs)
20
20
  *
21
- * - `create_voucher(name, handleType, recipient, expiresAt)` escrows the mint
22
- * price (a snapshot of the live price) into `["voucher", name]` and reserves
23
- * the voucher slot. `recipient` bound ⇒ the claim registers the handle to
24
- * that wallet no matter who submits; `null` ⇒ open-code (the claimer owns
25
- * it). An optional integrator referrer (the same trailing accounts as
21
+ * - `create_voucher(name, handleType, recipient, expiresAt, claimSecretHash)`
22
+ * escrows the mint price (a snapshot of the live price) into
23
+ * `["voucher", name]` and reserves the voucher slot. `recipient` bound ⇒ the
24
+ * claim registers the handle to that wallet no matter who submits; `null` ⇒
25
+ * open-code (the claimer owns it) and `claimSecretHash` (= `sha256(secret)`)
26
+ * is REQUIRED (M72) — the claimer must present the matching `secret`. An optional integrator referrer (the same trailing accounts as
26
27
  * `register`, from `integrator.ts`) is snapshotted so the claim can split
27
28
  * the escrow. `expiresAt` must be strictly in the future.
28
29
  * - `claim_voucher(name)` registers the handle, drawing the fee from the
@@ -74,13 +75,49 @@ function encU64(v) {
74
75
  new DataView(out.buffer).setBigInt64(0, v, true);
75
76
  return out;
76
77
  }
78
+ /** Borsh `Option<[u8;32]>`: tag `0x00` (None) or `0x01 ‖ 32 bytes` (Some) —
79
+ * the `claim_secret_hash` on create and the `claim_secret` on claim (M72). */
80
+ function encOption32(v) {
81
+ if (v === null)
82
+ return Uint8Array.from([0]);
83
+ if (v.length !== 32)
84
+ throw new Error("claim secret / hash must be exactly 32 bytes");
85
+ const out = new Uint8Array(33);
86
+ out[0] = 1;
87
+ out.set(v, 1);
88
+ return out;
89
+ }
90
+ /**
91
+ * 32 bytes of fresh CSPRNG output — an OPEN gift voucher's claim secret (M72).
92
+ * Keep it OUT of the path/query (it belongs in the gift link's URL *fragment*,
93
+ * which is never sent to a server); its `sha256` ({@link giftSecretHash}) is
94
+ * what goes on chain as `claim_secret_hash`.
95
+ */
96
+ export function giftSecret() {
97
+ return crypto.getRandomValues(new Uint8Array(32));
98
+ }
99
+ /**
100
+ * `sha256(secret)` — the 32-byte `claim_secret_hash` an OPEN voucher stores at
101
+ * create and the program recomputes from the claimer's `claim_secret` (M72;
102
+ * mismatch ⇒ `BadClaimSecret`, 6105). WebCrypto, so async — mirrors
103
+ * commitReveal's `commitmentHash`.
104
+ */
105
+ export async function giftSecretHash(secret) {
106
+ if (secret.length !== 32)
107
+ throw new Error("gift secret must be exactly 32 bytes");
108
+ const digest = await crypto.subtle.digest("SHA-256", secret);
109
+ return new Uint8Array(digest);
110
+ }
77
111
  /**
78
112
  * Decode a `PrepaidVoucher` account's raw data, or null if not one. Parsed
79
- * sequentially (Borsh): the two `Option<Pubkey>` fields are 1 byte when
80
- * absent, 33 when present, so fixed offsets after them would be wrong.
113
+ * sequentially (Borsh): the two `Option<Pubkey>` fields AND the M72
114
+ * `Option<[u8;32]> claim_secret_hash` are 1 byte when absent, 33 when present,
115
+ * so fixed offsets after them would be wrong. `claim_secret_hash` sits AFTER
116
+ * `created_at` and BEFORE `bump` (+33 bytes at its Some size — the Anchor
117
+ * INIT_SPACE voucher grew 167 → 200 B).
81
118
  */
82
119
  export function decodePrepaidVoucher(data) {
83
- if (data.length < 8 + 32 + 32 + 1 + 1 + 8 + 1 + 1 + 2 + 8 + 8 + 1)
120
+ if (data.length < 8 + 32 + 32 + 1 + 1 + 8 + 1 + 1 + 2 + 8 + 8 + 1 + 1)
84
121
  return null;
85
122
  for (let i = 0; i < 8; i++)
86
123
  if (data[i] !== PREPAID_VOUCHER_DISCRIMINATOR[i])
@@ -118,15 +155,25 @@ export function decodePrepaidVoucher(data) {
118
155
  o += 8;
119
156
  const createdAt = view.getBigInt64(o, true);
120
157
  o += 8;
158
+ const claimSecretTag = data[o];
159
+ o += 1;
160
+ let claimSecretHash = null;
161
+ if (claimSecretTag === 1) {
162
+ claimSecretHash = data.slice(o, o + 32);
163
+ o += 32;
164
+ }
121
165
  const bump = data[o];
122
- return { payer, name, handleType, amount, recipient, integrator, integratorRateBps, expiresAt, createdAt, bump };
166
+ return { payer, name, handleType, amount, recipient, integrator, integratorRateBps, expiresAt, createdAt, claimSecretHash, bump };
123
167
  }
124
- /** Build `create_voucher`. */
168
+ /** Build `create_voucher`. Args (Borsh): `name, handle_type, recipient:
169
+ * Option<Pubkey>, expires_at: i64, claim_secret_hash: Option<[u8;32]>` — the
170
+ * M72 hash is APPENDED LAST. */
125
171
  export function buildCreateVoucherIx(p) {
126
172
  const name = encString(p.name);
127
173
  const recipient = p.recipient ?? null;
128
174
  const recBytes = recipient === null ? Uint8Array.from([0]) : Uint8Array.from([1, ...toBytes32(recipient, "recipient")]);
129
- const data = new Uint8Array(8 + name.length + 1 + recBytes.length + 8);
175
+ const hashBytes = encOption32(p.claimSecretHash ?? null);
176
+ const data = new Uint8Array(8 + name.length + 1 + recBytes.length + 8 + hashBytes.length);
130
177
  let o = 0;
131
178
  data.set(CREATE_VOUCHER_DISCRIMINATOR, o);
132
179
  o += 8;
@@ -137,6 +184,8 @@ export function buildCreateVoucherIx(p) {
137
184
  data.set(recBytes, o);
138
185
  o += recBytes.length;
139
186
  data.set(encU64(BigInt(p.expiresAt)), o);
187
+ o += 8;
188
+ data.set(hashBytes, o);
140
189
  const keys = [
141
190
  { pubkey: toBase58(p.payer, "payer"), isSigner: true, isWritable: true },
142
191
  { pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: false },
@@ -148,12 +197,15 @@ export function buildCreateVoucherIx(p) {
148
197
  keys.push(...p.integratorAccounts);
149
198
  return { programId: toBase58(p.programId, "programId"), keys, data };
150
199
  }
151
- /** Build `claim_voucher`. */
200
+ /** Build `claim_voucher`. Args (Borsh): `name, claim_secret: Option<[u8;32]>`
201
+ * — the M72 secret is APPENDED LAST. */
152
202
  export function buildClaimVoucherIx(p) {
153
203
  const name = encString(p.name);
154
- const data = new Uint8Array(8 + name.length);
204
+ const secretBytes = encOption32(p.claimSecret ?? null);
205
+ const data = new Uint8Array(8 + name.length + secretBytes.length);
155
206
  data.set(CLAIM_VOUCHER_DISCRIMINATOR, 0);
156
207
  data.set(name, 8);
208
+ data.set(secretBytes, 8 + name.length);
157
209
  const keys = [
158
210
  { pubkey: toBase58(p.claimer, "claimer"), isSigner: true, isWritable: true },
159
211
  { pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: true },
@@ -207,9 +259,11 @@ const SYSVAR_INSTRUCTIONS = "Sysvar1nstructions1111111111111111111111111";
207
259
  */
208
260
  export function buildClaimVoucherTokenizedIx(p) {
209
261
  const name = encString(p.name);
210
- const data = new Uint8Array(8 + name.length);
262
+ const secretBytes = encOption32(p.claimSecret ?? null);
263
+ const data = new Uint8Array(8 + name.length + secretBytes.length);
211
264
  data.set(CLAIM_VOUCHER_TOKENIZED_DISCRIMINATOR, 0);
212
265
  data.set(name, 8);
266
+ data.set(secretBytes, 8 + name.length);
213
267
  const keys = [
214
268
  { pubkey: toBase58(p.claimer, "claimer"), isSigner: true, isWritable: true },
215
269
  { pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: true },
package/dist/x402.d.ts CHANGED
@@ -31,6 +31,18 @@
31
31
  /** X1 testnet's x402 network id — CAIP-30 `"solana:" + first 32 chars of the
32
32
  * genesis hash — so unmodified OSS x402 clients route to it (#8325). */
33
33
  export declare const X1_TESTNET_NETWORK = "solana:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWw";
34
+ /** X1 MAINNET's x402 network id — CAIP-30 `"solana:" + first 32 chars of the
35
+ * mainnet genesis hash `4SvBP3omtvcCVWdxq1zBY5cDp4wndjsThb6nEMn6iMdN`, so
36
+ * unmodified OSS x402 clients route to it. Verified against `getGenesisHash`
37
+ * and the live `api.x1id.io` 402 offer. This is the default for
38
+ * {@link buildX402Requirements} in this mainnet-era release. */
39
+ export declare const X1_MAINNET_NETWORK = "solana:4SvBP3omtvcCVWdxq1zBY5cDp4wndjsT";
40
+ /** Derive the CAIP-30 x402 network id from a cluster's FULL genesis hash —
41
+ * `"solana:" + first 32 base58 chars`. A server that knows its own RPC's
42
+ * genesis (the dev-api, the MCP server) should pass the result as the x402
43
+ * `network` rather than relying on a compiled-in default, so it is always
44
+ * correct for whatever cluster it is actually pointed at. */
45
+ export declare function x402NetworkForGenesis(genesisHash: string): string;
34
46
  /** The pre-#8325 `"x1:<full-genesis>"` id. DEPRECATED — kept only so a reader
35
47
  * can still recognise manifests published before the CAIP-30 migration. */
36
48
  export declare const X1_LEGACY_TESTNET_NETWORK = "x1:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWwT5iTdeaviDho";
@@ -96,6 +108,10 @@ export interface BuildX402RequirementsParams {
96
108
  * always set to this same buyer address. Pass the address the caller
97
109
  * expects to pay from. */
98
110
  readonly buyer: string;
111
+ /** CAIP-30 network id. Defaults to {@link X1_MAINNET_NETWORK}; a server on
112
+ * another cluster should pass its own — e.g.
113
+ * `x402NetworkForGenesis(await rpcGenesisHash)` — rather than rely on the
114
+ * default. Pass {@link X1_TESTNET_NETWORK} for testnet. */
99
115
  readonly network?: string;
100
116
  readonly maxTimeoutSeconds?: number;
101
117
  readonly memo?: string;
package/dist/x402.js CHANGED
@@ -31,6 +31,20 @@
31
31
  /** X1 testnet's x402 network id — CAIP-30 `"solana:" + first 32 chars of the
32
32
  * genesis hash — so unmodified OSS x402 clients route to it (#8325). */
33
33
  export const X1_TESTNET_NETWORK = "solana:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWw";
34
+ /** X1 MAINNET's x402 network id — CAIP-30 `"solana:" + first 32 chars of the
35
+ * mainnet genesis hash `4SvBP3omtvcCVWdxq1zBY5cDp4wndjsThb6nEMn6iMdN`, so
36
+ * unmodified OSS x402 clients route to it. Verified against `getGenesisHash`
37
+ * and the live `api.x1id.io` 402 offer. This is the default for
38
+ * {@link buildX402Requirements} in this mainnet-era release. */
39
+ export const X1_MAINNET_NETWORK = "solana:4SvBP3omtvcCVWdxq1zBY5cDp4wndjsT";
40
+ /** Derive the CAIP-30 x402 network id from a cluster's FULL genesis hash —
41
+ * `"solana:" + first 32 base58 chars`. A server that knows its own RPC's
42
+ * genesis (the dev-api, the MCP server) should pass the result as the x402
43
+ * `network` rather than relying on a compiled-in default, so it is always
44
+ * correct for whatever cluster it is actually pointed at. */
45
+ export function x402NetworkForGenesis(genesisHash) {
46
+ return "solana:" + genesisHash.slice(0, 32);
47
+ }
34
48
  /** The pre-#8325 `"x1:<full-genesis>"` id. DEPRECATED — kept only so a reader
35
49
  * can still recognise manifests published before the CAIP-30 migration. */
36
50
  export const X1_LEGACY_TESTNET_NETWORK = "x1:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWwT5iTdeaviDho";
@@ -63,7 +77,7 @@ export function buildX402Requirements(p) {
63
77
  }
64
78
  return {
65
79
  scheme: X402_SCHEME_EXACT,
66
- network: p.network ?? X1_TESTNET_NETWORK,
80
+ network: p.network ?? X1_MAINNET_NETWORK,
67
81
  amount: p.amount,
68
82
  asset: p.asset,
69
83
  payTo: p.payTo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x1id/resolve",
3
- "version": "0.15.0",
3
+ "version": "0.16.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",