zcashname-sdk 0.8.3 → 0.8.5
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/zns.cjs +134 -135
- package/dist/zns.d.cts +78 -46
- package/dist/zns.d.ts +78 -46
- package/dist/zns.js +132 -131
- package/package.json +2 -2
package/dist/zns.cjs
CHANGED
|
@@ -33,35 +33,94 @@ __export(zns_exports, {
|
|
|
33
33
|
BUY_COMMISSION: () => BUY_COMMISSION,
|
|
34
34
|
DEFAULT_URL: () => DEFAULT_URL,
|
|
35
35
|
LIST_COMMISSION: () => LIST_COMMISSION,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
ZNS: () => ZNS,
|
|
39
|
-
ZNS_ACTIONS: () => ZNS_ACTIONS
|
|
36
|
+
NETWORKS: () => NETWORKS,
|
|
37
|
+
ZNS: () => ZNS
|
|
40
38
|
});
|
|
41
39
|
module.exports = __toCommonJS(zns_exports);
|
|
42
40
|
var ed25519 = __toESM(require("@noble/ed25519"), 1);
|
|
43
41
|
var import_bech32 = require("bech32");
|
|
44
42
|
|
|
45
43
|
// src/types.ts
|
|
46
|
-
var
|
|
47
|
-
var LIST_COMMISSION = 1e6;
|
|
44
|
+
var ZNS_ACTIONS = ["CLAIM", "BUY", "UPDATE", "LIST", "DELIST", "RELEASE"];
|
|
48
45
|
|
|
49
46
|
// src/zns.ts
|
|
50
47
|
var DEFAULT_URL = "https://light.zcash.me/zns-testnet";
|
|
51
|
-
var
|
|
52
|
-
var
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
var BUY_COMMISSION = 1e4;
|
|
49
|
+
var LIST_COMMISSION = 1e6;
|
|
50
|
+
var NETWORKS = {
|
|
51
|
+
testnet: {
|
|
52
|
+
url: "https://light.zcash.me/zns-testnet",
|
|
53
|
+
registryAddress: "utest1f32kn6c4zvn54xr8wfsnxmj9hzpu2mwgtxzpzwcw34906tdccdvzs0z2dx38lly7tpan77x6udt8pjczqm22ymsdhlz9j0tk5yq664nl",
|
|
54
|
+
uivk: "utest1hzw7wyadutvzfgpna80yftsk5l7jeyu2p5me5quvp28tytxueta00cx4068wnlzcv7tx9n3t3gfhsy83pe4y6jrhxtzaq0hj6xtg5zrk2dn7zen3vns2a5pgs4fxdjlletmqrhfa42"
|
|
55
|
+
},
|
|
56
|
+
mainnet: {
|
|
57
|
+
url: "https://light.zcash.me/zns",
|
|
58
|
+
registryAddress: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9",
|
|
59
|
+
uivk: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9"
|
|
60
|
+
}
|
|
57
61
|
};
|
|
58
|
-
var ZNS_ACTIONS = ["CLAIM", "BUY", "UPDATE", "LIST", "DELIST", "RELEASE"];
|
|
59
62
|
var NAME_RE = /^[a-z0-9]{1,62}$/;
|
|
63
|
+
function isValidName(name) {
|
|
64
|
+
return NAME_RE.test(name);
|
|
65
|
+
}
|
|
66
|
+
function normalizeApiResponse(obj) {
|
|
67
|
+
if (Array.isArray(obj)) return obj.map((item) => normalizeApiResponse(item));
|
|
68
|
+
if (obj && typeof obj === "object") {
|
|
69
|
+
return Object.fromEntries(
|
|
70
|
+
Object.entries(obj).map(([k, v]) => [
|
|
71
|
+
k.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
|
|
72
|
+
normalizeApiResponse(v)
|
|
73
|
+
])
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return obj;
|
|
77
|
+
}
|
|
60
78
|
function isWholeNumber(value) {
|
|
61
79
|
return /^\d+$/.test(value) && !value.startsWith("0") || value === "0";
|
|
62
80
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
81
|
+
function isValidUnifiedAddress(address) {
|
|
82
|
+
if (!address) return false;
|
|
83
|
+
if (address.startsWith("utest1")) return true;
|
|
84
|
+
if (address.startsWith("u1")) return true;
|
|
85
|
+
try {
|
|
86
|
+
const decoded = import_bech32.bech32m.decode(address);
|
|
87
|
+
return decoded.prefix === "u" || decoded.prefix === "utest";
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function isValidTransparentAddress(address) {
|
|
93
|
+
if (!address) return false;
|
|
94
|
+
const validPrefixes = ["t1", "t3", "tm", "tn"];
|
|
95
|
+
if (!validPrefixes.some((p) => address.startsWith(p))) return false;
|
|
96
|
+
if (address.length < 26 || address.length > 35) return false;
|
|
97
|
+
const base58Regex = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/;
|
|
98
|
+
return base58Regex.test(address);
|
|
99
|
+
}
|
|
100
|
+
var PAYLOAD_RULES = {
|
|
101
|
+
CLAIM: { format: "CLAIM:<name>:<ua>", checks: ["name", "ua"] },
|
|
102
|
+
BUY: { format: "BUY:<name>:<ua>", checks: ["name", "ua"] },
|
|
103
|
+
UPDATE: { format: "UPDATE:<name>:<ua>:<nonce>", checks: ["name", "ua", "nonce"] },
|
|
104
|
+
LIST: { format: "LIST:<name>:<price>:<pay_taddr>:<nonce>", checks: ["name", "price", "pay_taddr", "nonce"] },
|
|
105
|
+
DELIST: { format: "DELIST:<name>:<nonce>", checks: ["name", "nonce"] },
|
|
106
|
+
RELEASE: { format: "RELEASE:<name>:<nonce>", checks: ["name", "nonce"] }
|
|
107
|
+
};
|
|
108
|
+
function validateField(value, type) {
|
|
109
|
+
switch (type) {
|
|
110
|
+
case "name":
|
|
111
|
+
return NAME_RE.test(value) ? null : "Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.";
|
|
112
|
+
case "ua":
|
|
113
|
+
return isValidUnifiedAddress(value) ? null : `Invalid unified address: "${value}".`;
|
|
114
|
+
case "price":
|
|
115
|
+
return isWholeNumber(value) && Number(value) > 0 ? null : "Price must be a positive whole number in zats.";
|
|
116
|
+
case "nonce":
|
|
117
|
+
return isWholeNumber(value) ? null : "Nonce must be a whole number.";
|
|
118
|
+
case "pay_taddr":
|
|
119
|
+
return isValidTransparentAddress(value) ? null : `Invalid transparent address: "${value}".`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function buildValidationResult(level, action, message) {
|
|
123
|
+
return { valid: level === "valid", action, message, level };
|
|
65
124
|
}
|
|
66
125
|
var ZNS = class {
|
|
67
126
|
/**
|
|
@@ -73,16 +132,28 @@ var ZNS = class {
|
|
|
73
132
|
constructor(options) {
|
|
74
133
|
this.rpcId = 0;
|
|
75
134
|
this._verified = false;
|
|
135
|
+
/** Validate a Zcash Unified Address format.
|
|
136
|
+
* Accepts both mainnet ('u') and testnet ('utest') prefixes.
|
|
137
|
+
* Performs basic format validation but NOT full bech32m checksum verification.
|
|
138
|
+
* Returns true if the address looks like a unified address, false otherwise.
|
|
139
|
+
*
|
|
140
|
+
* @todo(F4Jumble) Upgrade to full ZIP-316 decoding with F4Jumble to:
|
|
141
|
+
* - Parse actual typecodes from address items
|
|
142
|
+
* - Validate F4Jumble checksum (not just bech32m)
|
|
143
|
+
* - Optionally enforce: address must contain at least one Orchard receiver (typecode 0x03)
|
|
144
|
+
* Requires @noble/hashes (blake2b) implementation of F4Jumble inverse. */
|
|
145
|
+
this.isValidUnifiedAddress = isValidUnifiedAddress;
|
|
146
|
+
this.isValidTransparentAddress = isValidTransparentAddress;
|
|
76
147
|
this.network = options?.network ?? "testnet";
|
|
77
|
-
this.url = options?.url ??
|
|
148
|
+
this.url = options?.url ?? NETWORKS[this.network].url;
|
|
78
149
|
}
|
|
79
150
|
/**
|
|
80
151
|
* Verifies that the connected server is a known ZNS instance.
|
|
81
152
|
* @throws Error if the server's UIVK is not recognized
|
|
82
153
|
*/
|
|
83
154
|
async verify() {
|
|
84
|
-
const status = await this.
|
|
85
|
-
if (
|
|
155
|
+
const status = await this.status();
|
|
156
|
+
if (status.uivk !== NETWORKS[this.network].uivk) {
|
|
86
157
|
throw new Error(
|
|
87
158
|
`UIVK mismatch: indexer returned "${status.uivk.slice(0, 20)}..." which is not a known ZNS instance`
|
|
88
159
|
);
|
|
@@ -95,72 +166,61 @@ var ZNS = class {
|
|
|
95
166
|
}
|
|
96
167
|
/** Get the registry address for the current network. */
|
|
97
168
|
get registryAddress() {
|
|
98
|
-
|
|
99
|
-
if (!addr) {
|
|
100
|
-
throw new Error(`Unknown network: ${this.network}`);
|
|
101
|
-
}
|
|
102
|
-
return addr;
|
|
169
|
+
return NETWORKS[this.network].registryAddress;
|
|
103
170
|
}
|
|
104
171
|
/** Fetch current server status including pricing and configuration. */
|
|
105
172
|
async status() {
|
|
106
|
-
|
|
173
|
+
const raw = await this.rpc("status");
|
|
174
|
+
return normalizeApiResponse(raw);
|
|
107
175
|
}
|
|
108
176
|
/** Resolve a ZNS name to its registration. Returns null if not registered. */
|
|
109
177
|
async resolveName(name) {
|
|
110
|
-
|
|
178
|
+
const raw = await this.rpc("resolve", { query: name });
|
|
179
|
+
return raw ? normalizeApiResponse(raw) : null;
|
|
111
180
|
}
|
|
112
181
|
/** Resolve a Zcash Unified Address to all names pointing to it. Returns empty array if none.
|
|
113
182
|
* Supports pagination with limit (default 50, max 500) and offset (default 0). */
|
|
114
183
|
async resolveAddress(address, limit, offset) {
|
|
115
|
-
|
|
184
|
+
const raw = await this.rpc("resolve", {
|
|
116
185
|
query: address,
|
|
117
186
|
limit,
|
|
118
187
|
offset
|
|
119
188
|
});
|
|
189
|
+
return raw.map((r) => normalizeApiResponse(r));
|
|
120
190
|
}
|
|
121
191
|
/** List all registered names. Useful for explorers or browsers.
|
|
122
192
|
* Supports pagination with limit (default 50, max 500) and offset (default 0). */
|
|
123
193
|
async listAllRegistrations(limit, offset) {
|
|
124
|
-
|
|
194
|
+
const raw = await this.rpc("resolve", {
|
|
125
195
|
query: "",
|
|
126
196
|
limit,
|
|
127
197
|
offset
|
|
128
198
|
});
|
|
199
|
+
return raw.map((r) => normalizeApiResponse(r));
|
|
129
200
|
}
|
|
130
201
|
/** Check if a name is available for registration.
|
|
131
202
|
* Returns false immediately for invalid names without hitting the server. */
|
|
132
203
|
async isAvailable(name) {
|
|
133
|
-
if (!
|
|
204
|
+
if (!isValidName(name)) return false;
|
|
134
205
|
const result = await this.resolveName(name);
|
|
135
206
|
return result === null;
|
|
136
207
|
}
|
|
137
|
-
/** Validate a Zcash Unified Address format.
|
|
138
|
-
* Accepts both mainnet ('u') and testnet ('utest') prefixes.
|
|
139
|
-
* Performs basic format validation but NOT full bech32m checksum verification.
|
|
140
|
-
* Returns true if the address looks like a unified address, false otherwise. */
|
|
141
|
-
isValidUnifiedAddress(address) {
|
|
142
|
-
if (!address) return false;
|
|
143
|
-
if (address.startsWith("utest1")) return true;
|
|
144
|
-
if (address.startsWith("u1")) return true;
|
|
145
|
-
try {
|
|
146
|
-
const decoded = import_bech32.bech32m.decode(address);
|
|
147
|
-
return decoded.prefix === "u" || decoded.prefix === "utest";
|
|
148
|
-
} catch {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
208
|
async listings(limit, offset) {
|
|
153
|
-
const
|
|
209
|
+
const raw = await this.rpc("listings", {
|
|
154
210
|
limit,
|
|
155
211
|
offset
|
|
156
212
|
});
|
|
157
|
-
return
|
|
213
|
+
return {
|
|
214
|
+
listings: raw.listings.map((l) => normalizeApiResponse(l)),
|
|
215
|
+
total: raw.total
|
|
216
|
+
};
|
|
158
217
|
}
|
|
159
218
|
async events(filter) {
|
|
160
|
-
|
|
219
|
+
const raw = await this.rpc(
|
|
161
220
|
"events",
|
|
162
|
-
filter ?? {}
|
|
221
|
+
normalizeApiResponse(filter ?? {})
|
|
163
222
|
);
|
|
223
|
+
return normalizeApiResponse(raw);
|
|
164
224
|
}
|
|
165
225
|
/**
|
|
166
226
|
* Verify a listing's signature.
|
|
@@ -170,7 +230,7 @@ var ZNS = class {
|
|
|
170
230
|
*/
|
|
171
231
|
async verifyListing(listing, adminPubkey) {
|
|
172
232
|
const pubkey = listing.pubkey ?? adminPubkey;
|
|
173
|
-
const payload = `LIST:${listing.name}:${listing.price}:${listing.
|
|
233
|
+
const payload = `LIST:${listing.name}:${listing.price}:${listing.payTaddr}:${listing.nonce}`;
|
|
174
234
|
return this.verifyEd25519(payload, listing.signature, pubkey);
|
|
175
235
|
}
|
|
176
236
|
/**
|
|
@@ -186,10 +246,6 @@ var ZNS = class {
|
|
|
186
246
|
if (!payload) return false;
|
|
187
247
|
return this.verifyEd25519(payload, reg.signature, pubkey);
|
|
188
248
|
}
|
|
189
|
-
/** Check if a name is valid format (lowercase alphanumeric, 1-62 chars). */
|
|
190
|
-
isValidName(name) {
|
|
191
|
-
return NAME_RE.test(name);
|
|
192
|
-
}
|
|
193
249
|
/**
|
|
194
250
|
* Validate a signing payload string against the ZNS memo format spec.
|
|
195
251
|
*
|
|
@@ -216,7 +272,6 @@ var ZNS = class {
|
|
|
216
272
|
return {
|
|
217
273
|
valid: false,
|
|
218
274
|
action: "",
|
|
219
|
-
canonicalAction: null,
|
|
220
275
|
message: "Empty payload.",
|
|
221
276
|
level: "invalid"
|
|
222
277
|
};
|
|
@@ -226,83 +281,29 @@ var ZNS = class {
|
|
|
226
281
|
return {
|
|
227
282
|
valid: false,
|
|
228
283
|
action: raw.toUpperCase(),
|
|
229
|
-
canonicalAction: null,
|
|
230
284
|
message: "Missing colon separator. Expected format: ACTION:field1:field2:...",
|
|
231
285
|
level: "invalid"
|
|
232
286
|
};
|
|
233
287
|
}
|
|
234
|
-
const
|
|
235
|
-
const actionLower = raw.slice(0, colonIdx).toLowerCase();
|
|
288
|
+
const action = raw.slice(0, colonIdx).toUpperCase();
|
|
236
289
|
const rest = raw.slice(colonIdx + 1);
|
|
237
290
|
const parts = rest.split(":");
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return mk("invalid", actionUpper, "buy", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
253
|
-
if (!this.isValidUnifiedAddress(parts[1]))
|
|
254
|
-
return mk("invalid", actionUpper, "buy", `Invalid unified address: "${parts[1]}".`);
|
|
255
|
-
return mk("valid", actionUpper, "buy", "Valid BUY payload.");
|
|
256
|
-
}
|
|
257
|
-
case "update": {
|
|
258
|
-
if (parts.length !== 3)
|
|
259
|
-
return mk("invalid", actionUpper, "update", `Expected UPDATE:<name>:<ua>:<nonce>.`);
|
|
260
|
-
if (!NAME_RE.test(parts[0]))
|
|
261
|
-
return mk("invalid", actionUpper, "update", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
262
|
-
if (!this.isValidUnifiedAddress(parts[1]))
|
|
263
|
-
return mk("invalid", actionUpper, "update", `Invalid unified address: "${parts[1]}".`);
|
|
264
|
-
if (!isWholeNumber(parts[2]))
|
|
265
|
-
return mk("invalid", actionUpper, "update", `Nonce must be a whole number.`);
|
|
266
|
-
return mk("valid", actionUpper, "update", "Valid UPDATE payload.");
|
|
267
|
-
}
|
|
268
|
-
case "list": {
|
|
269
|
-
if (parts.length !== 4)
|
|
270
|
-
return mk("invalid", actionUpper, "list", `Expected LIST:<name>:<price_zats>:<pay_taddr>:<nonce>.`);
|
|
271
|
-
if (!NAME_RE.test(parts[0]))
|
|
272
|
-
return mk("invalid", actionUpper, "list", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
273
|
-
if (!isWholeNumber(parts[1]) || Number(parts[1]) <= 0)
|
|
274
|
-
return mk("invalid", actionUpper, "list", `Price must be a positive whole number in zats.`);
|
|
275
|
-
if (!isWholeNumber(parts[3]))
|
|
276
|
-
return mk("invalid", actionUpper, "list", `Nonce must be a whole number.`);
|
|
277
|
-
return mk("valid", actionUpper, "list", "Valid LIST payload.");
|
|
278
|
-
}
|
|
279
|
-
case "delist": {
|
|
280
|
-
if (parts.length !== 2)
|
|
281
|
-
return mk("invalid", actionUpper, "delist", `Expected DELIST:<name>:<nonce>.`);
|
|
282
|
-
if (!NAME_RE.test(parts[0]))
|
|
283
|
-
return mk("invalid", actionUpper, "delist", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
284
|
-
if (!isWholeNumber(parts[1]))
|
|
285
|
-
return mk("invalid", actionUpper, "delist", `Nonce must be a whole number.`);
|
|
286
|
-
return mk("valid", actionUpper, "delist", "Valid DELIST payload.");
|
|
287
|
-
}
|
|
288
|
-
case "release": {
|
|
289
|
-
if (parts.length !== 2)
|
|
290
|
-
return mk("invalid", actionUpper, "release", `Expected RELEASE:<name>:<nonce>.`);
|
|
291
|
-
if (!NAME_RE.test(parts[0]))
|
|
292
|
-
return mk("invalid", actionUpper, "release", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
293
|
-
if (!isWholeNumber(parts[1]))
|
|
294
|
-
return mk("invalid", actionUpper, "release", `Nonce must be a whole number.`);
|
|
295
|
-
return mk("valid", actionUpper, "release", "Valid RELEASE payload.");
|
|
296
|
-
}
|
|
297
|
-
default:
|
|
298
|
-
return {
|
|
299
|
-
valid: false,
|
|
300
|
-
action: actionUpper,
|
|
301
|
-
canonicalAction: null,
|
|
302
|
-
message: `Unrecognized action "${actionUpper}". Valid actions: ${ZNS_ACTIONS.join(", ")}.`,
|
|
303
|
-
level: "unrecognized"
|
|
304
|
-
};
|
|
291
|
+
if (!ZNS_ACTIONS.includes(action)) {
|
|
292
|
+
return {
|
|
293
|
+
valid: false,
|
|
294
|
+
action,
|
|
295
|
+
message: `Unrecognized action "${action}". Valid actions: ${ZNS_ACTIONS.join(", ")}.`,
|
|
296
|
+
level: "unrecognized"
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const rule = PAYLOAD_RULES[action];
|
|
300
|
+
if (parts.length !== rule.checks.length)
|
|
301
|
+
return buildValidationResult("invalid", action, `Expected ${rule.format}.`);
|
|
302
|
+
for (let i = 0; i < rule.checks.length; i++) {
|
|
303
|
+
const err = validateField(parts[i], rule.checks[i]);
|
|
304
|
+
if (err) return buildValidationResult("invalid", action, err);
|
|
305
305
|
}
|
|
306
|
+
return buildValidationResult("valid", action, `Valid ${action} payload.`);
|
|
306
307
|
}
|
|
307
308
|
/**
|
|
308
309
|
* Get the claim cost in zatoshis for a name of given length.
|
|
@@ -345,7 +346,7 @@ var ZNS = class {
|
|
|
345
346
|
*/
|
|
346
347
|
prepareClaim(name, address, cost) {
|
|
347
348
|
this.requireValidName(name);
|
|
348
|
-
if (!
|
|
349
|
+
if (!isValidUnifiedAddress(address)) {
|
|
349
350
|
throw new Error(`Invalid Zcash Unified Address: ${address}`);
|
|
350
351
|
}
|
|
351
352
|
return {
|
|
@@ -360,16 +361,16 @@ var ZNS = class {
|
|
|
360
361
|
}
|
|
361
362
|
};
|
|
362
363
|
}
|
|
363
|
-
prepareList(name, price,
|
|
364
|
+
prepareList(name, price, payTaddr, nonce) {
|
|
364
365
|
this.requireValidName(name);
|
|
365
366
|
return {
|
|
366
367
|
name,
|
|
367
368
|
price,
|
|
368
|
-
|
|
369
|
+
payTaddr,
|
|
369
370
|
nonce,
|
|
370
|
-
payload: `LIST:${name}:${price}:${
|
|
371
|
+
payload: `LIST:${name}:${price}:${payTaddr}:${nonce}`,
|
|
371
372
|
complete: (signature, userPubkey) => {
|
|
372
|
-
const memo = userPubkey ? `ZNS:LIST:${name}:${price}:${
|
|
373
|
+
const memo = userPubkey ? `ZNS:LIST:${name}:${price}:${payTaddr}:${nonce}:${signature}:${userPubkey}` : `ZNS:LIST:${name}:${price}:${payTaddr}:${nonce}:${signature}`;
|
|
373
374
|
return { memo, uri: this.buildZcashUri(this.registryAddress, LIST_COMMISSION, memo) };
|
|
374
375
|
}
|
|
375
376
|
};
|
|
@@ -388,7 +389,7 @@ var ZNS = class {
|
|
|
388
389
|
}
|
|
389
390
|
prepareUpdate(name, newAddress, nonce) {
|
|
390
391
|
this.requireValidName(name);
|
|
391
|
-
if (!
|
|
392
|
+
if (!isValidUnifiedAddress(newAddress)) {
|
|
392
393
|
throw new Error(`Invalid Zcash Unified Address: ${newAddress}`);
|
|
393
394
|
}
|
|
394
395
|
return {
|
|
@@ -443,7 +444,7 @@ var ZNS = class {
|
|
|
443
444
|
}
|
|
444
445
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
445
446
|
registrationPayload(reg) {
|
|
446
|
-
switch (reg.
|
|
447
|
+
switch (reg.lastAction) {
|
|
447
448
|
case "CLAIM":
|
|
448
449
|
return `CLAIM:${reg.name}:${reg.address}`;
|
|
449
450
|
case "BUY":
|
|
@@ -470,7 +471,7 @@ var ZNS = class {
|
|
|
470
471
|
}
|
|
471
472
|
}
|
|
472
473
|
requireValidName(name) {
|
|
473
|
-
if (!
|
|
474
|
+
if (!isValidName(name)) throw new Error(`Invalid ZNS name: ${name}`);
|
|
474
475
|
}
|
|
475
476
|
/** Build a ZIP-321 URI. Amount is in zatoshis and will be converted to ZEC for the URI. */
|
|
476
477
|
buildZcashUri(address, amountZats, memo) {
|
|
@@ -536,8 +537,6 @@ var ZNS = class {
|
|
|
536
537
|
BUY_COMMISSION,
|
|
537
538
|
DEFAULT_URL,
|
|
538
539
|
LIST_COMMISSION,
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
ZNS,
|
|
542
|
-
ZNS_ACTIONS
|
|
540
|
+
NETWORKS,
|
|
541
|
+
ZNS
|
|
543
542
|
});
|
package/dist/zns.d.cts
CHANGED
|
@@ -1,59 +1,70 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* ZNS TypeScript SDK types.
|
|
3
|
+
*
|
|
4
|
+
* API responses are converted from snake_case (Rust convention) to camelCase
|
|
5
|
+
* (TypeScript convention) using the snakeToCamel utility. Types below are the
|
|
6
|
+
* user-facing shape — Raw* types and manual converters have been removed.
|
|
7
|
+
*/
|
|
4
8
|
type Zats = number;
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/** Target network for ZNS operations. */
|
|
10
|
+
type Network = "testnet" | "mainnet";
|
|
11
|
+
/** All user-signable ZNS actions. Single source of truth — ZnsAction is derived from this. */
|
|
12
|
+
declare const ZNS_ACTIONS: readonly ["CLAIM", "BUY", "UPDATE", "LIST", "DELIST", "RELEASE"];
|
|
13
|
+
/** Union of all user-signable action names. Derived from ZNS_ACTIONS. */
|
|
14
|
+
type ZnsAction = (typeof ZNS_ACTIONS)[number];
|
|
15
|
+
/** Ownership-changing actions — can appear as Registration.lastAction. */
|
|
16
|
+
type LastAction = Exclude<ZnsAction, "LIST">;
|
|
17
|
+
/** All actions that appear in the event log — user actions plus admin SETPRICE. */
|
|
18
|
+
type EventAction = ZnsAction | "SETPRICE";
|
|
19
|
+
/** Pending purchase for a listed name. */
|
|
20
|
+
interface PendingBuy {
|
|
21
|
+
buyer: string;
|
|
22
|
+
price: Zats;
|
|
23
|
+
claimHeight: number;
|
|
24
|
+
expiresAt: number;
|
|
13
25
|
txid: string;
|
|
14
|
-
height: number;
|
|
15
|
-
nonce: number;
|
|
16
|
-
signature: string | null;
|
|
17
|
-
last_action: LastAction;
|
|
18
|
-
pubkey: string | null;
|
|
19
|
-
listing: Listing | null;
|
|
20
26
|
}
|
|
21
|
-
/**
|
|
22
|
-
type LastAction = "CLAIM" | "BUY" | "UPDATE" | "DELIST" | "RELEASE";
|
|
23
|
-
/** All actions that can appear in the Event log (includes non-ownership actions like LIST) */
|
|
24
|
-
type EventAction = "CLAIM" | "LIST" | "DELIST" | "RELEASE" | "UPDATE" | "BUY" | "SETPRICE";
|
|
27
|
+
/** A name listing in the marketplace. */
|
|
25
28
|
interface Listing {
|
|
26
29
|
name: string;
|
|
27
30
|
price: Zats;
|
|
28
|
-
|
|
31
|
+
payTaddr: string;
|
|
29
32
|
nonce: number;
|
|
30
33
|
txid: string;
|
|
31
34
|
height: number;
|
|
32
35
|
signature: string;
|
|
33
36
|
pubkey: string | null;
|
|
34
|
-
|
|
37
|
+
pendingBuy: PendingBuy | undefined;
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
expires_at: number;
|
|
39
|
+
/** A registered ZNS name. */
|
|
40
|
+
interface Registration {
|
|
41
|
+
name: string;
|
|
42
|
+
address: string;
|
|
41
43
|
txid: string;
|
|
44
|
+
height: number;
|
|
45
|
+
nonce: number;
|
|
46
|
+
signature: string | null;
|
|
47
|
+
lastAction: LastAction;
|
|
48
|
+
pubkey: string | null;
|
|
49
|
+
listing: Listing | null;
|
|
42
50
|
}
|
|
51
|
+
/** Pricing tiers for name registration. */
|
|
43
52
|
interface Pricing {
|
|
44
53
|
nonce: number;
|
|
45
54
|
height: number;
|
|
46
55
|
tiers: Zats[];
|
|
47
56
|
}
|
|
57
|
+
/** Current indexer status including pricing configuration. */
|
|
48
58
|
interface Status {
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
syncedHeight: number;
|
|
60
|
+
adminPubkey: string;
|
|
51
61
|
uivk: string;
|
|
52
62
|
address: string;
|
|
53
63
|
registered: number;
|
|
54
64
|
listed: number;
|
|
55
65
|
pricing: Pricing | null;
|
|
56
66
|
}
|
|
67
|
+
/** An event in the ZNS event log. */
|
|
57
68
|
interface Event {
|
|
58
69
|
id: number;
|
|
59
70
|
name: string;
|
|
@@ -66,13 +77,15 @@ interface Event {
|
|
|
66
77
|
signature: string | null;
|
|
67
78
|
pubkey: string | null;
|
|
68
79
|
}
|
|
80
|
+
/** Filter options for querying events. */
|
|
69
81
|
interface EventsFilter {
|
|
70
82
|
name?: string;
|
|
71
83
|
action?: EventAction;
|
|
72
|
-
|
|
84
|
+
sinceHeight?: number;
|
|
73
85
|
limit?: number;
|
|
74
86
|
offset?: number;
|
|
75
87
|
}
|
|
88
|
+
/** Paginated events result. */
|
|
76
89
|
interface EventsResult {
|
|
77
90
|
events: Event[];
|
|
78
91
|
total: number;
|
|
@@ -103,7 +116,7 @@ interface PreparedClaim extends PreparedAction {
|
|
|
103
116
|
interface PreparedList extends PreparedAction {
|
|
104
117
|
readonly name: string;
|
|
105
118
|
readonly price: Zats;
|
|
106
|
-
readonly
|
|
119
|
+
readonly payTaddr: string;
|
|
107
120
|
readonly nonce: number;
|
|
108
121
|
}
|
|
109
122
|
/** Prepared DELIST action */
|
|
@@ -142,8 +155,6 @@ interface PayloadValidationResult {
|
|
|
142
155
|
readonly valid: boolean;
|
|
143
156
|
/** Parsed action name (uppercase), e.g. "CLAIM", "LIST" */
|
|
144
157
|
readonly action: string;
|
|
145
|
-
/** Canonical action used internally (lowercase), e.g. "claim", "list" */
|
|
146
|
-
readonly canonicalAction: string | null;
|
|
147
158
|
/** Human-readable validation message */
|
|
148
159
|
readonly message: string;
|
|
149
160
|
/** Validation level: valid | invalid | unrecognized */
|
|
@@ -151,12 +162,28 @@ interface PayloadValidationResult {
|
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
declare const DEFAULT_URL = "https://light.zcash.me/zns-testnet";
|
|
154
|
-
|
|
155
|
-
declare const
|
|
156
|
-
/**
|
|
157
|
-
*
|
|
158
|
-
declare const
|
|
159
|
-
|
|
165
|
+
/** Commission sent with a BUY claim memo (0.0001 ZEC = 10,000 zats). */
|
|
166
|
+
declare const BUY_COMMISSION: Zats;
|
|
167
|
+
/** Listing commission sent with a LIST memo (0.01 ZEC = 1,000,000 zats).
|
|
168
|
+
* Mirrors the indexer's formula: min_tier × 1000. */
|
|
169
|
+
declare const LIST_COMMISSION: Zats;
|
|
170
|
+
/** Network-specific configuration for ZNS. */
|
|
171
|
+
declare const NETWORKS: {
|
|
172
|
+
readonly testnet: {
|
|
173
|
+
readonly url: "https://light.zcash.me/zns-testnet";
|
|
174
|
+
readonly registryAddress: "utest1f32kn6c4zvn54xr8wfsnxmj9hzpu2mwgtxzpzwcw34906tdccdvzs0z2dx38lly7tpan77x6udt8pjczqm22ymsdhlz9j0tk5yq664nl";
|
|
175
|
+
readonly uivk: "utest1hzw7wyadutvzfgpna80yftsk5l7jeyu2p5me5quvp28tytxueta00cx4068wnlzcv7tx9n3t3gfhsy83pe4y6jrhxtzaq0hj6xtg5zrk2dn7zen3vns2a5pgs4fxdjlletmqrhfa42";
|
|
176
|
+
};
|
|
177
|
+
readonly mainnet: {
|
|
178
|
+
readonly url: "https://light.zcash.me/zns";
|
|
179
|
+
readonly registryAddress: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9";
|
|
180
|
+
readonly uivk: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9";
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
/** Validates a Zcash unified (u-) address. */
|
|
184
|
+
declare function isValidUnifiedAddress(address: string): boolean;
|
|
185
|
+
/** Validates a Zcash transparent (t-) address. */
|
|
186
|
+
declare function isValidTransparentAddress(address: string): boolean;
|
|
160
187
|
declare class ZNS {
|
|
161
188
|
private url;
|
|
162
189
|
private network;
|
|
@@ -197,8 +224,15 @@ declare class ZNS {
|
|
|
197
224
|
/** Validate a Zcash Unified Address format.
|
|
198
225
|
* Accepts both mainnet ('u') and testnet ('utest') prefixes.
|
|
199
226
|
* Performs basic format validation but NOT full bech32m checksum verification.
|
|
200
|
-
* Returns true if the address looks like a unified address, false otherwise.
|
|
201
|
-
|
|
227
|
+
* Returns true if the address looks like a unified address, false otherwise.
|
|
228
|
+
*
|
|
229
|
+
* @todo(F4Jumble) Upgrade to full ZIP-316 decoding with F4Jumble to:
|
|
230
|
+
* - Parse actual typecodes from address items
|
|
231
|
+
* - Validate F4Jumble checksum (not just bech32m)
|
|
232
|
+
* - Optionally enforce: address must contain at least one Orchard receiver (typecode 0x03)
|
|
233
|
+
* Requires @noble/hashes (blake2b) implementation of F4Jumble inverse. */
|
|
234
|
+
isValidUnifiedAddress: typeof isValidUnifiedAddress;
|
|
235
|
+
isValidTransparentAddress: typeof isValidTransparentAddress;
|
|
202
236
|
listings(limit?: number, offset?: number): Promise<{
|
|
203
237
|
listings: Listing[];
|
|
204
238
|
total: number;
|
|
@@ -218,8 +252,6 @@ declare class ZNS {
|
|
|
218
252
|
* @returns true if the signature is valid
|
|
219
253
|
*/
|
|
220
254
|
verifyRegistration(reg: Registration, adminPubkey: string): Promise<boolean>;
|
|
221
|
-
/** Check if a name is valid format (lowercase alphanumeric, 1-62 chars). */
|
|
222
|
-
isValidName(name: string): boolean;
|
|
223
255
|
/**
|
|
224
256
|
* Validate a signing payload string against the ZNS memo format spec.
|
|
225
257
|
*
|
|
@@ -269,7 +301,7 @@ declare class ZNS {
|
|
|
269
301
|
* @returns Prepared claim ready for signature completion
|
|
270
302
|
*/
|
|
271
303
|
prepareClaim(name: string, address: string, cost: Zats): PreparedClaim;
|
|
272
|
-
prepareList(name: string, price: Zats,
|
|
304
|
+
prepareList(name: string, price: Zats, payTaddr: string, nonce: number): PreparedList;
|
|
273
305
|
prepareDelist(name: string, nonce: number): PreparedDelist;
|
|
274
306
|
prepareUpdate(name: string, newAddress: string, nonce: number): PreparedUpdate;
|
|
275
307
|
prepareBuy(name: string, buyerAddress: string, price: Zats): PreparedBuy;
|
|
@@ -286,4 +318,4 @@ declare class ZNS {
|
|
|
286
318
|
private rpc;
|
|
287
319
|
}
|
|
288
320
|
|
|
289
|
-
export { BUY_COMMISSION,
|
|
321
|
+
export { BUY_COMMISSION, DEFAULT_URL, LIST_COMMISSION, NETWORKS, ZNS };
|
package/dist/zns.d.ts
CHANGED
|
@@ -1,59 +1,70 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* ZNS TypeScript SDK types.
|
|
3
|
+
*
|
|
4
|
+
* API responses are converted from snake_case (Rust convention) to camelCase
|
|
5
|
+
* (TypeScript convention) using the snakeToCamel utility. Types below are the
|
|
6
|
+
* user-facing shape — Raw* types and manual converters have been removed.
|
|
7
|
+
*/
|
|
4
8
|
type Zats = number;
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/** Target network for ZNS operations. */
|
|
10
|
+
type Network = "testnet" | "mainnet";
|
|
11
|
+
/** All user-signable ZNS actions. Single source of truth — ZnsAction is derived from this. */
|
|
12
|
+
declare const ZNS_ACTIONS: readonly ["CLAIM", "BUY", "UPDATE", "LIST", "DELIST", "RELEASE"];
|
|
13
|
+
/** Union of all user-signable action names. Derived from ZNS_ACTIONS. */
|
|
14
|
+
type ZnsAction = (typeof ZNS_ACTIONS)[number];
|
|
15
|
+
/** Ownership-changing actions — can appear as Registration.lastAction. */
|
|
16
|
+
type LastAction = Exclude<ZnsAction, "LIST">;
|
|
17
|
+
/** All actions that appear in the event log — user actions plus admin SETPRICE. */
|
|
18
|
+
type EventAction = ZnsAction | "SETPRICE";
|
|
19
|
+
/** Pending purchase for a listed name. */
|
|
20
|
+
interface PendingBuy {
|
|
21
|
+
buyer: string;
|
|
22
|
+
price: Zats;
|
|
23
|
+
claimHeight: number;
|
|
24
|
+
expiresAt: number;
|
|
13
25
|
txid: string;
|
|
14
|
-
height: number;
|
|
15
|
-
nonce: number;
|
|
16
|
-
signature: string | null;
|
|
17
|
-
last_action: LastAction;
|
|
18
|
-
pubkey: string | null;
|
|
19
|
-
listing: Listing | null;
|
|
20
26
|
}
|
|
21
|
-
/**
|
|
22
|
-
type LastAction = "CLAIM" | "BUY" | "UPDATE" | "DELIST" | "RELEASE";
|
|
23
|
-
/** All actions that can appear in the Event log (includes non-ownership actions like LIST) */
|
|
24
|
-
type EventAction = "CLAIM" | "LIST" | "DELIST" | "RELEASE" | "UPDATE" | "BUY" | "SETPRICE";
|
|
27
|
+
/** A name listing in the marketplace. */
|
|
25
28
|
interface Listing {
|
|
26
29
|
name: string;
|
|
27
30
|
price: Zats;
|
|
28
|
-
|
|
31
|
+
payTaddr: string;
|
|
29
32
|
nonce: number;
|
|
30
33
|
txid: string;
|
|
31
34
|
height: number;
|
|
32
35
|
signature: string;
|
|
33
36
|
pubkey: string | null;
|
|
34
|
-
|
|
37
|
+
pendingBuy: PendingBuy | undefined;
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
expires_at: number;
|
|
39
|
+
/** A registered ZNS name. */
|
|
40
|
+
interface Registration {
|
|
41
|
+
name: string;
|
|
42
|
+
address: string;
|
|
41
43
|
txid: string;
|
|
44
|
+
height: number;
|
|
45
|
+
nonce: number;
|
|
46
|
+
signature: string | null;
|
|
47
|
+
lastAction: LastAction;
|
|
48
|
+
pubkey: string | null;
|
|
49
|
+
listing: Listing | null;
|
|
42
50
|
}
|
|
51
|
+
/** Pricing tiers for name registration. */
|
|
43
52
|
interface Pricing {
|
|
44
53
|
nonce: number;
|
|
45
54
|
height: number;
|
|
46
55
|
tiers: Zats[];
|
|
47
56
|
}
|
|
57
|
+
/** Current indexer status including pricing configuration. */
|
|
48
58
|
interface Status {
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
syncedHeight: number;
|
|
60
|
+
adminPubkey: string;
|
|
51
61
|
uivk: string;
|
|
52
62
|
address: string;
|
|
53
63
|
registered: number;
|
|
54
64
|
listed: number;
|
|
55
65
|
pricing: Pricing | null;
|
|
56
66
|
}
|
|
67
|
+
/** An event in the ZNS event log. */
|
|
57
68
|
interface Event {
|
|
58
69
|
id: number;
|
|
59
70
|
name: string;
|
|
@@ -66,13 +77,15 @@ interface Event {
|
|
|
66
77
|
signature: string | null;
|
|
67
78
|
pubkey: string | null;
|
|
68
79
|
}
|
|
80
|
+
/** Filter options for querying events. */
|
|
69
81
|
interface EventsFilter {
|
|
70
82
|
name?: string;
|
|
71
83
|
action?: EventAction;
|
|
72
|
-
|
|
84
|
+
sinceHeight?: number;
|
|
73
85
|
limit?: number;
|
|
74
86
|
offset?: number;
|
|
75
87
|
}
|
|
88
|
+
/** Paginated events result. */
|
|
76
89
|
interface EventsResult {
|
|
77
90
|
events: Event[];
|
|
78
91
|
total: number;
|
|
@@ -103,7 +116,7 @@ interface PreparedClaim extends PreparedAction {
|
|
|
103
116
|
interface PreparedList extends PreparedAction {
|
|
104
117
|
readonly name: string;
|
|
105
118
|
readonly price: Zats;
|
|
106
|
-
readonly
|
|
119
|
+
readonly payTaddr: string;
|
|
107
120
|
readonly nonce: number;
|
|
108
121
|
}
|
|
109
122
|
/** Prepared DELIST action */
|
|
@@ -142,8 +155,6 @@ interface PayloadValidationResult {
|
|
|
142
155
|
readonly valid: boolean;
|
|
143
156
|
/** Parsed action name (uppercase), e.g. "CLAIM", "LIST" */
|
|
144
157
|
readonly action: string;
|
|
145
|
-
/** Canonical action used internally (lowercase), e.g. "claim", "list" */
|
|
146
|
-
readonly canonicalAction: string | null;
|
|
147
158
|
/** Human-readable validation message */
|
|
148
159
|
readonly message: string;
|
|
149
160
|
/** Validation level: valid | invalid | unrecognized */
|
|
@@ -151,12 +162,28 @@ interface PayloadValidationResult {
|
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
declare const DEFAULT_URL = "https://light.zcash.me/zns-testnet";
|
|
154
|
-
|
|
155
|
-
declare const
|
|
156
|
-
/**
|
|
157
|
-
*
|
|
158
|
-
declare const
|
|
159
|
-
|
|
165
|
+
/** Commission sent with a BUY claim memo (0.0001 ZEC = 10,000 zats). */
|
|
166
|
+
declare const BUY_COMMISSION: Zats;
|
|
167
|
+
/** Listing commission sent with a LIST memo (0.01 ZEC = 1,000,000 zats).
|
|
168
|
+
* Mirrors the indexer's formula: min_tier × 1000. */
|
|
169
|
+
declare const LIST_COMMISSION: Zats;
|
|
170
|
+
/** Network-specific configuration for ZNS. */
|
|
171
|
+
declare const NETWORKS: {
|
|
172
|
+
readonly testnet: {
|
|
173
|
+
readonly url: "https://light.zcash.me/zns-testnet";
|
|
174
|
+
readonly registryAddress: "utest1f32kn6c4zvn54xr8wfsnxmj9hzpu2mwgtxzpzwcw34906tdccdvzs0z2dx38lly7tpan77x6udt8pjczqm22ymsdhlz9j0tk5yq664nl";
|
|
175
|
+
readonly uivk: "utest1hzw7wyadutvzfgpna80yftsk5l7jeyu2p5me5quvp28tytxueta00cx4068wnlzcv7tx9n3t3gfhsy83pe4y6jrhxtzaq0hj6xtg5zrk2dn7zen3vns2a5pgs4fxdjlletmqrhfa42";
|
|
176
|
+
};
|
|
177
|
+
readonly mainnet: {
|
|
178
|
+
readonly url: "https://light.zcash.me/zns";
|
|
179
|
+
readonly registryAddress: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9";
|
|
180
|
+
readonly uivk: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9";
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
/** Validates a Zcash unified (u-) address. */
|
|
184
|
+
declare function isValidUnifiedAddress(address: string): boolean;
|
|
185
|
+
/** Validates a Zcash transparent (t-) address. */
|
|
186
|
+
declare function isValidTransparentAddress(address: string): boolean;
|
|
160
187
|
declare class ZNS {
|
|
161
188
|
private url;
|
|
162
189
|
private network;
|
|
@@ -197,8 +224,15 @@ declare class ZNS {
|
|
|
197
224
|
/** Validate a Zcash Unified Address format.
|
|
198
225
|
* Accepts both mainnet ('u') and testnet ('utest') prefixes.
|
|
199
226
|
* Performs basic format validation but NOT full bech32m checksum verification.
|
|
200
|
-
* Returns true if the address looks like a unified address, false otherwise.
|
|
201
|
-
|
|
227
|
+
* Returns true if the address looks like a unified address, false otherwise.
|
|
228
|
+
*
|
|
229
|
+
* @todo(F4Jumble) Upgrade to full ZIP-316 decoding with F4Jumble to:
|
|
230
|
+
* - Parse actual typecodes from address items
|
|
231
|
+
* - Validate F4Jumble checksum (not just bech32m)
|
|
232
|
+
* - Optionally enforce: address must contain at least one Orchard receiver (typecode 0x03)
|
|
233
|
+
* Requires @noble/hashes (blake2b) implementation of F4Jumble inverse. */
|
|
234
|
+
isValidUnifiedAddress: typeof isValidUnifiedAddress;
|
|
235
|
+
isValidTransparentAddress: typeof isValidTransparentAddress;
|
|
202
236
|
listings(limit?: number, offset?: number): Promise<{
|
|
203
237
|
listings: Listing[];
|
|
204
238
|
total: number;
|
|
@@ -218,8 +252,6 @@ declare class ZNS {
|
|
|
218
252
|
* @returns true if the signature is valid
|
|
219
253
|
*/
|
|
220
254
|
verifyRegistration(reg: Registration, adminPubkey: string): Promise<boolean>;
|
|
221
|
-
/** Check if a name is valid format (lowercase alphanumeric, 1-62 chars). */
|
|
222
|
-
isValidName(name: string): boolean;
|
|
223
255
|
/**
|
|
224
256
|
* Validate a signing payload string against the ZNS memo format spec.
|
|
225
257
|
*
|
|
@@ -269,7 +301,7 @@ declare class ZNS {
|
|
|
269
301
|
* @returns Prepared claim ready for signature completion
|
|
270
302
|
*/
|
|
271
303
|
prepareClaim(name: string, address: string, cost: Zats): PreparedClaim;
|
|
272
|
-
prepareList(name: string, price: Zats,
|
|
304
|
+
prepareList(name: string, price: Zats, payTaddr: string, nonce: number): PreparedList;
|
|
273
305
|
prepareDelist(name: string, nonce: number): PreparedDelist;
|
|
274
306
|
prepareUpdate(name: string, newAddress: string, nonce: number): PreparedUpdate;
|
|
275
307
|
prepareBuy(name: string, buyerAddress: string, price: Zats): PreparedBuy;
|
|
@@ -286,4 +318,4 @@ declare class ZNS {
|
|
|
286
318
|
private rpc;
|
|
287
319
|
}
|
|
288
320
|
|
|
289
|
-
export { BUY_COMMISSION,
|
|
321
|
+
export { BUY_COMMISSION, DEFAULT_URL, LIST_COMMISSION, NETWORKS, ZNS };
|
package/dist/zns.js
CHANGED
|
@@ -3,25 +3,86 @@ import * as ed25519 from "@noble/ed25519";
|
|
|
3
3
|
import { bech32m } from "bech32";
|
|
4
4
|
|
|
5
5
|
// src/types.ts
|
|
6
|
-
var
|
|
7
|
-
var LIST_COMMISSION = 1e6;
|
|
6
|
+
var ZNS_ACTIONS = ["CLAIM", "BUY", "UPDATE", "LIST", "DELIST", "RELEASE"];
|
|
8
7
|
|
|
9
8
|
// src/zns.ts
|
|
10
9
|
var DEFAULT_URL = "https://light.zcash.me/zns-testnet";
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
var BUY_COMMISSION = 1e4;
|
|
11
|
+
var LIST_COMMISSION = 1e6;
|
|
12
|
+
var NETWORKS = {
|
|
13
|
+
testnet: {
|
|
14
|
+
url: "https://light.zcash.me/zns-testnet",
|
|
15
|
+
registryAddress: "utest1f32kn6c4zvn54xr8wfsnxmj9hzpu2mwgtxzpzwcw34906tdccdvzs0z2dx38lly7tpan77x6udt8pjczqm22ymsdhlz9j0tk5yq664nl",
|
|
16
|
+
uivk: "utest1hzw7wyadutvzfgpna80yftsk5l7jeyu2p5me5quvp28tytxueta00cx4068wnlzcv7tx9n3t3gfhsy83pe4y6jrhxtzaq0hj6xtg5zrk2dn7zen3vns2a5pgs4fxdjlletmqrhfa42"
|
|
17
|
+
},
|
|
18
|
+
mainnet: {
|
|
19
|
+
url: "https://light.zcash.me/zns",
|
|
20
|
+
registryAddress: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9",
|
|
21
|
+
uivk: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9"
|
|
22
|
+
}
|
|
17
23
|
};
|
|
18
|
-
var ZNS_ACTIONS = ["CLAIM", "BUY", "UPDATE", "LIST", "DELIST", "RELEASE"];
|
|
19
24
|
var NAME_RE = /^[a-z0-9]{1,62}$/;
|
|
25
|
+
function isValidName(name) {
|
|
26
|
+
return NAME_RE.test(name);
|
|
27
|
+
}
|
|
28
|
+
function normalizeApiResponse(obj) {
|
|
29
|
+
if (Array.isArray(obj)) return obj.map((item) => normalizeApiResponse(item));
|
|
30
|
+
if (obj && typeof obj === "object") {
|
|
31
|
+
return Object.fromEntries(
|
|
32
|
+
Object.entries(obj).map(([k, v]) => [
|
|
33
|
+
k.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
|
|
34
|
+
normalizeApiResponse(v)
|
|
35
|
+
])
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return obj;
|
|
39
|
+
}
|
|
20
40
|
function isWholeNumber(value) {
|
|
21
41
|
return /^\d+$/.test(value) && !value.startsWith("0") || value === "0";
|
|
22
42
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
43
|
+
function isValidUnifiedAddress(address) {
|
|
44
|
+
if (!address) return false;
|
|
45
|
+
if (address.startsWith("utest1")) return true;
|
|
46
|
+
if (address.startsWith("u1")) return true;
|
|
47
|
+
try {
|
|
48
|
+
const decoded = bech32m.decode(address);
|
|
49
|
+
return decoded.prefix === "u" || decoded.prefix === "utest";
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isValidTransparentAddress(address) {
|
|
55
|
+
if (!address) return false;
|
|
56
|
+
const validPrefixes = ["t1", "t3", "tm", "tn"];
|
|
57
|
+
if (!validPrefixes.some((p) => address.startsWith(p))) return false;
|
|
58
|
+
if (address.length < 26 || address.length > 35) return false;
|
|
59
|
+
const base58Regex = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/;
|
|
60
|
+
return base58Regex.test(address);
|
|
61
|
+
}
|
|
62
|
+
var PAYLOAD_RULES = {
|
|
63
|
+
CLAIM: { format: "CLAIM:<name>:<ua>", checks: ["name", "ua"] },
|
|
64
|
+
BUY: { format: "BUY:<name>:<ua>", checks: ["name", "ua"] },
|
|
65
|
+
UPDATE: { format: "UPDATE:<name>:<ua>:<nonce>", checks: ["name", "ua", "nonce"] },
|
|
66
|
+
LIST: { format: "LIST:<name>:<price>:<pay_taddr>:<nonce>", checks: ["name", "price", "pay_taddr", "nonce"] },
|
|
67
|
+
DELIST: { format: "DELIST:<name>:<nonce>", checks: ["name", "nonce"] },
|
|
68
|
+
RELEASE: { format: "RELEASE:<name>:<nonce>", checks: ["name", "nonce"] }
|
|
69
|
+
};
|
|
70
|
+
function validateField(value, type) {
|
|
71
|
+
switch (type) {
|
|
72
|
+
case "name":
|
|
73
|
+
return NAME_RE.test(value) ? null : "Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.";
|
|
74
|
+
case "ua":
|
|
75
|
+
return isValidUnifiedAddress(value) ? null : `Invalid unified address: "${value}".`;
|
|
76
|
+
case "price":
|
|
77
|
+
return isWholeNumber(value) && Number(value) > 0 ? null : "Price must be a positive whole number in zats.";
|
|
78
|
+
case "nonce":
|
|
79
|
+
return isWholeNumber(value) ? null : "Nonce must be a whole number.";
|
|
80
|
+
case "pay_taddr":
|
|
81
|
+
return isValidTransparentAddress(value) ? null : `Invalid transparent address: "${value}".`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function buildValidationResult(level, action, message) {
|
|
85
|
+
return { valid: level === "valid", action, message, level };
|
|
25
86
|
}
|
|
26
87
|
var ZNS = class {
|
|
27
88
|
/**
|
|
@@ -33,16 +94,28 @@ var ZNS = class {
|
|
|
33
94
|
constructor(options) {
|
|
34
95
|
this.rpcId = 0;
|
|
35
96
|
this._verified = false;
|
|
97
|
+
/** Validate a Zcash Unified Address format.
|
|
98
|
+
* Accepts both mainnet ('u') and testnet ('utest') prefixes.
|
|
99
|
+
* Performs basic format validation but NOT full bech32m checksum verification.
|
|
100
|
+
* Returns true if the address looks like a unified address, false otherwise.
|
|
101
|
+
*
|
|
102
|
+
* @todo(F4Jumble) Upgrade to full ZIP-316 decoding with F4Jumble to:
|
|
103
|
+
* - Parse actual typecodes from address items
|
|
104
|
+
* - Validate F4Jumble checksum (not just bech32m)
|
|
105
|
+
* - Optionally enforce: address must contain at least one Orchard receiver (typecode 0x03)
|
|
106
|
+
* Requires @noble/hashes (blake2b) implementation of F4Jumble inverse. */
|
|
107
|
+
this.isValidUnifiedAddress = isValidUnifiedAddress;
|
|
108
|
+
this.isValidTransparentAddress = isValidTransparentAddress;
|
|
36
109
|
this.network = options?.network ?? "testnet";
|
|
37
|
-
this.url = options?.url ??
|
|
110
|
+
this.url = options?.url ?? NETWORKS[this.network].url;
|
|
38
111
|
}
|
|
39
112
|
/**
|
|
40
113
|
* Verifies that the connected server is a known ZNS instance.
|
|
41
114
|
* @throws Error if the server's UIVK is not recognized
|
|
42
115
|
*/
|
|
43
116
|
async verify() {
|
|
44
|
-
const status = await this.
|
|
45
|
-
if (
|
|
117
|
+
const status = await this.status();
|
|
118
|
+
if (status.uivk !== NETWORKS[this.network].uivk) {
|
|
46
119
|
throw new Error(
|
|
47
120
|
`UIVK mismatch: indexer returned "${status.uivk.slice(0, 20)}..." which is not a known ZNS instance`
|
|
48
121
|
);
|
|
@@ -55,72 +128,61 @@ var ZNS = class {
|
|
|
55
128
|
}
|
|
56
129
|
/** Get the registry address for the current network. */
|
|
57
130
|
get registryAddress() {
|
|
58
|
-
|
|
59
|
-
if (!addr) {
|
|
60
|
-
throw new Error(`Unknown network: ${this.network}`);
|
|
61
|
-
}
|
|
62
|
-
return addr;
|
|
131
|
+
return NETWORKS[this.network].registryAddress;
|
|
63
132
|
}
|
|
64
133
|
/** Fetch current server status including pricing and configuration. */
|
|
65
134
|
async status() {
|
|
66
|
-
|
|
135
|
+
const raw = await this.rpc("status");
|
|
136
|
+
return normalizeApiResponse(raw);
|
|
67
137
|
}
|
|
68
138
|
/** Resolve a ZNS name to its registration. Returns null if not registered. */
|
|
69
139
|
async resolveName(name) {
|
|
70
|
-
|
|
140
|
+
const raw = await this.rpc("resolve", { query: name });
|
|
141
|
+
return raw ? normalizeApiResponse(raw) : null;
|
|
71
142
|
}
|
|
72
143
|
/** Resolve a Zcash Unified Address to all names pointing to it. Returns empty array if none.
|
|
73
144
|
* Supports pagination with limit (default 50, max 500) and offset (default 0). */
|
|
74
145
|
async resolveAddress(address, limit, offset) {
|
|
75
|
-
|
|
146
|
+
const raw = await this.rpc("resolve", {
|
|
76
147
|
query: address,
|
|
77
148
|
limit,
|
|
78
149
|
offset
|
|
79
150
|
});
|
|
151
|
+
return raw.map((r) => normalizeApiResponse(r));
|
|
80
152
|
}
|
|
81
153
|
/** List all registered names. Useful for explorers or browsers.
|
|
82
154
|
* Supports pagination with limit (default 50, max 500) and offset (default 0). */
|
|
83
155
|
async listAllRegistrations(limit, offset) {
|
|
84
|
-
|
|
156
|
+
const raw = await this.rpc("resolve", {
|
|
85
157
|
query: "",
|
|
86
158
|
limit,
|
|
87
159
|
offset
|
|
88
160
|
});
|
|
161
|
+
return raw.map((r) => normalizeApiResponse(r));
|
|
89
162
|
}
|
|
90
163
|
/** Check if a name is available for registration.
|
|
91
164
|
* Returns false immediately for invalid names without hitting the server. */
|
|
92
165
|
async isAvailable(name) {
|
|
93
|
-
if (!
|
|
166
|
+
if (!isValidName(name)) return false;
|
|
94
167
|
const result = await this.resolveName(name);
|
|
95
168
|
return result === null;
|
|
96
169
|
}
|
|
97
|
-
/** Validate a Zcash Unified Address format.
|
|
98
|
-
* Accepts both mainnet ('u') and testnet ('utest') prefixes.
|
|
99
|
-
* Performs basic format validation but NOT full bech32m checksum verification.
|
|
100
|
-
* Returns true if the address looks like a unified address, false otherwise. */
|
|
101
|
-
isValidUnifiedAddress(address) {
|
|
102
|
-
if (!address) return false;
|
|
103
|
-
if (address.startsWith("utest1")) return true;
|
|
104
|
-
if (address.startsWith("u1")) return true;
|
|
105
|
-
try {
|
|
106
|
-
const decoded = bech32m.decode(address);
|
|
107
|
-
return decoded.prefix === "u" || decoded.prefix === "utest";
|
|
108
|
-
} catch {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
170
|
async listings(limit, offset) {
|
|
113
|
-
const
|
|
171
|
+
const raw = await this.rpc("listings", {
|
|
114
172
|
limit,
|
|
115
173
|
offset
|
|
116
174
|
});
|
|
117
|
-
return
|
|
175
|
+
return {
|
|
176
|
+
listings: raw.listings.map((l) => normalizeApiResponse(l)),
|
|
177
|
+
total: raw.total
|
|
178
|
+
};
|
|
118
179
|
}
|
|
119
180
|
async events(filter) {
|
|
120
|
-
|
|
181
|
+
const raw = await this.rpc(
|
|
121
182
|
"events",
|
|
122
|
-
filter ?? {}
|
|
183
|
+
normalizeApiResponse(filter ?? {})
|
|
123
184
|
);
|
|
185
|
+
return normalizeApiResponse(raw);
|
|
124
186
|
}
|
|
125
187
|
/**
|
|
126
188
|
* Verify a listing's signature.
|
|
@@ -130,7 +192,7 @@ var ZNS = class {
|
|
|
130
192
|
*/
|
|
131
193
|
async verifyListing(listing, adminPubkey) {
|
|
132
194
|
const pubkey = listing.pubkey ?? adminPubkey;
|
|
133
|
-
const payload = `LIST:${listing.name}:${listing.price}:${listing.
|
|
195
|
+
const payload = `LIST:${listing.name}:${listing.price}:${listing.payTaddr}:${listing.nonce}`;
|
|
134
196
|
return this.verifyEd25519(payload, listing.signature, pubkey);
|
|
135
197
|
}
|
|
136
198
|
/**
|
|
@@ -146,10 +208,6 @@ var ZNS = class {
|
|
|
146
208
|
if (!payload) return false;
|
|
147
209
|
return this.verifyEd25519(payload, reg.signature, pubkey);
|
|
148
210
|
}
|
|
149
|
-
/** Check if a name is valid format (lowercase alphanumeric, 1-62 chars). */
|
|
150
|
-
isValidName(name) {
|
|
151
|
-
return NAME_RE.test(name);
|
|
152
|
-
}
|
|
153
211
|
/**
|
|
154
212
|
* Validate a signing payload string against the ZNS memo format spec.
|
|
155
213
|
*
|
|
@@ -176,7 +234,6 @@ var ZNS = class {
|
|
|
176
234
|
return {
|
|
177
235
|
valid: false,
|
|
178
236
|
action: "",
|
|
179
|
-
canonicalAction: null,
|
|
180
237
|
message: "Empty payload.",
|
|
181
238
|
level: "invalid"
|
|
182
239
|
};
|
|
@@ -186,83 +243,29 @@ var ZNS = class {
|
|
|
186
243
|
return {
|
|
187
244
|
valid: false,
|
|
188
245
|
action: raw.toUpperCase(),
|
|
189
|
-
canonicalAction: null,
|
|
190
246
|
message: "Missing colon separator. Expected format: ACTION:field1:field2:...",
|
|
191
247
|
level: "invalid"
|
|
192
248
|
};
|
|
193
249
|
}
|
|
194
|
-
const
|
|
195
|
-
const actionLower = raw.slice(0, colonIdx).toLowerCase();
|
|
250
|
+
const action = raw.slice(0, colonIdx).toUpperCase();
|
|
196
251
|
const rest = raw.slice(colonIdx + 1);
|
|
197
252
|
const parts = rest.split(":");
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return mk("invalid", actionUpper, "buy", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
213
|
-
if (!this.isValidUnifiedAddress(parts[1]))
|
|
214
|
-
return mk("invalid", actionUpper, "buy", `Invalid unified address: "${parts[1]}".`);
|
|
215
|
-
return mk("valid", actionUpper, "buy", "Valid BUY payload.");
|
|
216
|
-
}
|
|
217
|
-
case "update": {
|
|
218
|
-
if (parts.length !== 3)
|
|
219
|
-
return mk("invalid", actionUpper, "update", `Expected UPDATE:<name>:<ua>:<nonce>.`);
|
|
220
|
-
if (!NAME_RE.test(parts[0]))
|
|
221
|
-
return mk("invalid", actionUpper, "update", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
222
|
-
if (!this.isValidUnifiedAddress(parts[1]))
|
|
223
|
-
return mk("invalid", actionUpper, "update", `Invalid unified address: "${parts[1]}".`);
|
|
224
|
-
if (!isWholeNumber(parts[2]))
|
|
225
|
-
return mk("invalid", actionUpper, "update", `Nonce must be a whole number.`);
|
|
226
|
-
return mk("valid", actionUpper, "update", "Valid UPDATE payload.");
|
|
227
|
-
}
|
|
228
|
-
case "list": {
|
|
229
|
-
if (parts.length !== 4)
|
|
230
|
-
return mk("invalid", actionUpper, "list", `Expected LIST:<name>:<price_zats>:<pay_taddr>:<nonce>.`);
|
|
231
|
-
if (!NAME_RE.test(parts[0]))
|
|
232
|
-
return mk("invalid", actionUpper, "list", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
233
|
-
if (!isWholeNumber(parts[1]) || Number(parts[1]) <= 0)
|
|
234
|
-
return mk("invalid", actionUpper, "list", `Price must be a positive whole number in zats.`);
|
|
235
|
-
if (!isWholeNumber(parts[3]))
|
|
236
|
-
return mk("invalid", actionUpper, "list", `Nonce must be a whole number.`);
|
|
237
|
-
return mk("valid", actionUpper, "list", "Valid LIST payload.");
|
|
238
|
-
}
|
|
239
|
-
case "delist": {
|
|
240
|
-
if (parts.length !== 2)
|
|
241
|
-
return mk("invalid", actionUpper, "delist", `Expected DELIST:<name>:<nonce>.`);
|
|
242
|
-
if (!NAME_RE.test(parts[0]))
|
|
243
|
-
return mk("invalid", actionUpper, "delist", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
244
|
-
if (!isWholeNumber(parts[1]))
|
|
245
|
-
return mk("invalid", actionUpper, "delist", `Nonce must be a whole number.`);
|
|
246
|
-
return mk("valid", actionUpper, "delist", "Valid DELIST payload.");
|
|
247
|
-
}
|
|
248
|
-
case "release": {
|
|
249
|
-
if (parts.length !== 2)
|
|
250
|
-
return mk("invalid", actionUpper, "release", `Expected RELEASE:<name>:<nonce>.`);
|
|
251
|
-
if (!NAME_RE.test(parts[0]))
|
|
252
|
-
return mk("invalid", actionUpper, "release", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
|
|
253
|
-
if (!isWholeNumber(parts[1]))
|
|
254
|
-
return mk("invalid", actionUpper, "release", `Nonce must be a whole number.`);
|
|
255
|
-
return mk("valid", actionUpper, "release", "Valid RELEASE payload.");
|
|
256
|
-
}
|
|
257
|
-
default:
|
|
258
|
-
return {
|
|
259
|
-
valid: false,
|
|
260
|
-
action: actionUpper,
|
|
261
|
-
canonicalAction: null,
|
|
262
|
-
message: `Unrecognized action "${actionUpper}". Valid actions: ${ZNS_ACTIONS.join(", ")}.`,
|
|
263
|
-
level: "unrecognized"
|
|
264
|
-
};
|
|
253
|
+
if (!ZNS_ACTIONS.includes(action)) {
|
|
254
|
+
return {
|
|
255
|
+
valid: false,
|
|
256
|
+
action,
|
|
257
|
+
message: `Unrecognized action "${action}". Valid actions: ${ZNS_ACTIONS.join(", ")}.`,
|
|
258
|
+
level: "unrecognized"
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const rule = PAYLOAD_RULES[action];
|
|
262
|
+
if (parts.length !== rule.checks.length)
|
|
263
|
+
return buildValidationResult("invalid", action, `Expected ${rule.format}.`);
|
|
264
|
+
for (let i = 0; i < rule.checks.length; i++) {
|
|
265
|
+
const err = validateField(parts[i], rule.checks[i]);
|
|
266
|
+
if (err) return buildValidationResult("invalid", action, err);
|
|
265
267
|
}
|
|
268
|
+
return buildValidationResult("valid", action, `Valid ${action} payload.`);
|
|
266
269
|
}
|
|
267
270
|
/**
|
|
268
271
|
* Get the claim cost in zatoshis for a name of given length.
|
|
@@ -305,7 +308,7 @@ var ZNS = class {
|
|
|
305
308
|
*/
|
|
306
309
|
prepareClaim(name, address, cost) {
|
|
307
310
|
this.requireValidName(name);
|
|
308
|
-
if (!
|
|
311
|
+
if (!isValidUnifiedAddress(address)) {
|
|
309
312
|
throw new Error(`Invalid Zcash Unified Address: ${address}`);
|
|
310
313
|
}
|
|
311
314
|
return {
|
|
@@ -320,16 +323,16 @@ var ZNS = class {
|
|
|
320
323
|
}
|
|
321
324
|
};
|
|
322
325
|
}
|
|
323
|
-
prepareList(name, price,
|
|
326
|
+
prepareList(name, price, payTaddr, nonce) {
|
|
324
327
|
this.requireValidName(name);
|
|
325
328
|
return {
|
|
326
329
|
name,
|
|
327
330
|
price,
|
|
328
|
-
|
|
331
|
+
payTaddr,
|
|
329
332
|
nonce,
|
|
330
|
-
payload: `LIST:${name}:${price}:${
|
|
333
|
+
payload: `LIST:${name}:${price}:${payTaddr}:${nonce}`,
|
|
331
334
|
complete: (signature, userPubkey) => {
|
|
332
|
-
const memo = userPubkey ? `ZNS:LIST:${name}:${price}:${
|
|
335
|
+
const memo = userPubkey ? `ZNS:LIST:${name}:${price}:${payTaddr}:${nonce}:${signature}:${userPubkey}` : `ZNS:LIST:${name}:${price}:${payTaddr}:${nonce}:${signature}`;
|
|
333
336
|
return { memo, uri: this.buildZcashUri(this.registryAddress, LIST_COMMISSION, memo) };
|
|
334
337
|
}
|
|
335
338
|
};
|
|
@@ -348,7 +351,7 @@ var ZNS = class {
|
|
|
348
351
|
}
|
|
349
352
|
prepareUpdate(name, newAddress, nonce) {
|
|
350
353
|
this.requireValidName(name);
|
|
351
|
-
if (!
|
|
354
|
+
if (!isValidUnifiedAddress(newAddress)) {
|
|
352
355
|
throw new Error(`Invalid Zcash Unified Address: ${newAddress}`);
|
|
353
356
|
}
|
|
354
357
|
return {
|
|
@@ -403,7 +406,7 @@ var ZNS = class {
|
|
|
403
406
|
}
|
|
404
407
|
// ── Private helpers ────────────────────────────────────────────────────────
|
|
405
408
|
registrationPayload(reg) {
|
|
406
|
-
switch (reg.
|
|
409
|
+
switch (reg.lastAction) {
|
|
407
410
|
case "CLAIM":
|
|
408
411
|
return `CLAIM:${reg.name}:${reg.address}`;
|
|
409
412
|
case "BUY":
|
|
@@ -430,7 +433,7 @@ var ZNS = class {
|
|
|
430
433
|
}
|
|
431
434
|
}
|
|
432
435
|
requireValidName(name) {
|
|
433
|
-
if (!
|
|
436
|
+
if (!isValidName(name)) throw new Error(`Invalid ZNS name: ${name}`);
|
|
434
437
|
}
|
|
435
438
|
/** Build a ZIP-321 URI. Amount is in zatoshis and will be converted to ZEC for the URI. */
|
|
436
439
|
buildZcashUri(address, amountZats, memo) {
|
|
@@ -495,8 +498,6 @@ export {
|
|
|
495
498
|
BUY_COMMISSION,
|
|
496
499
|
DEFAULT_URL,
|
|
497
500
|
LIST_COMMISSION,
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
ZNS,
|
|
501
|
-
ZNS_ACTIONS
|
|
501
|
+
NETWORKS,
|
|
502
|
+
ZNS
|
|
502
503
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zcashname-sdk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "TypeScript SDK for the Zcash Name System (ZNS)",
|
|
5
|
+
"description": "TypeScript SDK for the Zcash Name System (ZNS).",
|
|
6
6
|
"main": "dist/zns.cjs",
|
|
7
7
|
"module": "dist/zns.js",
|
|
8
8
|
"types": "dist/zns.d.ts",
|