@x1id/resolve 0.10.0 → 0.11.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/index.d.ts CHANGED
@@ -41,6 +41,7 @@ export * from "./integrator.js";
41
41
  export * from "./voucher.js";
42
42
  export * from "./agent.js";
43
43
  export * from "./register.js";
44
+ export * from "./lease.js";
44
45
  export * from "./accounts.js";
45
46
  export * from "./x402.js";
46
47
  export * from "./recordWrite.js";
package/dist/index.js CHANGED
@@ -41,6 +41,7 @@ export * from "./integrator.js";
41
41
  export * from "./voucher.js";
42
42
  export * from "./agent.js";
43
43
  export * from "./register.js";
44
+ export * from "./lease.js";
44
45
  // Was previously imported here for internal use only, never re-exported —
45
46
  // promoted to public API 2026-09-22 because a second real package (mcp/)
46
47
  // now needs `parseHandleAccount`/`RpcFn` directly rather than duplicating
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Lease-to-own instructions (#8150, #8320) — the SIBLING of `register` for term
3
+ * leases, not a replacement: the permanent one-time `register` path
4
+ * (register.ts) is untouched and stays the default.
5
+ *
6
+ * - `register_leased` — claim an unregistered `["handle", name]` under a lease,
7
+ * paying the first installment now.
8
+ * - `renew_lease` — pay more toward a leased handle. PERMISSIONLESS: the payer
9
+ * need not be the owner (anyone may keep or pay off someone else's lease,
10
+ * ENS-registrar-style).
11
+ * - `reclaim_expired_lease` — take over a leased handle whose grace period has
12
+ * ENDED, under a FRESH lease; the reclaimer becomes `owner`. Also
13
+ * permissionless. The program enforces `now >= expires_at + grace`.
14
+ *
15
+ * Hand-rolled like the rest of this package — no Anchor client, no
16
+ * `@solana/web3.js`. PDAs are NOT derived here (see delegate.ts) — derive
17
+ * `["config"]` / `["handle", name]` with your runtime's `findProgramAddress`,
18
+ * and read `Config.treasury` with {@link fetchConfigTreasury} (register.ts).
19
+ *
20
+ * `payment` is in lamports — the amount accepted toward the lease now. The
21
+ * on-chain `lease_seconds_purchased` / `lease_accept_payment` logic converts it
22
+ * to lease-time, capped at the permanent price (offering the full permanent
23
+ * price up front settles the handle permanently in one instruction, writing
24
+ * `expires_at = 0`). This builder does not compute that split; simulate or
25
+ * mirror it client-side if you need to preview before signing.
26
+ */
27
+ import type { AddressLike, BuiltInstruction } from "./delegate.js";
28
+ import type { HandleTypeValue } from "./voucher.js";
29
+ /** `sha256("global:register_leased")[0..8]`. */
30
+ export declare const REGISTER_LEASED_DISCRIMINATOR: Uint8Array;
31
+ /** `sha256("global:renew_lease")[0..8]`. */
32
+ export declare const RENEW_LEASE_DISCRIMINATOR: Uint8Array;
33
+ /** `sha256("global:reclaim_expired_lease")[0..8]`. */
34
+ export declare const RECLAIM_EXPIRED_LEASE_DISCRIMINATOR: Uint8Array;
35
+ export interface RegisterLeasedParams {
36
+ /** The registry program id. */
37
+ readonly programId: AddressLike;
38
+ /** Pays the first lease installment + the `Handle` account's rent. Signer. */
39
+ readonly payer: AddressLike;
40
+ /** The handle's owner. Need not sign — may be registered on another's behalf. */
41
+ readonly owner: AddressLike;
42
+ /** The `["config"]` PDA. */
43
+ readonly config: AddressLike;
44
+ /** `Config.treasury` — read it fresh via {@link fetchConfigTreasury}. */
45
+ readonly treasury: AddressLike;
46
+ /** The `["handle", name]` PDA being claimed — must not already exist. */
47
+ readonly handle: AddressLike;
48
+ /** Canonical form (`parseName(...).canonical`), 1..=32 bytes. */
49
+ readonly name: string;
50
+ readonly handleType: HandleTypeValue;
51
+ /** First lease payment, in lamports. `>= permanentPrice` settles it outright. */
52
+ readonly payment: bigint;
53
+ }
54
+ /** Build `register_leased`. Data: `disc ‖ borsh(name) ‖ handleType(u8) ‖ payment(u64 LE)`. */
55
+ export declare function buildRegisterLeasedIx(p: RegisterLeasedParams): BuiltInstruction;
56
+ export interface RenewLeaseParams {
57
+ /** The registry program id. */
58
+ readonly programId: AddressLike;
59
+ /** Pays toward the lease. Signer. PERMISSIONLESS — need NOT be the owner. */
60
+ readonly payer: AddressLike;
61
+ /** The `["config"]` PDA (read-only on this path). */
62
+ readonly config: AddressLike;
63
+ /** `Config.treasury` — the program address-constrains it to this, so it can't
64
+ * be substituted; read it fresh via {@link fetchConfigTreasury}. */
65
+ readonly treasury: AddressLike;
66
+ /** The leased `["handle", name]` PDA being paid toward. */
67
+ readonly handle: AddressLike;
68
+ /** Lamports to accept toward the lease (capped at the permanent price on-chain). */
69
+ readonly payment: bigint;
70
+ }
71
+ /** Build `renew_lease`. Data: `disc ‖ payment(u64 LE)`. */
72
+ export declare function buildRenewLeaseIx(p: RenewLeaseParams): BuiltInstruction;
73
+ export interface ReclaimExpiredLeaseParams {
74
+ /** The registry program id. */
75
+ readonly programId: AddressLike;
76
+ /** Pays the fresh lease's first installment; becomes the new owner. Signer.
77
+ * PERMISSIONLESS — anyone may reclaim a grace-ended leased handle. */
78
+ readonly payer: AddressLike;
79
+ /** The new owner (typically the same as `payer`). Need not sign. */
80
+ readonly owner: AddressLike;
81
+ /** The `["config"]` PDA. */
82
+ readonly config: AddressLike;
83
+ /** `Config.treasury` — address-constrained on-chain; {@link fetchConfigTreasury}. */
84
+ readonly treasury: AddressLike;
85
+ /** The leased `["handle", name]` PDA being reclaimed — its grace must have ended. */
86
+ readonly handle: AddressLike;
87
+ readonly handleType: HandleTypeValue;
88
+ /** The fresh lease's first payment, in lamports. */
89
+ readonly payment: bigint;
90
+ }
91
+ /** Build `reclaim_expired_lease`. Data: `disc ‖ handleType(u8) ‖ payment(u64 LE)`. */
92
+ export declare function buildReclaimExpiredLeaseIx(p: ReclaimExpiredLeaseParams): BuiltInstruction;
package/dist/lease.js ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Lease-to-own instructions (#8150, #8320) — the SIBLING of `register` for term
3
+ * leases, not a replacement: the permanent one-time `register` path
4
+ * (register.ts) is untouched and stays the default.
5
+ *
6
+ * - `register_leased` — claim an unregistered `["handle", name]` under a lease,
7
+ * paying the first installment now.
8
+ * - `renew_lease` — pay more toward a leased handle. PERMISSIONLESS: the payer
9
+ * need not be the owner (anyone may keep or pay off someone else's lease,
10
+ * ENS-registrar-style).
11
+ * - `reclaim_expired_lease` — take over a leased handle whose grace period has
12
+ * ENDED, under a FRESH lease; the reclaimer becomes `owner`. Also
13
+ * permissionless. The program enforces `now >= expires_at + grace`.
14
+ *
15
+ * Hand-rolled like the rest of this package — no Anchor client, no
16
+ * `@solana/web3.js`. PDAs are NOT derived here (see delegate.ts) — derive
17
+ * `["config"]` / `["handle", name]` with your runtime's `findProgramAddress`,
18
+ * and read `Config.treasury` with {@link fetchConfigTreasury} (register.ts).
19
+ *
20
+ * `payment` is in lamports — the amount accepted toward the lease now. The
21
+ * on-chain `lease_seconds_purchased` / `lease_accept_payment` logic converts it
22
+ * to lease-time, capped at the permanent price (offering the full permanent
23
+ * price up front settles the handle permanently in one instruction, writing
24
+ * `expires_at = 0`). This builder does not compute that split; simulate or
25
+ * mirror it client-side if you need to preview before signing.
26
+ */
27
+ import { encodeBase58, decodeBase58_32 } from "./base58.js";
28
+ /** `sha256("global:register_leased")[0..8]`. */
29
+ export const REGISTER_LEASED_DISCRIMINATOR = Uint8Array.from([83, 192, 142, 193, 191, 53, 8, 15]);
30
+ /** `sha256("global:renew_lease")[0..8]`. */
31
+ export const RENEW_LEASE_DISCRIMINATOR = Uint8Array.from([11, 121, 141, 53, 216, 217, 123, 139]);
32
+ /** `sha256("global:reclaim_expired_lease")[0..8]`. */
33
+ export const RECLAIM_EXPIRED_LEASE_DISCRIMINATOR = Uint8Array.from([66, 80, 245, 8, 1, 21, 161, 110]);
34
+ const SYSTEM_PROGRAM = "11111111111111111111111111111111";
35
+ function toBytes32(v, what) {
36
+ if (typeof v === "string") {
37
+ const b = decodeBase58_32(v);
38
+ if (!b)
39
+ throw new Error(`${what} is not a valid base58 address`);
40
+ return b;
41
+ }
42
+ if (v.length !== 32)
43
+ throw new Error(`${what} must be exactly 32 bytes`);
44
+ return v;
45
+ }
46
+ function toBase58(v, what) {
47
+ return encodeBase58(toBytes32(v, what));
48
+ }
49
+ function assertHandleType(t) {
50
+ if (!Number.isInteger(t) || t < 0 || t > 3) {
51
+ throw new Error("handleType must be 0 (Human), 1 (Merchant), 2 (Org) or 3 (Agent)");
52
+ }
53
+ }
54
+ function encodeName(name) {
55
+ const b = new TextEncoder().encode(name);
56
+ if (b.length < 1 || b.length > 32)
57
+ throw new Error("name must be 1..=32 bytes (UTF-8)");
58
+ return b;
59
+ }
60
+ function u64le(n) {
61
+ if (n < 0n)
62
+ throw new Error("payment must be a non-negative u64 (lamports)");
63
+ const b = new Uint8Array(8);
64
+ new DataView(b.buffer).setBigUint64(0, n, true);
65
+ return b;
66
+ }
67
+ /** Build `register_leased`. Data: `disc ‖ borsh(name) ‖ handleType(u8) ‖ payment(u64 LE)`. */
68
+ export function buildRegisterLeasedIx(p) {
69
+ assertHandleType(p.handleType);
70
+ const nb = encodeName(p.name);
71
+ const data = new Uint8Array(8 + 4 + nb.length + 1 + 8);
72
+ data.set(REGISTER_LEASED_DISCRIMINATOR, 0);
73
+ new DataView(data.buffer).setUint32(8, nb.length, true);
74
+ data.set(nb, 12);
75
+ data[12 + nb.length] = p.handleType;
76
+ data.set(u64le(p.payment), 12 + nb.length + 1);
77
+ const keys = [
78
+ { pubkey: toBase58(p.payer, "payer"), isSigner: true, isWritable: true },
79
+ { pubkey: toBase58(p.owner, "owner"), isSigner: false, isWritable: false },
80
+ { pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: true },
81
+ { pubkey: toBase58(p.treasury, "treasury"), isSigner: false, isWritable: true },
82
+ { pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: true },
83
+ { pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
84
+ ];
85
+ return { programId: toBase58(p.programId, "programId"), keys, data };
86
+ }
87
+ /** Build `renew_lease`. Data: `disc ‖ payment(u64 LE)`. */
88
+ export function buildRenewLeaseIx(p) {
89
+ const data = new Uint8Array(8 + 8);
90
+ data.set(RENEW_LEASE_DISCRIMINATOR, 0);
91
+ data.set(u64le(p.payment), 8);
92
+ const keys = [
93
+ { pubkey: toBase58(p.payer, "payer"), isSigner: true, isWritable: true },
94
+ { pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: false },
95
+ { pubkey: toBase58(p.treasury, "treasury"), isSigner: false, isWritable: true },
96
+ { pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: true },
97
+ { pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
98
+ ];
99
+ return { programId: toBase58(p.programId, "programId"), keys, data };
100
+ }
101
+ /** Build `reclaim_expired_lease`. Data: `disc ‖ handleType(u8) ‖ payment(u64 LE)`. */
102
+ export function buildReclaimExpiredLeaseIx(p) {
103
+ assertHandleType(p.handleType);
104
+ const data = new Uint8Array(8 + 1 + 8);
105
+ data.set(RECLAIM_EXPIRED_LEASE_DISCRIMINATOR, 0);
106
+ data[8] = p.handleType;
107
+ data.set(u64le(p.payment), 9);
108
+ const keys = [
109
+ { pubkey: toBase58(p.payer, "payer"), isSigner: true, isWritable: true },
110
+ { pubkey: toBase58(p.owner, "owner"), isSigner: false, isWritable: false },
111
+ { pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: true },
112
+ { pubkey: toBase58(p.treasury, "treasury"), isSigner: false, isWritable: true },
113
+ { pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: true },
114
+ { pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
115
+ ];
116
+ return { programId: toBase58(p.programId, "programId"), keys, data };
117
+ }
package/dist/voucher.d.ts CHANGED
@@ -128,3 +128,56 @@ export interface RefundVoucherParams {
128
128
  }
129
129
  /** Build `refund_voucher` — payer-signed, only valid at/after `expiresAt`. */
130
130
  export declare function buildRefundVoucherIx(p: RefundVoucherParams): BuiltInstruction;
131
+ /** `sha256("global:claim_voucher_tokenized")[0..8]`. */
132
+ export declare const CLAIM_VOUCHER_TOKENIZED_DISCRIMINATOR: Uint8Array;
133
+ export interface ClaimVoucherTokenizedParams {
134
+ readonly programId: AddressLike;
135
+ /** Submits + signs; fronts the tx fee (reimbursed) + the handle rent. */
136
+ readonly claimer: AddressLike;
137
+ readonly config: AddressLike;
138
+ readonly treasury: AddressLike;
139
+ readonly voucher: AddressLike;
140
+ /** The original voucher payer — receives rent back when the voucher closes. */
141
+ readonly voucherPayer: AddressLike;
142
+ /** Who the pNFT + ownership go to: the bound recipient, or the claimer for an
143
+ * open voucher. The mint's ATA + token record derive against THIS address. */
144
+ readonly owner: AddressLike;
145
+ /** The `["handle", name]` PDA. */
146
+ readonly handle: AddressLike;
147
+ readonly name: string;
148
+ /** `["nft_mint", handle]` PDA. */
149
+ readonly mint: AddressLike;
150
+ /** `owner`'s associated token account for `mint` (receives the frozen unit). */
151
+ readonly ownerTokenAccount: AddressLike;
152
+ /** Metaplex metadata PDA for `mint`. */
153
+ readonly metadata: AddressLike;
154
+ /** Metaplex master-edition PDA for `mint`. */
155
+ readonly masterEdition: AddressLike;
156
+ /** pNFT TokenRecord PDA for (`mint`, `ownerTokenAccount`). */
157
+ readonly tokenRecord: AddressLike;
158
+ /** The collection parent mint (`["collection_mint"]`). */
159
+ readonly collectionMint: AddressLike;
160
+ /** The collection parent metadata (writable — sized-collection bump). */
161
+ readonly collectionMetadata: AddressLike;
162
+ /** The collection parent master edition. */
163
+ readonly collectionMasterEdition: AddressLike;
164
+ /** The collection authority PDA. */
165
+ readonly collectionAuthority: AddressLike;
166
+ /** The integrator wallet — REQUIRED iff the voucher carries one; must equal
167
+ * the voucher's `integrator`. Omit otherwise. */
168
+ readonly integrator?: AddressLike;
169
+ }
170
+ /**
171
+ * Build `claim_voucher_tokenized` (#8335) — the tokenized sibling of
172
+ * {@link buildClaimVoucherIx} and the DEFAULT path the live gift-claim flow
173
+ * uses: it registers the handle AND mints its tradeable capability pNFT in one
174
+ * instruction. The pNFT + ownership go to `owner` (the bound recipient, or the
175
+ * claimer for an open voucher), so `ownerTokenAccount` / `tokenRecord` derive
176
+ * against `owner`, not the claimer. Data: `disc ‖ borsh(name)` — the handle type
177
+ * comes from the voucher, not an argument.
178
+ *
179
+ * The register + 3 Metaplex CPIs exceed the 200k default compute budget —
180
+ * prepend a ComputeBudget `setComputeUnitLimit` instruction in the SAME
181
+ * transaction (the app uses ~400k) or the claim fails on-chain.
182
+ */
183
+ export declare function buildClaimVoucherTokenizedIx(p: ClaimVoucherTokenizedParams): BuiltInstruction;
package/dist/voucher.js CHANGED
@@ -183,3 +183,59 @@ export function buildRefundVoucherIx(p) {
183
183
  data,
184
184
  };
185
185
  }
186
+ // ===================== claim_voucher_tokenized (gift NFTs, #8335) =====================
187
+ /** `sha256("global:claim_voucher_tokenized")[0..8]`. */
188
+ export const CLAIM_VOUCHER_TOKENIZED_DISCRIMINATOR = Uint8Array.from([131, 111, 174, 105, 154, 248, 191, 235]);
189
+ // Fixed Metaplex / SPL program ids + sysvar in the pNFT-mint tail — hardcoded,
190
+ // never derived (matches the app's mintNft.ts).
191
+ const TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
192
+ const ASSOCIATED_TOKEN_PROGRAM_ID = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
193
+ const TOKEN_METADATA_PROGRAM = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
194
+ const SYSVAR_INSTRUCTIONS = "Sysvar1nstructions1111111111111111111111111";
195
+ /**
196
+ * Build `claim_voucher_tokenized` (#8335) — the tokenized sibling of
197
+ * {@link buildClaimVoucherIx} and the DEFAULT path the live gift-claim flow
198
+ * uses: it registers the handle AND mints its tradeable capability pNFT in one
199
+ * instruction. The pNFT + ownership go to `owner` (the bound recipient, or the
200
+ * claimer for an open voucher), so `ownerTokenAccount` / `tokenRecord` derive
201
+ * against `owner`, not the claimer. Data: `disc ‖ borsh(name)` — the handle type
202
+ * comes from the voucher, not an argument.
203
+ *
204
+ * The register + 3 Metaplex CPIs exceed the 200k default compute budget —
205
+ * prepend a ComputeBudget `setComputeUnitLimit` instruction in the SAME
206
+ * transaction (the app uses ~400k) or the claim fails on-chain.
207
+ */
208
+ export function buildClaimVoucherTokenizedIx(p) {
209
+ const name = encString(p.name);
210
+ const data = new Uint8Array(8 + name.length);
211
+ data.set(CLAIM_VOUCHER_TOKENIZED_DISCRIMINATOR, 0);
212
+ data.set(name, 8);
213
+ const keys = [
214
+ { pubkey: toBase58(p.claimer, "claimer"), isSigner: true, isWritable: true },
215
+ { pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: true },
216
+ { pubkey: toBase58(p.treasury, "treasury"), isSigner: false, isWritable: true },
217
+ { pubkey: toBase58(p.voucher, "voucher"), isSigner: false, isWritable: true },
218
+ { pubkey: toBase58(p.voucherPayer, "voucherPayer"), isSigner: false, isWritable: true },
219
+ { pubkey: toBase58(p.owner, "owner"), isSigner: false, isWritable: false },
220
+ { pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: true },
221
+ // Metaplex pNFT tail — order + flags verbatim from mintNft.ts::tokenizedTailKeys.
222
+ { pubkey: toBase58(p.mint, "mint"), isSigner: false, isWritable: true },
223
+ { pubkey: toBase58(p.ownerTokenAccount, "ownerTokenAccount"), isSigner: false, isWritable: true },
224
+ { pubkey: toBase58(p.metadata, "metadata"), isSigner: false, isWritable: true },
225
+ { pubkey: toBase58(p.masterEdition, "masterEdition"), isSigner: false, isWritable: true },
226
+ { pubkey: toBase58(p.tokenRecord, "tokenRecord"), isSigner: false, isWritable: true },
227
+ { pubkey: toBase58(p.collectionMint, "collectionMint"), isSigner: false, isWritable: false },
228
+ { pubkey: toBase58(p.collectionMetadata, "collectionMetadata"), isSigner: false, isWritable: true },
229
+ { pubkey: toBase58(p.collectionMasterEdition, "collectionMasterEdition"), isSigner: false, isWritable: false },
230
+ { pubkey: toBase58(p.collectionAuthority, "collectionAuthority"), isSigner: false, isWritable: false },
231
+ { pubkey: TOKEN_METADATA_PROGRAM, isSigner: false, isWritable: false },
232
+ { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
233
+ { pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
234
+ { pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
235
+ { pubkey: SYSVAR_INSTRUCTIONS, isSigner: false, isWritable: false },
236
+ ];
237
+ if (p.integrator !== undefined) {
238
+ keys.push({ pubkey: toBase58(p.integrator, "integrator"), isSigner: false, isWritable: true });
239
+ }
240
+ return { programId: toBase58(p.programId, "programId"), keys, data };
241
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x1id/resolve",
3
- "version": "0.10.0",
3
+ "version": "0.11.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",