@x1id/resolve 0.19.1 → 0.21.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.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `transfer` — hand an UNTOKENIZED handle to a new owner (a direct gift /
3
+ * reassignment of `handle.owner`). This is the plain-handle counterpart to a
4
+ * tokenized handle's pNFT transfer: a tokenized handle's ownership IS its NFT,
5
+ * so the program REFUSES `transfer` on one (`HandleIsTokenized`) — move the
6
+ * pNFT instead (see `pnftTransfer.ts`).
7
+ *
8
+ * Signed by the handle's CURRENT owner (`has_one = owner` on-chain). The
9
+ * program also refuses the move if the handle is locked (`require_unlocked`)
10
+ * or a live auction on it still has bids. It is NOT gated by the registry
11
+ * pause, so a treasury gift can be made even while registration is paused.
12
+ *
13
+ * Hand-rolled like the rest of this package — no Anchor client, no web3.js.
14
+ * PDAs are NOT derived here (see delegate.ts's module docs); derive
15
+ * `["handle", name]` and `["auction", handle]` with your runtime's canonical
16
+ * `findProgramAddress` and pass them in.
17
+ */
18
+ import { encodeBase58, decodeBase58_32 } from "./base58.js";
19
+ /** Anchor instruction discriminator: `sha256("global:transfer")[0..8]`. */
20
+ export const TRANSFER_DISCRIMINATOR = Uint8Array.from([
21
+ 0xa3, 0x34, 0xc8, 0xe7, 0x8c, 0x03, 0x45, 0xba,
22
+ ]);
23
+ function toBytes32(v, what) {
24
+ if (typeof v === "string") {
25
+ const b = decodeBase58_32(v);
26
+ if (!b)
27
+ throw new Error(`${what} is not a valid base58 address`);
28
+ return b;
29
+ }
30
+ if (v.length !== 32)
31
+ throw new Error(`${what} must be exactly 32 bytes`);
32
+ return v;
33
+ }
34
+ function toBase58(v, what) {
35
+ return encodeBase58(toBytes32(v, what));
36
+ }
37
+ /**
38
+ * Build `transfer(new_owner)`. The data is the 8-byte discriminator followed by
39
+ * the 32-byte new owner; the accounts are `owner` (signer), `handle` (writable)
40
+ * and the read-only `["auction", handle]` PDA, plus any scoped-TLD
41
+ * remaining-accounts. Returns an UNSIGNED instruction — sign with `owner`.
42
+ */
43
+ export function buildTransferIx(p) {
44
+ const data = new Uint8Array(8 + 32);
45
+ data.set(TRANSFER_DISCRIMINATOR, 0);
46
+ data.set(toBytes32(p.newOwner, "newOwner"), 8);
47
+ const keys = [
48
+ { pubkey: toBase58(p.owner, "owner"), isSigner: true, isWritable: false },
49
+ { pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: true },
50
+ { pubkey: toBase58(p.auction, "auction"), isSigner: false, isWritable: false },
51
+ ];
52
+ for (const a of p.remainingAccounts ?? []) {
53
+ keys.push({ pubkey: toBase58(a, "remainingAccount"), isSigner: false, isWritable: false });
54
+ }
55
+ return { programId: toBase58(p.programId, "programId"), keys, data };
56
+ }
package/dist/types.d.ts CHANGED
@@ -94,6 +94,10 @@ export interface Resolved {
94
94
  export type Chain = "X1" | "SOL" | "ETH" | "BTC";
95
95
  export declare const CHAIN_COIN_TYPE: Readonly<Record<Chain, number>>;
96
96
  export type ResolveErrorCode = "unrecognized" | "ambiguous" | "invalid-handle" | "invalid-domain" | "not-found" | "no-record-for-chain" | "rpc-error"
97
+ /** A caller passed a malformed value to an SDK primitive (e.g. a gate that
98
+ * isn't "any-verified"/{kind}/{anyOf}) — a programming error, surfaced as a
99
+ * typed ResolveError rather than a raw TypeError. */
100
+ | "invalid-input"
97
101
  /**
98
102
  * A reachable namespace exists for this name, but the adapter that would
99
103
  * resolve it is not configured on this deployment (e.g. no `ETH_RPC_URL` for
package/dist/voucher.d.ts CHANGED
@@ -22,8 +22,8 @@
22
22
  * escrows the mint price (a snapshot of the live price) into
23
23
  * `["voucher", name]` and reserves the voucher slot. `recipient` bound ⇒ the
24
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
25
+ * a bound recipient (audit S6 — open/recipient-less vouchers were removed as
26
+ * front-runnable; `claimSecretHash` is vestigial and rejected). An optional integrator referrer (the same trailing accounts as
27
27
  * `register`, from `integrator.ts`) is snapshotted so the claim can split
28
28
  * the escrow. `expiresAt` must be strictly in the future.
29
29
  * - `claim_voucher(name)` registers the handle, drawing the fee from the
package/dist/voucher.js CHANGED
@@ -22,8 +22,8 @@
22
22
  * escrows the mint price (a snapshot of the live price) into
23
23
  * `["voucher", name]` and reserves the voucher slot. `recipient` bound ⇒ the
24
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
25
+ * a bound recipient (audit S6 — open/recipient-less vouchers were removed as
26
+ * front-runnable; `claimSecretHash` is vestigial and rejected). An optional integrator referrer (the same trailing accounts as
27
27
  * `register`, from `integrator.ts`) is snapshotted so the claim can split
28
28
  * the escrow. `expiresAt` must be strictly in the future.
29
29
  * - `claim_voucher(name)` registers the handle, drawing the fee from the
@@ -171,8 +171,19 @@ export function decodePrepaidVoucher(data) {
171
171
  export function buildCreateVoucherIx(p) {
172
172
  const name = encString(p.name);
173
173
  const recipient = p.recipient ?? null;
174
+ // Audit S6: the program now REFUSES a recipient-less voucher (the claim tx
175
+ // reveals the secret in cleartext, so an open voucher is front-runnable —
176
+ // error 6106 VoucherMustBeBound). `claimSecretHash` is NO LONGER a second
177
+ // factor: with a recipient mandatory the program stores None and ignores any
178
+ // hash. Throw on both so no integrator believes either still works.
179
+ if (recipient === null) {
180
+ throw new Error("a voucher must bind its recipient (open secret-code vouchers are front-runnable and were disabled on-chain)");
181
+ }
182
+ if (p.claimSecretHash != null) {
183
+ throw new Error("claimSecretHash is removed — vouchers are recipient-bound and carry no claim secret (the program ignores it)");
184
+ }
174
185
  const recBytes = recipient === null ? Uint8Array.from([0]) : Uint8Array.from([1, ...toBytes32(recipient, "recipient")]);
175
- const hashBytes = encOption32(p.claimSecretHash ?? null);
186
+ const hashBytes = encOption32(null); // S6: always None (a supplied hash throws above)
176
187
  const data = new Uint8Array(8 + name.length + 1 + recBytes.length + 8 + hashBytes.length);
177
188
  let o = 0;
178
189
  data.set(CREATE_VOUCHER_DISCRIMINATOR, o);
package/dist/x402.js CHANGED
@@ -60,6 +60,14 @@ export const X402_VERSION = 2;
60
60
  * and let the buyer fetch their own — the spec allows either).
61
61
  */
62
62
  export function buildX402Requirements(p) {
63
+ // W171: payTo / asset / buyer are REQUIRED base58 pubkeys — a missing one
64
+ // otherwise sails through (payTo: undefined) straight into the requirements.
65
+ const PUBKEY_RE = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
66
+ for (const [field, value] of [["payTo", p.payTo], ["asset", p.asset], ["buyer", p.buyer]]) {
67
+ if (!PUBKEY_RE.test((value ?? "").trim())) {
68
+ throw new Error(`${field} must be a base58 address (32-44 chars); got ${value === undefined ? "undefined" : `"${value}"`}`);
69
+ }
70
+ }
63
71
  if (!/^\d+$/.test(p.amount)) {
64
72
  throw new Error("amount must be a decimal string of smallest units (e.g. \"1000000\")");
65
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x1id/resolve",
3
- "version": "0.19.1",
3
+ "version": "0.21.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",