zcashname-sdk 0.8.0 → 0.8.2

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 CHANGED
@@ -32,6 +32,7 @@ var zns_exports = {};
32
32
  __export(zns_exports, {
33
33
  BUY_COMMISSION: () => BUY_COMMISSION,
34
34
  DEFAULT_URL: () => DEFAULT_URL,
35
+ LIST_COMMISSION: () => LIST_COMMISSION,
35
36
  MAINNET_UIVK: () => MAINNET_UIVK,
36
37
  TESTNET_UIVK: () => TESTNET_UIVK,
37
38
  ZNS: () => ZNS
@@ -42,6 +43,7 @@ var import_bech32 = require("bech32");
42
43
 
43
44
  // src/types.ts
44
45
  var BUY_COMMISSION = 1e4;
46
+ var LIST_COMMISSION = 1e6;
45
47
 
46
48
  // src/zns.ts
47
49
  var DEFAULT_URL = "https://light.zcash.me/zns-testnet";
@@ -53,6 +55,12 @@ var REGISTRY_ADDRESSES = {
53
55
  mainnet: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9"
54
56
  };
55
57
  var NAME_RE = /^[a-z0-9]{1,62}$/;
58
+ function isWholeNumber(value) {
59
+ return /^\d+$/.test(value) && !value.startsWith("0") || value === "0";
60
+ }
61
+ function mk(level, action, canonical, message) {
62
+ return { valid: level === "valid", action, canonicalAction: canonical, message, level };
63
+ }
56
64
  var ZNS = class {
57
65
  /**
58
66
  * Creates a new ZNS client.
@@ -180,6 +188,137 @@ var ZNS = class {
180
188
  isValidName(name) {
181
189
  return NAME_RE.test(name);
182
190
  }
191
+ /**
192
+ * Validate a signing payload string against the ZNS memo format spec.
193
+ *
194
+ * This is the single source of truth for payload format validation —
195
+ * it mirrors the Rust indexer's `parse_memo` and `signing_payload` logic.
196
+ *
197
+ * Does NOT validate the signature (see {@link verifyEd25519} for that).
198
+ * Does NOT validate the name against the blockchain (see {@link isAvailable} for that).
199
+ *
200
+ * @param payload - Raw payload string, e.g. `CLAIM:foo:u1abc`
201
+ * @returns Validation result with level and human-readable message
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * const result = zns.validatePayload("CLAIM:alice:u1qvs2...");
206
+ * if (!result.valid) {
207
+ * console.error(result.message);
208
+ * }
209
+ * ```
210
+ */
211
+ validatePayload(payload) {
212
+ const raw = String(payload ?? "").trim();
213
+ if (!raw) {
214
+ return {
215
+ valid: false,
216
+ action: "",
217
+ canonicalAction: null,
218
+ message: "Empty payload.",
219
+ level: "invalid"
220
+ };
221
+ }
222
+ const colonIdx = raw.indexOf(":");
223
+ if (colonIdx === -1) {
224
+ return {
225
+ valid: false,
226
+ action: raw.toUpperCase(),
227
+ canonicalAction: null,
228
+ message: "Missing colon separator. Expected format: ACTION:field1:field2:...",
229
+ level: "invalid"
230
+ };
231
+ }
232
+ const actionUpper = raw.slice(0, colonIdx).toUpperCase();
233
+ const actionLower = raw.slice(0, colonIdx).toLowerCase();
234
+ const rest = raw.slice(colonIdx + 1);
235
+ const parts = rest.split(":");
236
+ switch (actionLower) {
237
+ case "claim": {
238
+ if (parts.length !== 2)
239
+ return mk("invalid", actionUpper, "claim", `Expected CLAIM:<name>:<ua>.`);
240
+ if (!NAME_RE.test(parts[0]))
241
+ return mk("invalid", actionUpper, "claim", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
242
+ if (!this.isValidUnifiedAddress(parts[1]))
243
+ return mk("invalid", actionUpper, "claim", `Invalid unified address: "${parts[1]}".`);
244
+ return mk("valid", actionUpper, "claim", "Valid CLAIM payload.");
245
+ }
246
+ case "buy": {
247
+ if (parts.length !== 2)
248
+ return mk("invalid", actionUpper, "buy", `Expected BUY:<name>:<buyer_ua>.`);
249
+ if (!NAME_RE.test(parts[0]))
250
+ return mk("invalid", actionUpper, "buy", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
251
+ if (!this.isValidUnifiedAddress(parts[1]))
252
+ return mk("invalid", actionUpper, "buy", `Invalid unified address: "${parts[1]}".`);
253
+ return mk("valid", actionUpper, "buy", "Valid BUY payload.");
254
+ }
255
+ case "update": {
256
+ if (parts.length !== 3)
257
+ return mk("invalid", actionUpper, "update", `Expected UPDATE:<name>:<ua>:<nonce>.`);
258
+ if (!NAME_RE.test(parts[0]))
259
+ return mk("invalid", actionUpper, "update", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
260
+ if (!this.isValidUnifiedAddress(parts[1]))
261
+ return mk("invalid", actionUpper, "update", `Invalid unified address: "${parts[1]}".`);
262
+ if (!isWholeNumber(parts[2]))
263
+ return mk("invalid", actionUpper, "update", `Nonce must be a whole number.`);
264
+ return mk("valid", actionUpper, "update", "Valid UPDATE payload.");
265
+ }
266
+ case "list": {
267
+ if (parts.length !== 4)
268
+ return mk("invalid", actionUpper, "list", `Expected LIST:<name>:<price_zats>:<pay_taddr>:<nonce>.`);
269
+ if (!NAME_RE.test(parts[0]))
270
+ return mk("invalid", actionUpper, "list", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
271
+ if (!isWholeNumber(parts[1]) || Number(parts[1]) <= 0)
272
+ return mk("invalid", actionUpper, "list", `Price must be a positive whole number in zats.`);
273
+ if (!isWholeNumber(parts[3]))
274
+ return mk("invalid", actionUpper, "list", `Nonce must be a whole number.`);
275
+ return mk("valid", actionUpper, "list", "Valid LIST payload.");
276
+ }
277
+ case "delist": {
278
+ if (parts.length !== 2)
279
+ return mk("invalid", actionUpper, "delist", `Expected DELIST:<name>:<nonce>.`);
280
+ if (!NAME_RE.test(parts[0]))
281
+ return mk("invalid", actionUpper, "delist", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
282
+ if (!isWholeNumber(parts[1]))
283
+ return mk("invalid", actionUpper, "delist", `Nonce must be a whole number.`);
284
+ return mk("valid", actionUpper, "delist", "Valid DELIST payload.");
285
+ }
286
+ case "release": {
287
+ if (parts.length !== 2)
288
+ return mk("invalid", actionUpper, "release", `Expected RELEASE:<name>:<nonce>.`);
289
+ if (!NAME_RE.test(parts[0]))
290
+ return mk("invalid", actionUpper, "release", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
291
+ if (!isWholeNumber(parts[1]))
292
+ return mk("invalid", actionUpper, "release", `Nonce must be a whole number.`);
293
+ return mk("valid", actionUpper, "release", "Valid RELEASE payload.");
294
+ }
295
+ case "setprice": {
296
+ if (parts.length < 3)
297
+ return mk("invalid", actionUpper, "setprice", `Expected SETPRICE:<count>:<tier1>:...:<nonce>.`);
298
+ const count = Number(parts[0]);
299
+ if (!isWholeNumber(parts[0]) || isNaN(count) || count <= 0)
300
+ return mk("invalid", actionUpper, "setprice", `Count must be a positive whole number.`);
301
+ const priceCount = parts.length - 2;
302
+ if (priceCount !== count)
303
+ return mk("invalid", actionUpper, "setprice", `SETPRICE count mismatch: header is ${count} but found ${priceCount} price tiers.`);
304
+ if (!isWholeNumber(parts[parts.length - 1]))
305
+ return mk("invalid", actionUpper, "setprice", `Nonce must be a whole number.`);
306
+ for (let i = 1; i < parts.length - 1; i++) {
307
+ if (!isWholeNumber(parts[i]) || Number(parts[i]) <= 0)
308
+ return mk("invalid", actionUpper, "setprice", `Tier ${i} must be a positive whole number in zats.`);
309
+ }
310
+ return mk("valid", actionUpper, "setprice", "Valid SETPRICE payload.");
311
+ }
312
+ default:
313
+ return {
314
+ valid: false,
315
+ action: actionUpper,
316
+ canonicalAction: null,
317
+ message: `Unrecognized action "${actionUpper}". Valid actions: CLAIM, BUY, UPDATE, LIST, DELIST, RELEASE, SETPRICE.`,
318
+ level: "unrecognized"
319
+ };
320
+ }
321
+ }
183
322
  /**
184
323
  * Get the claim cost in zatoshis for a name of given length.
185
324
  * @param nameLength The length of the name (1-62)
@@ -246,7 +385,7 @@ var ZNS = class {
246
385
  payload: `LIST:${name}:${price}:${pay_taddr}:${nonce}`,
247
386
  complete: (signature, userPubkey) => {
248
387
  const memo = userPubkey ? `ZNS:LIST:${name}:${price}:${pay_taddr}:${nonce}:${signature}:${userPubkey}` : `ZNS:LIST:${name}:${price}:${pay_taddr}:${nonce}:${signature}`;
249
- return { memo, uri: this.buildZcashUri(this.registryAddress, void 0, memo) };
388
+ return { memo, uri: this.buildZcashUri(this.registryAddress, LIST_COMMISSION, memo) };
250
389
  }
251
390
  };
252
391
  }
@@ -411,6 +550,7 @@ var ZNS = class {
411
550
  0 && (module.exports = {
412
551
  BUY_COMMISSION,
413
552
  DEFAULT_URL,
553
+ LIST_COMMISSION,
414
554
  MAINNET_UIVK,
415
555
  TESTNET_UIVK,
416
556
  ZNS
package/dist/zns.d.cts CHANGED
@@ -4,6 +4,9 @@
4
4
  type Zats = number;
5
5
  /** Commission sent with a BUY claim memo (0.0001 ZEC = 10,000 zats). */
6
6
  declare const BUY_COMMISSION: Zats;
7
+ /** Listing commission sent with a LIST memo (0.01 ZEC = 1,000,000 zats).
8
+ * Mirrors the indexer's formula: min_tier × 1000. */
9
+ declare const LIST_COMMISSION: Zats;
7
10
  interface Registration {
8
11
  name: string;
9
12
  address: string;
@@ -130,6 +133,22 @@ interface PreparedSetPrice extends PreparedAction {
130
133
  readonly prices: readonly Zats[];
131
134
  readonly nonce: number;
132
135
  }
136
+ /** Validation result for a signing payload.
137
+ * Mirrors the Rust indexer's parse_memo format validation.
138
+ * @see https://github.com/zcashme/ZNS/blob/main/src/memo.rs */
139
+ type PayloadValidationLevel = "valid" | "invalid" | "unrecognized";
140
+ interface PayloadValidationResult {
141
+ /** Whether the payload is valid for signing. */
142
+ readonly valid: boolean;
143
+ /** Parsed action name (uppercase), e.g. "CLAIM", "LIST" */
144
+ readonly action: string;
145
+ /** Canonical action used internally (lowercase), e.g. "claim", "list" */
146
+ readonly canonicalAction: string | null;
147
+ /** Human-readable validation message */
148
+ readonly message: string;
149
+ /** Validation level: valid | invalid | unrecognized */
150
+ readonly level: PayloadValidationLevel;
151
+ }
133
152
 
134
153
  declare const DEFAULT_URL = "https://light.zcash.me/zns-testnet";
135
154
  declare const TESTNET_UIVK = "uivktest1hzw7wyadutvzfgpna80yftsk5l7jeyu2p5me5quvp28tytxueta00cx4068wnlzcv7tx9n3t3gfhsy83pe4y6jrhxtzaq0hj6xtg5zrk2dn7zen3vns2a5pgs4fxdjlletmqrhfa42";
@@ -198,6 +217,27 @@ declare class ZNS {
198
217
  verifyRegistration(reg: Registration, adminPubkey: string): Promise<boolean>;
199
218
  /** Check if a name is valid format (lowercase alphanumeric, 1-62 chars). */
200
219
  isValidName(name: string): boolean;
220
+ /**
221
+ * Validate a signing payload string against the ZNS memo format spec.
222
+ *
223
+ * This is the single source of truth for payload format validation —
224
+ * it mirrors the Rust indexer's `parse_memo` and `signing_payload` logic.
225
+ *
226
+ * Does NOT validate the signature (see {@link verifyEd25519} for that).
227
+ * Does NOT validate the name against the blockchain (see {@link isAvailable} for that).
228
+ *
229
+ * @param payload - Raw payload string, e.g. `CLAIM:foo:u1abc`
230
+ * @returns Validation result with level and human-readable message
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * const result = zns.validatePayload("CLAIM:alice:u1qvs2...");
235
+ * if (!result.valid) {
236
+ * console.error(result.message);
237
+ * }
238
+ * ```
239
+ */
240
+ validatePayload(payload: string): PayloadValidationResult;
201
241
  /**
202
242
  * Get the claim cost in zatoshis for a name of given length.
203
243
  * @param nameLength The length of the name (1-62)
@@ -243,4 +283,4 @@ declare class ZNS {
243
283
  private rpc;
244
284
  }
245
285
 
246
- export { BUY_COMMISSION, type CompletedAction, DEFAULT_URL, type Event, type EventAction, type EventsFilter, type EventsResult, type LastAction, type Listing, MAINNET_UIVK, type Network, type PendingBuy, type PreparedBuy, type PreparedClaim, type PreparedDelist, type PreparedList, type PreparedRelease, type PreparedSetPrice, type PreparedUpdate, type Pricing, type Registration, type Status, TESTNET_UIVK, ZNS, type Zats };
286
+ export { BUY_COMMISSION, type CompletedAction, DEFAULT_URL, type Event, type EventAction, type EventsFilter, type EventsResult, LIST_COMMISSION, type LastAction, type Listing, MAINNET_UIVK, type Network, type PayloadValidationLevel, type PayloadValidationResult, type PendingBuy, type PreparedBuy, type PreparedClaim, type PreparedDelist, type PreparedList, type PreparedRelease, type PreparedSetPrice, type PreparedUpdate, type Pricing, type Registration, type Status, TESTNET_UIVK, ZNS, type Zats };
package/dist/zns.d.ts CHANGED
@@ -4,6 +4,9 @@
4
4
  type Zats = number;
5
5
  /** Commission sent with a BUY claim memo (0.0001 ZEC = 10,000 zats). */
6
6
  declare const BUY_COMMISSION: Zats;
7
+ /** Listing commission sent with a LIST memo (0.01 ZEC = 1,000,000 zats).
8
+ * Mirrors the indexer's formula: min_tier × 1000. */
9
+ declare const LIST_COMMISSION: Zats;
7
10
  interface Registration {
8
11
  name: string;
9
12
  address: string;
@@ -130,6 +133,22 @@ interface PreparedSetPrice extends PreparedAction {
130
133
  readonly prices: readonly Zats[];
131
134
  readonly nonce: number;
132
135
  }
136
+ /** Validation result for a signing payload.
137
+ * Mirrors the Rust indexer's parse_memo format validation.
138
+ * @see https://github.com/zcashme/ZNS/blob/main/src/memo.rs */
139
+ type PayloadValidationLevel = "valid" | "invalid" | "unrecognized";
140
+ interface PayloadValidationResult {
141
+ /** Whether the payload is valid for signing. */
142
+ readonly valid: boolean;
143
+ /** Parsed action name (uppercase), e.g. "CLAIM", "LIST" */
144
+ readonly action: string;
145
+ /** Canonical action used internally (lowercase), e.g. "claim", "list" */
146
+ readonly canonicalAction: string | null;
147
+ /** Human-readable validation message */
148
+ readonly message: string;
149
+ /** Validation level: valid | invalid | unrecognized */
150
+ readonly level: PayloadValidationLevel;
151
+ }
133
152
 
134
153
  declare const DEFAULT_URL = "https://light.zcash.me/zns-testnet";
135
154
  declare const TESTNET_UIVK = "uivktest1hzw7wyadutvzfgpna80yftsk5l7jeyu2p5me5quvp28tytxueta00cx4068wnlzcv7tx9n3t3gfhsy83pe4y6jrhxtzaq0hj6xtg5zrk2dn7zen3vns2a5pgs4fxdjlletmqrhfa42";
@@ -198,6 +217,27 @@ declare class ZNS {
198
217
  verifyRegistration(reg: Registration, adminPubkey: string): Promise<boolean>;
199
218
  /** Check if a name is valid format (lowercase alphanumeric, 1-62 chars). */
200
219
  isValidName(name: string): boolean;
220
+ /**
221
+ * Validate a signing payload string against the ZNS memo format spec.
222
+ *
223
+ * This is the single source of truth for payload format validation —
224
+ * it mirrors the Rust indexer's `parse_memo` and `signing_payload` logic.
225
+ *
226
+ * Does NOT validate the signature (see {@link verifyEd25519} for that).
227
+ * Does NOT validate the name against the blockchain (see {@link isAvailable} for that).
228
+ *
229
+ * @param payload - Raw payload string, e.g. `CLAIM:foo:u1abc`
230
+ * @returns Validation result with level and human-readable message
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * const result = zns.validatePayload("CLAIM:alice:u1qvs2...");
235
+ * if (!result.valid) {
236
+ * console.error(result.message);
237
+ * }
238
+ * ```
239
+ */
240
+ validatePayload(payload: string): PayloadValidationResult;
201
241
  /**
202
242
  * Get the claim cost in zatoshis for a name of given length.
203
243
  * @param nameLength The length of the name (1-62)
@@ -243,4 +283,4 @@ declare class ZNS {
243
283
  private rpc;
244
284
  }
245
285
 
246
- export { BUY_COMMISSION, type CompletedAction, DEFAULT_URL, type Event, type EventAction, type EventsFilter, type EventsResult, type LastAction, type Listing, MAINNET_UIVK, type Network, type PendingBuy, type PreparedBuy, type PreparedClaim, type PreparedDelist, type PreparedList, type PreparedRelease, type PreparedSetPrice, type PreparedUpdate, type Pricing, type Registration, type Status, TESTNET_UIVK, ZNS, type Zats };
286
+ export { BUY_COMMISSION, type CompletedAction, DEFAULT_URL, type Event, type EventAction, type EventsFilter, type EventsResult, LIST_COMMISSION, type LastAction, type Listing, MAINNET_UIVK, type Network, type PayloadValidationLevel, type PayloadValidationResult, type PendingBuy, type PreparedBuy, type PreparedClaim, type PreparedDelist, type PreparedList, type PreparedRelease, type PreparedSetPrice, type PreparedUpdate, type Pricing, type Registration, type Status, TESTNET_UIVK, ZNS, type Zats };
package/dist/zns.js CHANGED
@@ -4,6 +4,7 @@ import { bech32m } from "bech32";
4
4
 
5
5
  // src/types.ts
6
6
  var BUY_COMMISSION = 1e4;
7
+ var LIST_COMMISSION = 1e6;
7
8
 
8
9
  // src/zns.ts
9
10
  var DEFAULT_URL = "https://light.zcash.me/zns-testnet";
@@ -15,6 +16,12 @@ var REGISTRY_ADDRESSES = {
15
16
  mainnet: "u1k0evt0ahj5qdt6y9ftsxndl8lrkm4ff6rp00u04cjpmqj6hxl9t8hfsxftmn3ht34e03lljh89czn2h8qn67rwrs8x0hm3lsxsucp9q9"
16
17
  };
17
18
  var NAME_RE = /^[a-z0-9]{1,62}$/;
19
+ function isWholeNumber(value) {
20
+ return /^\d+$/.test(value) && !value.startsWith("0") || value === "0";
21
+ }
22
+ function mk(level, action, canonical, message) {
23
+ return { valid: level === "valid", action, canonicalAction: canonical, message, level };
24
+ }
18
25
  var ZNS = class {
19
26
  /**
20
27
  * Creates a new ZNS client.
@@ -142,6 +149,137 @@ var ZNS = class {
142
149
  isValidName(name) {
143
150
  return NAME_RE.test(name);
144
151
  }
152
+ /**
153
+ * Validate a signing payload string against the ZNS memo format spec.
154
+ *
155
+ * This is the single source of truth for payload format validation —
156
+ * it mirrors the Rust indexer's `parse_memo` and `signing_payload` logic.
157
+ *
158
+ * Does NOT validate the signature (see {@link verifyEd25519} for that).
159
+ * Does NOT validate the name against the blockchain (see {@link isAvailable} for that).
160
+ *
161
+ * @param payload - Raw payload string, e.g. `CLAIM:foo:u1abc`
162
+ * @returns Validation result with level and human-readable message
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * const result = zns.validatePayload("CLAIM:alice:u1qvs2...");
167
+ * if (!result.valid) {
168
+ * console.error(result.message);
169
+ * }
170
+ * ```
171
+ */
172
+ validatePayload(payload) {
173
+ const raw = String(payload ?? "").trim();
174
+ if (!raw) {
175
+ return {
176
+ valid: false,
177
+ action: "",
178
+ canonicalAction: null,
179
+ message: "Empty payload.",
180
+ level: "invalid"
181
+ };
182
+ }
183
+ const colonIdx = raw.indexOf(":");
184
+ if (colonIdx === -1) {
185
+ return {
186
+ valid: false,
187
+ action: raw.toUpperCase(),
188
+ canonicalAction: null,
189
+ message: "Missing colon separator. Expected format: ACTION:field1:field2:...",
190
+ level: "invalid"
191
+ };
192
+ }
193
+ const actionUpper = raw.slice(0, colonIdx).toUpperCase();
194
+ const actionLower = raw.slice(0, colonIdx).toLowerCase();
195
+ const rest = raw.slice(colonIdx + 1);
196
+ const parts = rest.split(":");
197
+ switch (actionLower) {
198
+ case "claim": {
199
+ if (parts.length !== 2)
200
+ return mk("invalid", actionUpper, "claim", `Expected CLAIM:<name>:<ua>.`);
201
+ if (!NAME_RE.test(parts[0]))
202
+ return mk("invalid", actionUpper, "claim", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
203
+ if (!this.isValidUnifiedAddress(parts[1]))
204
+ return mk("invalid", actionUpper, "claim", `Invalid unified address: "${parts[1]}".`);
205
+ return mk("valid", actionUpper, "claim", "Valid CLAIM payload.");
206
+ }
207
+ case "buy": {
208
+ if (parts.length !== 2)
209
+ return mk("invalid", actionUpper, "buy", `Expected BUY:<name>:<buyer_ua>.`);
210
+ if (!NAME_RE.test(parts[0]))
211
+ return mk("invalid", actionUpper, "buy", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
212
+ if (!this.isValidUnifiedAddress(parts[1]))
213
+ return mk("invalid", actionUpper, "buy", `Invalid unified address: "${parts[1]}".`);
214
+ return mk("valid", actionUpper, "buy", "Valid BUY payload.");
215
+ }
216
+ case "update": {
217
+ if (parts.length !== 3)
218
+ return mk("invalid", actionUpper, "update", `Expected UPDATE:<name>:<ua>:<nonce>.`);
219
+ if (!NAME_RE.test(parts[0]))
220
+ return mk("invalid", actionUpper, "update", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
221
+ if (!this.isValidUnifiedAddress(parts[1]))
222
+ return mk("invalid", actionUpper, "update", `Invalid unified address: "${parts[1]}".`);
223
+ if (!isWholeNumber(parts[2]))
224
+ return mk("invalid", actionUpper, "update", `Nonce must be a whole number.`);
225
+ return mk("valid", actionUpper, "update", "Valid UPDATE payload.");
226
+ }
227
+ case "list": {
228
+ if (parts.length !== 4)
229
+ return mk("invalid", actionUpper, "list", `Expected LIST:<name>:<price_zats>:<pay_taddr>:<nonce>.`);
230
+ if (!NAME_RE.test(parts[0]))
231
+ return mk("invalid", actionUpper, "list", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
232
+ if (!isWholeNumber(parts[1]) || Number(parts[1]) <= 0)
233
+ return mk("invalid", actionUpper, "list", `Price must be a positive whole number in zats.`);
234
+ if (!isWholeNumber(parts[3]))
235
+ return mk("invalid", actionUpper, "list", `Nonce must be a whole number.`);
236
+ return mk("valid", actionUpper, "list", "Valid LIST payload.");
237
+ }
238
+ case "delist": {
239
+ if (parts.length !== 2)
240
+ return mk("invalid", actionUpper, "delist", `Expected DELIST:<name>:<nonce>.`);
241
+ if (!NAME_RE.test(parts[0]))
242
+ return mk("invalid", actionUpper, "delist", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
243
+ if (!isWholeNumber(parts[1]))
244
+ return mk("invalid", actionUpper, "delist", `Nonce must be a whole number.`);
245
+ return mk("valid", actionUpper, "delist", "Valid DELIST payload.");
246
+ }
247
+ case "release": {
248
+ if (parts.length !== 2)
249
+ return mk("invalid", actionUpper, "release", `Expected RELEASE:<name>:<nonce>.`);
250
+ if (!NAME_RE.test(parts[0]))
251
+ return mk("invalid", actionUpper, "release", `Invalid name. Use lowercase a-z and 0-9, 1 to 62 chars.`);
252
+ if (!isWholeNumber(parts[1]))
253
+ return mk("invalid", actionUpper, "release", `Nonce must be a whole number.`);
254
+ return mk("valid", actionUpper, "release", "Valid RELEASE payload.");
255
+ }
256
+ case "setprice": {
257
+ if (parts.length < 3)
258
+ return mk("invalid", actionUpper, "setprice", `Expected SETPRICE:<count>:<tier1>:...:<nonce>.`);
259
+ const count = Number(parts[0]);
260
+ if (!isWholeNumber(parts[0]) || isNaN(count) || count <= 0)
261
+ return mk("invalid", actionUpper, "setprice", `Count must be a positive whole number.`);
262
+ const priceCount = parts.length - 2;
263
+ if (priceCount !== count)
264
+ return mk("invalid", actionUpper, "setprice", `SETPRICE count mismatch: header is ${count} but found ${priceCount} price tiers.`);
265
+ if (!isWholeNumber(parts[parts.length - 1]))
266
+ return mk("invalid", actionUpper, "setprice", `Nonce must be a whole number.`);
267
+ for (let i = 1; i < parts.length - 1; i++) {
268
+ if (!isWholeNumber(parts[i]) || Number(parts[i]) <= 0)
269
+ return mk("invalid", actionUpper, "setprice", `Tier ${i} must be a positive whole number in zats.`);
270
+ }
271
+ return mk("valid", actionUpper, "setprice", "Valid SETPRICE payload.");
272
+ }
273
+ default:
274
+ return {
275
+ valid: false,
276
+ action: actionUpper,
277
+ canonicalAction: null,
278
+ message: `Unrecognized action "${actionUpper}". Valid actions: CLAIM, BUY, UPDATE, LIST, DELIST, RELEASE, SETPRICE.`,
279
+ level: "unrecognized"
280
+ };
281
+ }
282
+ }
145
283
  /**
146
284
  * Get the claim cost in zatoshis for a name of given length.
147
285
  * @param nameLength The length of the name (1-62)
@@ -208,7 +346,7 @@ var ZNS = class {
208
346
  payload: `LIST:${name}:${price}:${pay_taddr}:${nonce}`,
209
347
  complete: (signature, userPubkey) => {
210
348
  const memo = userPubkey ? `ZNS:LIST:${name}:${price}:${pay_taddr}:${nonce}:${signature}:${userPubkey}` : `ZNS:LIST:${name}:${price}:${pay_taddr}:${nonce}:${signature}`;
211
- return { memo, uri: this.buildZcashUri(this.registryAddress, void 0, memo) };
349
+ return { memo, uri: this.buildZcashUri(this.registryAddress, LIST_COMMISSION, memo) };
212
350
  }
213
351
  };
214
352
  }
@@ -372,6 +510,7 @@ var ZNS = class {
372
510
  export {
373
511
  BUY_COMMISSION,
374
512
  DEFAULT_URL,
513
+ LIST_COMMISSION,
375
514
  MAINNET_UIVK,
376
515
  TESTNET_UIVK,
377
516
  ZNS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zcashname-sdk",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "type": "module",
5
5
  "description": "TypeScript SDK for the Zcash Name System (ZNS)",
6
6
  "main": "dist/zns.cjs",