@x1id/resolve 0.10.0 → 0.12.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 +74 -5
- package/dist/adminConfig.d.ts +63 -0
- package/dist/adminConfig.js +113 -0
- package/dist/adminMarket.d.ts +67 -0
- package/dist/adminMarket.js +134 -0
- package/dist/attestation.d.ts +103 -1
- package/dist/attestation.js +124 -0
- package/dist/gateClient.d.ts +98 -0
- package/dist/gateClient.js +118 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +138 -2
- package/dist/lease.d.ts +92 -0
- package/dist/lease.js +117 -0
- package/dist/namespaceOverride.d.ts +123 -0
- package/dist/namespaceOverride.js +204 -0
- package/dist/register.d.ts +30 -0
- package/dist/register.js +37 -0
- package/dist/scoped.d.ts +84 -0
- package/dist/scoped.js +126 -0
- package/dist/types.d.ts +23 -4
- package/dist/types.js +2 -1
- package/dist/universal.d.ts +58 -0
- package/dist/universal.js +67 -0
- package/dist/universalDetect.d.ts +44 -0
- package/dist/universalDetect.js +105 -0
- package/dist/universalEns.d.ts +44 -0
- package/dist/universalEns.js +138 -0
- package/dist/universalNative.d.ts +13 -0
- package/dist/universalNative.js +43 -0
- package/dist/universalSns.d.ts +95 -0
- package/dist/universalSns.js +223 -0
- package/dist/universalTypes.d.ts +117 -0
- package/dist/universalTypes.js +24 -0
- package/dist/voucher.d.ts +53 -0
- package/dist/voucher.js +56 -0
- package/dist/wasm.d.ts +17 -0
- package/dist/wasm.js +38 -0
- package/package.json +1 -1
- package/wasm/x1_resolve_wasm.wasm +0 -0
package/dist/lease.d.ts
ADDED
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-handle namespace overrides — `create_namespace_override` /
|
|
3
|
+
* `update_namespace_override` / `close_namespace_override` (#8138 family).
|
|
4
|
+
*
|
|
5
|
+
* A namespace override lets a handle's owner redirect `<handle>.<namespace>`
|
|
6
|
+
* (e.g. `jack.xnt`) to a DIFFERENT address than the handle's default, per
|
|
7
|
+
* namespace label. It is a record-edit-authority action (the handle owner, or a
|
|
8
|
+
* record delegate, signs — NOT an admin action), in the same family as the
|
|
9
|
+
* address `Record` writers: an optional `record_delegate` named slot plus the
|
|
10
|
+
* tokenized-handle holder-ATA proof in `remaining_accounts[0]`.
|
|
11
|
+
*
|
|
12
|
+
* NOTE: this capability is built but NOT yet wired into public resolution —
|
|
13
|
+
* `resolve()` does not read overrides yet (that is the cutover switch). These
|
|
14
|
+
* builders + the decoder are the on-chain-write + read-decode half, ready for
|
|
15
|
+
* when namespaced resolution goes live.
|
|
16
|
+
*
|
|
17
|
+
* Hand-rolled like the rest of the package — no Anchor client, no web3.js. PDAs
|
|
18
|
+
* are not derived here: derive `["handle", name]`, `["namespace", label]`,
|
|
19
|
+
* `["namespace_override", handlePubkey, label]`, `["delegate", handlePubkey]`
|
|
20
|
+
* with your runtime and pass them in, same convention as records.ts / subname.ts.
|
|
21
|
+
*/
|
|
22
|
+
import type { AddressLike, BuiltInstruction } from "./delegate.js";
|
|
23
|
+
import type { RpcFn } from "./accounts.js";
|
|
24
|
+
/** sha256("global:create_namespace_override")[..8]. */
|
|
25
|
+
export declare const CREATE_NAMESPACE_OVERRIDE_DISCRIMINATOR: Uint8Array;
|
|
26
|
+
/** sha256("global:update_namespace_override")[..8]. */
|
|
27
|
+
export declare const UPDATE_NAMESPACE_OVERRIDE_DISCRIMINATOR: Uint8Array;
|
|
28
|
+
/** sha256("global:close_namespace_override")[..8]. */
|
|
29
|
+
export declare const CLOSE_NAMESPACE_OVERRIDE_DISCRIMINATOR: Uint8Array;
|
|
30
|
+
/** The Anchor ACCOUNT discriminator for a `NamespaceOverride` — sha256("account:NamespaceOverride")[..8]. */
|
|
31
|
+
export declare const NAMESPACE_OVERRIDE_ACCOUNT_DISCRIMINATOR: Uint8Array;
|
|
32
|
+
/** The record-edit-authority tail every writer here shares: an optional named
|
|
33
|
+
* `record_delegate` slot (None = the program-id placeholder), then the
|
|
34
|
+
* tokenized-handle holder-ATA proof in remaining_accounts[0]. Own copy per
|
|
35
|
+
* module, matching recordWrite.ts's convention. */
|
|
36
|
+
interface AuthorityTail {
|
|
37
|
+
/** `["delegate", handle]` PDA — pass when a record delegate (not the owner) signs. */
|
|
38
|
+
readonly recordDelegate?: AddressLike;
|
|
39
|
+
/** The owner's pNFT token account — REQUIRED for a tokenized handle (proves
|
|
40
|
+
* the signer holds the capability NFT); omit for an untokenized handle. */
|
|
41
|
+
readonly holderTokenAccount?: AddressLike;
|
|
42
|
+
}
|
|
43
|
+
export interface CreateNamespaceOverrideParams extends AuthorityTail {
|
|
44
|
+
readonly programId: AddressLike;
|
|
45
|
+
/** Rent payer for the new override account; signs. */
|
|
46
|
+
readonly payer: AddressLike;
|
|
47
|
+
/** The handle owner (or delegate signer); signs. */
|
|
48
|
+
readonly owner: AddressLike;
|
|
49
|
+
/** `["handle", name]` PDA. */
|
|
50
|
+
readonly handle: AddressLike;
|
|
51
|
+
/** `["namespace", label]` PDA — must be Active for a create. */
|
|
52
|
+
readonly namespace: AddressLike;
|
|
53
|
+
/** `["namespace_override", handle, label]` PDA (init). */
|
|
54
|
+
readonly namespaceOverride: AddressLike;
|
|
55
|
+
/** The namespace label (e.g. "xnt"), an instruction ARG (== the seed). */
|
|
56
|
+
readonly namespaceLabel: string;
|
|
57
|
+
/** The address `<handle>.<label>` should resolve to. */
|
|
58
|
+
readonly value: AddressLike;
|
|
59
|
+
}
|
|
60
|
+
/** Build `create_namespace_override`. Args: namespace_label (String) + value (Pubkey). */
|
|
61
|
+
export declare function buildCreateNamespaceOverrideIx(p: CreateNamespaceOverrideParams): BuiltInstruction;
|
|
62
|
+
export interface UpdateNamespaceOverrideParams extends AuthorityTail {
|
|
63
|
+
readonly programId: AddressLike;
|
|
64
|
+
readonly owner: AddressLike;
|
|
65
|
+
readonly handle: AddressLike;
|
|
66
|
+
/** `["namespace_override", handle, label]` PDA (self-seeds from its stored label). */
|
|
67
|
+
readonly namespaceOverride: AddressLike;
|
|
68
|
+
/** The new target address. */
|
|
69
|
+
readonly value: AddressLike;
|
|
70
|
+
}
|
|
71
|
+
/** Build `update_namespace_override` — change the target address. Arg: value (Pubkey). */
|
|
72
|
+
export declare function buildUpdateNamespaceOverrideIx(p: UpdateNamespaceOverrideParams): BuiltInstruction;
|
|
73
|
+
export interface CloseNamespaceOverrideParams extends AuthorityTail {
|
|
74
|
+
readonly programId: AddressLike;
|
|
75
|
+
readonly owner: AddressLike;
|
|
76
|
+
readonly handle: AddressLike;
|
|
77
|
+
/** `["namespace_override", handle, label]` PDA (closed to recipient). */
|
|
78
|
+
readonly namespaceOverride: AddressLike;
|
|
79
|
+
/** Lamports recipient for the closed override's rent; need not sign. */
|
|
80
|
+
readonly recipient: AddressLike;
|
|
81
|
+
}
|
|
82
|
+
/** Build `close_namespace_override` — close the override, rent to recipient. No args. */
|
|
83
|
+
export declare function buildCloseNamespaceOverrideIx(p: CloseNamespaceOverrideParams): BuiltInstruction;
|
|
84
|
+
export interface NamespaceOverride {
|
|
85
|
+
/** The handle PDA this override belongs to. */
|
|
86
|
+
readonly handle: string;
|
|
87
|
+
/** The namespace label it applies to (e.g. "xnt"). */
|
|
88
|
+
readonly namespace: string;
|
|
89
|
+
/** The address `<handle>.<namespace>` resolves to instead of the default. */
|
|
90
|
+
readonly value: string;
|
|
91
|
+
/** Unix seconds last stamped. Trust ONLY while `updatedAt >= handle.registeredAt`
|
|
92
|
+
* (the universal epoch-staleness rule — a release+re-register reuses the PDA). */
|
|
93
|
+
readonly updatedAt: bigint;
|
|
94
|
+
readonly bump: number;
|
|
95
|
+
}
|
|
96
|
+
/** Decode a `NamespaceOverride` account. Layout: disc(8) | handle(32) |
|
|
97
|
+
* namespace(String: u32 len + bytes) | value(32) | updated_at(i64) | bump(1).
|
|
98
|
+
* Returns null if the data is too short / malformed. */
|
|
99
|
+
export declare function decodeNamespaceOverride(data: Uint8Array): NamespaceOverride | null;
|
|
100
|
+
/**
|
|
101
|
+
* Fetch + decode a handle's namespace override for one label, applying the
|
|
102
|
+
* universal epoch-staleness rule — a `NamespaceOverride` PDA is reused across a
|
|
103
|
+
* release + re-register of the same handle, so an override from a PREVIOUS owner
|
|
104
|
+
* (stamped before `handleRegisteredAt`) must NOT count. Returns the live
|
|
105
|
+
* override, or null when there is none / it is stale / the account is malformed.
|
|
106
|
+
*
|
|
107
|
+
* GATED read path: this is NOT called by `resolve()` — namespaced resolution does
|
|
108
|
+
* not honor overrides yet (that is the cutover switch). Derive `overrideAccount`
|
|
109
|
+
* with `WasmResolver.deriveNamespaceOverrideAccount(handle, label, programId)`.
|
|
110
|
+
*/
|
|
111
|
+
export declare function fetchNamespaceOverride(rpc: RpcFn, overrideAccount: string, handleRegisteredAt: bigint): Promise<NamespaceOverride | null>;
|
|
112
|
+
/**
|
|
113
|
+
* List all LIVE namespace overrides for one handle (a getProgramAccounts scan
|
|
114
|
+
* filtered by the account discriminator + the `handle` field at offset 8), with
|
|
115
|
+
* the epoch-staleness rule applied. Each entry includes its account address.
|
|
116
|
+
*
|
|
117
|
+
* GATED read path — not used by `resolve()` yet. Pass the handle's
|
|
118
|
+
* `registeredAt` so previous-owner overrides are dropped.
|
|
119
|
+
*/
|
|
120
|
+
export declare function fetchNamespaceOverridesForHandle(rpc: RpcFn, programId: string, handleAccount: string, handleRegisteredAt: bigint): Promise<Array<NamespaceOverride & {
|
|
121
|
+
account: string;
|
|
122
|
+
}>>;
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-handle namespace overrides — `create_namespace_override` /
|
|
3
|
+
* `update_namespace_override` / `close_namespace_override` (#8138 family).
|
|
4
|
+
*
|
|
5
|
+
* A namespace override lets a handle's owner redirect `<handle>.<namespace>`
|
|
6
|
+
* (e.g. `jack.xnt`) to a DIFFERENT address than the handle's default, per
|
|
7
|
+
* namespace label. It is a record-edit-authority action (the handle owner, or a
|
|
8
|
+
* record delegate, signs — NOT an admin action), in the same family as the
|
|
9
|
+
* address `Record` writers: an optional `record_delegate` named slot plus the
|
|
10
|
+
* tokenized-handle holder-ATA proof in `remaining_accounts[0]`.
|
|
11
|
+
*
|
|
12
|
+
* NOTE: this capability is built but NOT yet wired into public resolution —
|
|
13
|
+
* `resolve()` does not read overrides yet (that is the cutover switch). These
|
|
14
|
+
* builders + the decoder are the on-chain-write + read-decode half, ready for
|
|
15
|
+
* when namespaced resolution goes live.
|
|
16
|
+
*
|
|
17
|
+
* Hand-rolled like the rest of the package — no Anchor client, no web3.js. PDAs
|
|
18
|
+
* are not derived here: derive `["handle", name]`, `["namespace", label]`,
|
|
19
|
+
* `["namespace_override", handlePubkey, label]`, `["delegate", handlePubkey]`
|
|
20
|
+
* with your runtime and pass them in, same convention as records.ts / subname.ts.
|
|
21
|
+
*/
|
|
22
|
+
import { encodeBase58, decodeBase58_32 } from "./base58.js";
|
|
23
|
+
import { recordDelegateNonePlaceholder, recordDelegateSomeSlot } from "./delegate.js";
|
|
24
|
+
const SYSTEM_PROGRAM = "11111111111111111111111111111111";
|
|
25
|
+
/** sha256("global:create_namespace_override")[..8]. */
|
|
26
|
+
export const CREATE_NAMESPACE_OVERRIDE_DISCRIMINATOR = Uint8Array.from([86, 74, 181, 245, 1, 190, 77, 128]);
|
|
27
|
+
/** sha256("global:update_namespace_override")[..8]. */
|
|
28
|
+
export const UPDATE_NAMESPACE_OVERRIDE_DISCRIMINATOR = Uint8Array.from([6, 194, 181, 7, 155, 17, 115, 224]);
|
|
29
|
+
/** sha256("global:close_namespace_override")[..8]. */
|
|
30
|
+
export const CLOSE_NAMESPACE_OVERRIDE_DISCRIMINATOR = Uint8Array.from([95, 135, 159, 93, 70, 49, 213, 188]);
|
|
31
|
+
/** The Anchor ACCOUNT discriminator for a `NamespaceOverride` — sha256("account:NamespaceOverride")[..8]. */
|
|
32
|
+
export const NAMESPACE_OVERRIDE_ACCOUNT_DISCRIMINATOR = Uint8Array.from([53, 237, 118, 139, 3, 149, 13, 148]);
|
|
33
|
+
function toBytes32(v, what) {
|
|
34
|
+
if (typeof v === "string") {
|
|
35
|
+
const b = decodeBase58_32(v);
|
|
36
|
+
if (!b)
|
|
37
|
+
throw new Error(`${what} is not a valid base58 address`);
|
|
38
|
+
return b;
|
|
39
|
+
}
|
|
40
|
+
if (v.length !== 32)
|
|
41
|
+
throw new Error(`${what} must be exactly 32 bytes`);
|
|
42
|
+
return v;
|
|
43
|
+
}
|
|
44
|
+
function toBase58(v, what) {
|
|
45
|
+
return encodeBase58(toBytes32(v, what));
|
|
46
|
+
}
|
|
47
|
+
function encString(s) {
|
|
48
|
+
const bytes = new TextEncoder().encode(s);
|
|
49
|
+
const out = new Uint8Array(4 + bytes.length);
|
|
50
|
+
new DataView(out.buffer).setUint32(0, bytes.length, true);
|
|
51
|
+
out.set(bytes, 4);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
function concat(parts) {
|
|
55
|
+
const len = parts.reduce((n, p) => n + p.length, 0);
|
|
56
|
+
const out = new Uint8Array(len);
|
|
57
|
+
let o = 0;
|
|
58
|
+
for (const p of parts) {
|
|
59
|
+
out.set(p, o);
|
|
60
|
+
o += p.length;
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function pushAuthorityTail(keys, programId, p) {
|
|
65
|
+
if (p.recordDelegate !== undefined) {
|
|
66
|
+
keys.push(recordDelegateSomeSlot(p.recordDelegate));
|
|
67
|
+
}
|
|
68
|
+
else if (p.holderTokenAccount !== undefined) {
|
|
69
|
+
keys.push(recordDelegateNonePlaceholder(programId));
|
|
70
|
+
}
|
|
71
|
+
if (p.holderTokenAccount !== undefined) {
|
|
72
|
+
keys.push({ pubkey: toBase58(p.holderTokenAccount, "holderTokenAccount"), isSigner: false, isWritable: false });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Build `create_namespace_override`. Args: namespace_label (String) + value (Pubkey). */
|
|
76
|
+
export function buildCreateNamespaceOverrideIx(p) {
|
|
77
|
+
const data = concat([CREATE_NAMESPACE_OVERRIDE_DISCRIMINATOR, encString(p.namespaceLabel), toBytes32(p.value, "value")]);
|
|
78
|
+
const keys = [
|
|
79
|
+
{ pubkey: toBase58(p.payer, "payer"), isSigner: true, isWritable: true },
|
|
80
|
+
{ pubkey: toBase58(p.owner, "owner"), isSigner: true, isWritable: false },
|
|
81
|
+
{ pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: false },
|
|
82
|
+
{ pubkey: toBase58(p.namespace, "namespace"), isSigner: false, isWritable: false },
|
|
83
|
+
{ pubkey: toBase58(p.namespaceOverride, "namespaceOverride"), isSigner: false, isWritable: true },
|
|
84
|
+
{ pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
|
|
85
|
+
];
|
|
86
|
+
pushAuthorityTail(keys, p.programId, p);
|
|
87
|
+
return { programId: toBase58(p.programId, "programId"), keys, data };
|
|
88
|
+
}
|
|
89
|
+
/** Build `update_namespace_override` — change the target address. Arg: value (Pubkey). */
|
|
90
|
+
export function buildUpdateNamespaceOverrideIx(p) {
|
|
91
|
+
const data = concat([UPDATE_NAMESPACE_OVERRIDE_DISCRIMINATOR, toBytes32(p.value, "value")]);
|
|
92
|
+
const keys = [
|
|
93
|
+
{ pubkey: toBase58(p.owner, "owner"), isSigner: true, isWritable: false },
|
|
94
|
+
{ pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: false },
|
|
95
|
+
{ pubkey: toBase58(p.namespaceOverride, "namespaceOverride"), isSigner: false, isWritable: true },
|
|
96
|
+
];
|
|
97
|
+
pushAuthorityTail(keys, p.programId, p);
|
|
98
|
+
return { programId: toBase58(p.programId, "programId"), keys, data };
|
|
99
|
+
}
|
|
100
|
+
/** Build `close_namespace_override` — close the override, rent to recipient. No args. */
|
|
101
|
+
export function buildCloseNamespaceOverrideIx(p) {
|
|
102
|
+
const keys = [
|
|
103
|
+
{ pubkey: toBase58(p.owner, "owner"), isSigner: true, isWritable: false },
|
|
104
|
+
{ pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: false },
|
|
105
|
+
{ pubkey: toBase58(p.namespaceOverride, "namespaceOverride"), isSigner: false, isWritable: true },
|
|
106
|
+
{ pubkey: toBase58(p.recipient, "recipient"), isSigner: false, isWritable: true },
|
|
107
|
+
];
|
|
108
|
+
pushAuthorityTail(keys, p.programId, p);
|
|
109
|
+
return { programId: toBase58(p.programId, "programId"), keys, data: CLOSE_NAMESPACE_OVERRIDE_DISCRIMINATOR.slice() };
|
|
110
|
+
}
|
|
111
|
+
/** Decode a `NamespaceOverride` account. Layout: disc(8) | handle(32) |
|
|
112
|
+
* namespace(String: u32 len + bytes) | value(32) | updated_at(i64) | bump(1).
|
|
113
|
+
* Returns null if the data is too short / malformed. */
|
|
114
|
+
export function decodeNamespaceOverride(data) {
|
|
115
|
+
if (data.length < 8 + 32 + 4)
|
|
116
|
+
return null;
|
|
117
|
+
const dv = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
118
|
+
const handle = encodeBase58(data.subarray(8, 40));
|
|
119
|
+
const labelLen = dv.getUint32(40, true);
|
|
120
|
+
const labelStart = 44;
|
|
121
|
+
const labelEnd = labelStart + labelLen;
|
|
122
|
+
const valueEnd = labelEnd + 32;
|
|
123
|
+
const updatedEnd = valueEnd + 8;
|
|
124
|
+
if (data.length < updatedEnd + 1)
|
|
125
|
+
return null;
|
|
126
|
+
let namespace;
|
|
127
|
+
try {
|
|
128
|
+
namespace = new TextDecoder().decode(data.subarray(labelStart, labelEnd));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const value = encodeBase58(data.subarray(labelEnd, valueEnd));
|
|
134
|
+
const updatedAt = dv.getBigInt64(valueEnd, true);
|
|
135
|
+
const bump = data[updatedEnd];
|
|
136
|
+
return { handle, namespace, value, updatedAt, bump };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Fetch + decode a handle's namespace override for one label, applying the
|
|
140
|
+
* universal epoch-staleness rule — a `NamespaceOverride` PDA is reused across a
|
|
141
|
+
* release + re-register of the same handle, so an override from a PREVIOUS owner
|
|
142
|
+
* (stamped before `handleRegisteredAt`) must NOT count. Returns the live
|
|
143
|
+
* override, or null when there is none / it is stale / the account is malformed.
|
|
144
|
+
*
|
|
145
|
+
* GATED read path: this is NOT called by `resolve()` — namespaced resolution does
|
|
146
|
+
* not honor overrides yet (that is the cutover switch). Derive `overrideAccount`
|
|
147
|
+
* with `WasmResolver.deriveNamespaceOverrideAccount(handle, label, programId)`.
|
|
148
|
+
*/
|
|
149
|
+
export async function fetchNamespaceOverride(rpc, overrideAccount, handleRegisteredAt) {
|
|
150
|
+
const res = (await rpc("getAccountInfo", [
|
|
151
|
+
overrideAccount,
|
|
152
|
+
{ encoding: "base64", commitment: "confirmed" },
|
|
153
|
+
]));
|
|
154
|
+
const data0 = res?.value?.data?.[0];
|
|
155
|
+
if (!data0)
|
|
156
|
+
return null;
|
|
157
|
+
const bin = atob(data0);
|
|
158
|
+
const raw = new Uint8Array(bin.length);
|
|
159
|
+
for (let i = 0; i < bin.length; i++)
|
|
160
|
+
raw[i] = bin.charCodeAt(i);
|
|
161
|
+
const decoded = decodeNamespaceOverride(raw);
|
|
162
|
+
if (!decoded)
|
|
163
|
+
return null;
|
|
164
|
+
// Epoch-staleness: ignore an override left by a previous owner.
|
|
165
|
+
if (decoded.updatedAt < handleRegisteredAt)
|
|
166
|
+
return null;
|
|
167
|
+
return decoded;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* List all LIVE namespace overrides for one handle (a getProgramAccounts scan
|
|
171
|
+
* filtered by the account discriminator + the `handle` field at offset 8), with
|
|
172
|
+
* the epoch-staleness rule applied. Each entry includes its account address.
|
|
173
|
+
*
|
|
174
|
+
* GATED read path — not used by `resolve()` yet. Pass the handle's
|
|
175
|
+
* `registeredAt` so previous-owner overrides are dropped.
|
|
176
|
+
*/
|
|
177
|
+
export async function fetchNamespaceOverridesForHandle(rpc, programId, handleAccount, handleRegisteredAt) {
|
|
178
|
+
const res = (await rpc("getProgramAccounts", [
|
|
179
|
+
programId,
|
|
180
|
+
{
|
|
181
|
+
encoding: "base64",
|
|
182
|
+
commitment: "confirmed",
|
|
183
|
+
filters: [
|
|
184
|
+
{ memcmp: { offset: 0, bytes: encodeBase58(NAMESPACE_OVERRIDE_ACCOUNT_DISCRIMINATOR) } },
|
|
185
|
+
{ memcmp: { offset: 8, bytes: handleAccount } },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
]));
|
|
189
|
+
const out = [];
|
|
190
|
+
for (const a of res ?? []) {
|
|
191
|
+
const bin = atob(a.account.data[0]);
|
|
192
|
+
const raw = new Uint8Array(bin.length);
|
|
193
|
+
for (let i = 0; i < bin.length; i++)
|
|
194
|
+
raw[i] = bin.charCodeAt(i);
|
|
195
|
+
const decoded = decodeNamespaceOverride(raw);
|
|
196
|
+
// Defence in depth + staleness: the memcmp should guarantee the handle, but
|
|
197
|
+
// skip a mismatch or a previous-owner override.
|
|
198
|
+
if (decoded && decoded.handle === handleAccount && decoded.updatedAt >= handleRegisteredAt) {
|
|
199
|
+
out.push({ ...decoded, account: a.pubkey });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
out.sort((x, y) => x.namespace.localeCompare(y.namespace));
|
|
203
|
+
return out;
|
|
204
|
+
}
|
package/dist/register.d.ts
CHANGED
|
@@ -58,6 +58,36 @@ export interface RegisterParams {
|
|
|
58
58
|
* payer a price before they sign.
|
|
59
59
|
*/
|
|
60
60
|
export declare function buildRegisterIx(p: RegisterParams): BuiltInstruction;
|
|
61
|
+
/** Anchor instruction discriminator: `sha256("global:register_reserved")[0..8]`. */
|
|
62
|
+
export declare const REGISTER_RESERVED_DISCRIMINATOR: Uint8Array;
|
|
63
|
+
export interface RegisterReservedParams {
|
|
64
|
+
/** The registry program id. */
|
|
65
|
+
readonly programId: AddressLike;
|
|
66
|
+
/** `Config.admin` — the ONLY key this instruction accepts (the program's
|
|
67
|
+
* `has_one = admin` constraint). Signs AND pays the new `Handle` account's
|
|
68
|
+
* rent. On x1id today admin == treasury == the deploy key. */
|
|
69
|
+
readonly admin: AddressLike;
|
|
70
|
+
/** The `["config"]` PDA. */
|
|
71
|
+
readonly config: AddressLike;
|
|
72
|
+
/** The `["handle", name]` PDA being claimed — must not already exist
|
|
73
|
+
* (`init`). On success the handle's owner is set to `Config.treasury`. */
|
|
74
|
+
readonly handle: AddressLike;
|
|
75
|
+
/** Canonical form (`parseName(...).canonical`), MIN_LEN..=32 bytes. */
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly handleType: HandleTypeValue;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Build `register_reserved` — the admin-only, free registration of a reserved
|
|
81
|
+
* name INTO THE TREASURY (owner is set on-chain to `Config.treasury`, not to an
|
|
82
|
+
* arbitrary target). Admin-gated: the `admin` account must equal `Config.admin`
|
|
83
|
+
* and must sign. No price, no treasury-payment account — reserved registration
|
|
84
|
+
* is free; the admin just pays the new account's rent.
|
|
85
|
+
*
|
|
86
|
+
* Same hand-rolled shape as {@link buildRegisterIx}. Returns unsigned
|
|
87
|
+
* instruction data for the caller's runtime to assemble, sign (with the admin
|
|
88
|
+
* key) and submit.
|
|
89
|
+
*/
|
|
90
|
+
export declare function buildRegisterReservedIx(p: RegisterReservedParams): BuiltInstruction;
|
|
61
91
|
/** The `Config` fields `register_with_forti` needs beyond what
|
|
62
92
|
* {@link fetchConfigTreasury} already reads. `fortiRatePerLamport === 0n`
|
|
63
93
|
* means FORTI payment is not enabled on this deployment — the same
|
package/dist/register.js
CHANGED
|
@@ -103,6 +103,43 @@ export function buildRegisterIx(p) {
|
|
|
103
103
|
}
|
|
104
104
|
return { programId: toBase58(p.programId, "programId"), keys, data };
|
|
105
105
|
}
|
|
106
|
+
// ============================= register_reserved (#8404) =============================
|
|
107
|
+
/** Anchor instruction discriminator: `sha256("global:register_reserved")[0..8]`. */
|
|
108
|
+
export const REGISTER_RESERVED_DISCRIMINATOR = Uint8Array.from([
|
|
109
|
+
37, 151, 239, 242, 251, 26, 205, 224,
|
|
110
|
+
]);
|
|
111
|
+
/**
|
|
112
|
+
* Build `register_reserved` — the admin-only, free registration of a reserved
|
|
113
|
+
* name INTO THE TREASURY (owner is set on-chain to `Config.treasury`, not to an
|
|
114
|
+
* arbitrary target). Admin-gated: the `admin` account must equal `Config.admin`
|
|
115
|
+
* and must sign. No price, no treasury-payment account — reserved registration
|
|
116
|
+
* is free; the admin just pays the new account's rent.
|
|
117
|
+
*
|
|
118
|
+
* Same hand-rolled shape as {@link buildRegisterIx}. Returns unsigned
|
|
119
|
+
* instruction data for the caller's runtime to assemble, sign (with the admin
|
|
120
|
+
* key) and submit.
|
|
121
|
+
*/
|
|
122
|
+
export function buildRegisterReservedIx(p) {
|
|
123
|
+
if (!Number.isInteger(p.handleType) || p.handleType < 0 || p.handleType > 3) {
|
|
124
|
+
throw new Error("handleType must be 0 (Human), 1 (Merchant), 2 (Org) or 3 (Agent)");
|
|
125
|
+
}
|
|
126
|
+
const nameBytes = new TextEncoder().encode(p.name);
|
|
127
|
+
if (nameBytes.length < 1 || nameBytes.length > 32) {
|
|
128
|
+
throw new Error("name must be 1..=32 bytes (UTF-8)");
|
|
129
|
+
}
|
|
130
|
+
const data = new Uint8Array(8 + 4 + nameBytes.length + 1);
|
|
131
|
+
data.set(REGISTER_RESERVED_DISCRIMINATOR, 0);
|
|
132
|
+
new DataView(data.buffer).setUint32(8, nameBytes.length, true);
|
|
133
|
+
data.set(nameBytes, 12);
|
|
134
|
+
data[12 + nameBytes.length] = p.handleType;
|
|
135
|
+
const keys = [
|
|
136
|
+
{ pubkey: toBase58(p.admin, "admin"), isSigner: true, isWritable: true },
|
|
137
|
+
{ pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: true },
|
|
138
|
+
{ pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: true },
|
|
139
|
+
{ pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
|
|
140
|
+
];
|
|
141
|
+
return { programId: toBase58(p.programId, "programId"), keys, data };
|
|
142
|
+
}
|
|
106
143
|
// ============================= register_with_forti (#8136, WP #8200) =============================
|
|
107
144
|
/** Byte offsets of the `Config` fields `register_with_forti` needs, verbatim
|
|
108
145
|
* from state.rs's field order (confirmed against `migrate_config_v4`'s own
|