@rareprotocol/rare-cli 0.4.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ import { h as UtilsMerkleProofParams, i as UtilsMerkleProofArtifact, d as BuildUtilsTreeParams, U as UtilsTreeArtifact, e as UtilsTreeProofParams, f as UtilsTreeProofArtifact, g as UtilsTreeProofVerifyParams } from './batch-listing-Cu5Hoqxs.js';
2
+ import 'viem';
3
+ import './addresses-BE3luaB3.js';
4
+
5
+ declare function buildUtilsTree(params: BuildUtilsTreeParams): UtilsTreeArtifact;
6
+ declare function getUtilsTreeProof(params: UtilsTreeProofParams): UtilsTreeProofArtifact;
7
+ declare function verifyUtilsTreeProof(params: UtilsTreeProofVerifyParams): boolean;
8
+ declare function buildUtilsMerkleProof(params: UtilsMerkleProofParams): UtilsMerkleProofArtifact;
9
+
10
+ export { BuildUtilsTreeParams, UtilsMerkleProofArtifact, UtilsMerkleProofParams, UtilsTreeArtifact, UtilsTreeProofArtifact, UtilsTreeProofParams, UtilsTreeProofVerifyParams, buildUtilsMerkleProof, buildUtilsTree, getUtilsTreeProof, verifyUtilsTreeProof };
package/dist/utils.js ADDED
@@ -0,0 +1,554 @@
1
+ // src/sdk/batch-core.ts
2
+ import {
3
+ concatHex,
4
+ encodePacked,
5
+ getAddress,
6
+ isAddress,
7
+ isAddressEqual,
8
+ isHex,
9
+ keccak256,
10
+ maxUint256
11
+ } from "viem";
12
+
13
+ // src/sdk/amounts-core.ts
14
+ import { parseEther } from "viem";
15
+ var MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
16
+ var MIN_SAFE_INTEGER_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);
17
+ function toInteger(value, field) {
18
+ if (typeof value === "bigint") return value;
19
+ if (typeof value === "number") {
20
+ if (!Number.isFinite(value) || !Number.isInteger(value)) {
21
+ throw new Error(`${field} must be an integer.`);
22
+ }
23
+ if (!Number.isSafeInteger(value)) {
24
+ throw new Error(`${field} is too large to pass as a number. Pass it as a string or bigint to avoid precision loss.`);
25
+ }
26
+ return BigInt(value);
27
+ }
28
+ const normalized = value.trim();
29
+ if (normalized.length === 0) {
30
+ throw new Error(`${field} must be an integer.`);
31
+ }
32
+ try {
33
+ return BigInt(normalized);
34
+ } catch {
35
+ throw new Error(`${field} must be an integer.`);
36
+ }
37
+ }
38
+ function toNonNegativeInteger(value, field) {
39
+ const normalized = toInteger(value, field);
40
+ if (normalized < 0n) {
41
+ throw new Error(`${field} must be greater than or equal to 0.`);
42
+ }
43
+ return normalized;
44
+ }
45
+ function toPositiveInteger(value, field) {
46
+ const normalized = toInteger(value, field);
47
+ if (normalized <= 0n) {
48
+ throw new Error(`${field} must be greater than 0.`);
49
+ }
50
+ return normalized;
51
+ }
52
+
53
+ // src/sdk/batch-core.ts
54
+ var contractColumnNames = [
55
+ "contract",
56
+ "contract address",
57
+ "contractAddress",
58
+ "contract_address",
59
+ "collection",
60
+ "collection address",
61
+ "collectionAddress",
62
+ "token contract",
63
+ "tokenContract"
64
+ ];
65
+ var tokenIdColumnNames = [
66
+ "token id",
67
+ "tokenId",
68
+ "token_id",
69
+ "id",
70
+ "nft id"
71
+ ];
72
+ var chainIdColumnNames = [
73
+ "chain id",
74
+ "chainId",
75
+ "chain_id",
76
+ "chain"
77
+ ];
78
+ function buildBatchTokenTreeArtifact(params) {
79
+ const tokens = parseBatchTokenList(params);
80
+ const leaves = tokens.map((token) => hashBatchToken(token.contractAddress, token.tokenId));
81
+ const levels = buildMerkleLevels(leaves);
82
+ const root = levels.at(-1)?.[0];
83
+ if (root === void 0) {
84
+ throw new Error("Batch token list must include at least one token.");
85
+ }
86
+ const chainId = inferArtifactChainId(tokens);
87
+ return {
88
+ version: 1,
89
+ type: "rare-batch-token-list",
90
+ root,
91
+ count: tokens.length,
92
+ ...chainId === void 0 ? {} : { chainId },
93
+ tokens,
94
+ entries: tokens.map((token, index) => buildBatchTokenTreeEntry(token, leaves, levels, index))
95
+ };
96
+ }
97
+ function parseBatchTokenList(params) {
98
+ const format = params.format ?? detectBatchTokenInputFormat(params.content, params.sourceName);
99
+ const rawTokens = format === "json" ? parseJsonBatchTokens(params.content) : parseCsvBatchTokens(params.content);
100
+ return normalizeBatchTokens(rawTokens, params.chainId);
101
+ }
102
+ function getBatchTokenProof(params) {
103
+ const contractAddress = normalizeAddressValue(params.contractAddress, "contractAddress");
104
+ const tokenId = normalizeTokenId(params.tokenId, "tokenId");
105
+ const chainId = params.chainId === void 0 ? params.artifact.chainId : normalizeChainId(params.chainId, "chainId");
106
+ if (params.artifact.chainId !== void 0 && chainId !== void 0 && chainId !== params.artifact.chainId) {
107
+ throw new Error(`Token chainId ${chainId} does not match artifact chainId ${params.artifact.chainId}.`);
108
+ }
109
+ const entry = params.artifact.entries.find((candidate) => isAddressEqual(candidate.contractAddress, contractAddress) && candidate.tokenId === tokenId);
110
+ if (entry === void 0) {
111
+ throw new Error(`Token ${contractAddress} #${tokenId} is not present in the batch token list.`);
112
+ }
113
+ return {
114
+ version: 1,
115
+ type: "rare-batch-token-proof",
116
+ root: params.artifact.root,
117
+ contractAddress: entry.contractAddress,
118
+ tokenId: entry.tokenId,
119
+ ...chainId === void 0 ? {} : { chainId },
120
+ leaf: entry.leaf,
121
+ proof: entry.proof,
122
+ valid: verifyBatchTokenProof({
123
+ root: params.artifact.root,
124
+ contractAddress: entry.contractAddress,
125
+ tokenId: entry.tokenId,
126
+ proof: entry.proof
127
+ })
128
+ };
129
+ }
130
+ function verifyBatchTokenProof(params) {
131
+ const root = normalizeBytes32(params.root, "root");
132
+ const proof = params.proof.map((entry, index) => normalizeBytes32(entry, `proof[${index}]`));
133
+ const computedRoot = proof.reduce(
134
+ (hash, proofItem) => parentHash(hash, proofItem),
135
+ hashBatchToken(params.contractAddress, params.tokenId)
136
+ );
137
+ return computedRoot === root;
138
+ }
139
+ function hashBatchToken(contractAddress, tokenId) {
140
+ const normalizedAddress = normalizeAddressValue(contractAddress, "contractAddress");
141
+ const normalizedTokenId = normalizeTokenId(tokenId, "tokenId");
142
+ return keccak256(encodePacked(["address", "uint256"], [normalizedAddress, BigInt(normalizedTokenId)]));
143
+ }
144
+ function normalizeBytes32(value, field) {
145
+ if (!isHex(value, { strict: true }) || value.length !== 66) {
146
+ throw new Error(`${field} must be a bytes32 hex string.`);
147
+ }
148
+ const normalized = value.toLocaleLowerCase();
149
+ if (!isHex(normalized, { strict: true }) || normalized.length !== 66) {
150
+ throw new Error(`${field} must be a bytes32 hex string.`);
151
+ }
152
+ return normalized;
153
+ }
154
+ function detectBatchTokenInputFormat(content, sourceName) {
155
+ const lowerSource = sourceName?.toLowerCase();
156
+ if (lowerSource?.endsWith(".json")) {
157
+ return "json";
158
+ }
159
+ if (lowerSource?.endsWith(".csv")) {
160
+ return "csv";
161
+ }
162
+ const trimmed = content.trimStart();
163
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
164
+ return "json";
165
+ }
166
+ return "csv";
167
+ }
168
+ function parseJsonBatchTokens(content) {
169
+ const parsed = parseJson(content, "batch token JSON");
170
+ const entries = Array.isArray(parsed) ? parsed : isRecord(parsed) && Array.isArray(parsed.tokens) ? parsed.tokens : void 0;
171
+ if (entries === void 0) {
172
+ throw new Error("Batch token JSON must be an array of token objects or an object with a tokens array.");
173
+ }
174
+ return entries.map((entry, index) => extractJsonBatchToken(entry, index));
175
+ }
176
+ function parseCsvBatchTokens(content) {
177
+ const rows = content.replace(/^\uFEFF/, "").split(/\r?\n/).map(parseCsvRow).filter((row) => row.some((cell) => cell.length > 0));
178
+ const [firstRow] = rows;
179
+ if (firstRow === void 0) {
180
+ throw new Error("Batch token CSV must include at least one token.");
181
+ }
182
+ const contractIndex = firstRow.findIndex(isContractColumnName);
183
+ const tokenIdIndex = firstRow.findIndex(isTokenIdColumnName);
184
+ const chainIdIndex = firstRow.findIndex(isChainIdColumnName);
185
+ if (contractIndex >= 0 && tokenIdIndex >= 0) {
186
+ return rows.slice(1).map((row, index) => extractCsvBatchToken(row, {
187
+ rowNumber: index + 2,
188
+ contractIndex,
189
+ tokenIdIndex,
190
+ chainIdIndex
191
+ }));
192
+ }
193
+ const firstContractCell = firstRow[0];
194
+ if (firstRow.length >= 2 && firstContractCell !== void 0 && isAddress(firstContractCell)) {
195
+ return rows.map((row, index) => extractCsvBatchToken(row, {
196
+ rowNumber: index + 1,
197
+ contractIndex: 0,
198
+ tokenIdIndex: 1,
199
+ chainIdIndex: row.length > 2 ? 2 : -1
200
+ }));
201
+ }
202
+ throw new Error("Batch token CSV must include contract address and token ID columns.");
203
+ }
204
+ function extractCsvBatchToken(row, opts) {
205
+ const contractAddress = row[opts.contractIndex];
206
+ const tokenId = row[opts.tokenIdIndex];
207
+ const chainId = opts.chainIdIndex >= 0 ? row[opts.chainIdIndex] : void 0;
208
+ if (contractAddress === void 0 || contractAddress.length === 0) {
209
+ throw new Error(`Batch token CSV row ${opts.rowNumber} is missing a contract address.`);
210
+ }
211
+ if (tokenId === void 0 || tokenId.length === 0) {
212
+ throw new Error(`Batch token CSV row ${opts.rowNumber} is missing a token ID.`);
213
+ }
214
+ return {
215
+ contractAddress,
216
+ tokenId,
217
+ ...chainId === void 0 || chainId.length === 0 ? {} : { chainId }
218
+ };
219
+ }
220
+ function parseCsvRow(line) {
221
+ return line.split(",").map((cell) => cell.trim().replace(/^"|"$/g, "").replace(/""/g, '"'));
222
+ }
223
+ function extractJsonBatchToken(entry, index) {
224
+ if (!isRecord(entry)) {
225
+ throw new Error(`Batch token JSON entry ${index} must be an object with contractAddress and tokenId fields.`);
226
+ }
227
+ const contractAddress = readStringField(entry, contractColumnNames);
228
+ const tokenId = readIntegerField(entry, tokenIdColumnNames);
229
+ const chainId = readIntegerField(entry, chainIdColumnNames);
230
+ if (contractAddress === void 0) {
231
+ throw new Error(`Batch token JSON entry ${index} is missing a contractAddress field.`);
232
+ }
233
+ if (tokenId === void 0) {
234
+ throw new Error(`Batch token JSON entry ${index} is missing a tokenId field.`);
235
+ }
236
+ return {
237
+ contractAddress,
238
+ tokenId,
239
+ ...chainId === void 0 ? {} : { chainId }
240
+ };
241
+ }
242
+ function normalizeBatchTokens(rawTokens, chainIdInput) {
243
+ if (rawTokens.length === 0) {
244
+ throw new Error("Batch token list must include at least one token.");
245
+ }
246
+ const explicitChainId = chainIdInput === void 0 ? void 0 : normalizeChainId(chainIdInput, "chainId");
247
+ const rowChainIds = rawTokens.map((token, index) => token.chainId === void 0 ? void 0 : normalizeChainId(token.chainId, `batch token at index ${index} chainId`)).filter((chainId2) => chainId2 !== void 0);
248
+ const uniqueRowChainIds = [...new Set(rowChainIds)];
249
+ if (uniqueRowChainIds.length > 1) {
250
+ throw new Error(`Batch token list must use one chainId; found ${uniqueRowChainIds.join(", ")}.`);
251
+ }
252
+ if (explicitChainId !== void 0 && uniqueRowChainIds[0] !== void 0 && explicitChainId !== uniqueRowChainIds[0]) {
253
+ throw new Error(`Input chainId ${uniqueRowChainIds[0]} does not match --chain-id ${explicitChainId}.`);
254
+ }
255
+ const chainId = explicitChainId ?? uniqueRowChainIds[0];
256
+ const tokens = rawTokens.map((rawToken, index) => normalizeBatchToken(rawToken, index, chainId));
257
+ const duplicate = tokens.find((token, index) => tokens.slice(0, index).some((candidate) => candidate.chainId === token.chainId && candidate.tokenId === token.tokenId && isAddressEqual(candidate.contractAddress, token.contractAddress)));
258
+ if (duplicate !== void 0) {
259
+ throw new Error(`Duplicate batch token: ${duplicate.contractAddress} #${duplicate.tokenId}.`);
260
+ }
261
+ return [...tokens].sort(compareBatchTokens);
262
+ }
263
+ function buildBatchTokenTreeEntry(token, leaves, levels, index) {
264
+ const leaf = leaves[index];
265
+ if (leaf === void 0) {
266
+ throw new Error(`Missing Merkle leaf for batch token at index ${index}.`);
267
+ }
268
+ return {
269
+ ...token,
270
+ leaf,
271
+ proof: buildMerkleProof(levels, index)
272
+ };
273
+ }
274
+ function normalizeBatchToken(rawToken, index, chainId) {
275
+ return {
276
+ contractAddress: normalizeAddressValue(rawToken.contractAddress, `batch token at index ${index} contractAddress`),
277
+ tokenId: normalizeTokenId(rawToken.tokenId, `batch token at index ${index} tokenId`),
278
+ ...chainId === void 0 ? {} : { chainId }
279
+ };
280
+ }
281
+ function normalizeAddressValue(value, field) {
282
+ const trimmed = value.trim();
283
+ if (!isAddress(trimmed)) {
284
+ throw new Error(`${field} must be a valid 0x address.`);
285
+ }
286
+ return getAddress(trimmed);
287
+ }
288
+ function normalizeTokenId(value, field) {
289
+ const normalized = toNonNegativeInteger(normalizeIntegerInput(value, field), field);
290
+ if (normalized > maxUint256) {
291
+ throw new Error(`${field} must fit in uint256.`);
292
+ }
293
+ return normalized.toString();
294
+ }
295
+ function normalizeChainId(value, field) {
296
+ const normalized = toPositiveInteger(normalizeIntegerInput(value, field), field);
297
+ if (normalized > BigInt(Number.MAX_SAFE_INTEGER)) {
298
+ throw new Error(`${field} must fit in a safe JavaScript integer.`);
299
+ }
300
+ return Number(normalized);
301
+ }
302
+ function normalizeIntegerInput(value, field) {
303
+ if (typeof value === "string") {
304
+ const trimmed = value.trim();
305
+ if (trimmed.length === 0) {
306
+ throw new Error(`${field} must be an integer.`);
307
+ }
308
+ return trimmed;
309
+ }
310
+ return value;
311
+ }
312
+ function compareBatchTokens(a, b) {
313
+ const addressCompare = a.contractAddress.localeCompare(b.contractAddress);
314
+ if (addressCompare !== 0) {
315
+ return addressCompare;
316
+ }
317
+ return a.tokenId.localeCompare(b.tokenId);
318
+ }
319
+ function inferArtifactChainId(tokens) {
320
+ const [chainId] = [...new Set(tokens.map((token) => token.chainId))];
321
+ return chainId;
322
+ }
323
+ function readStringField(entry, keys) {
324
+ const value = keys.map((key) => entry[key]).find((candidate) => typeof candidate === "string");
325
+ return typeof value === "string" ? value : void 0;
326
+ }
327
+ function readIntegerField(entry, keys) {
328
+ const value = keys.map((key) => entry[key]).find((candidate) => typeof candidate === "string" || typeof candidate === "number" || typeof candidate === "bigint");
329
+ if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
330
+ return value;
331
+ }
332
+ return void 0;
333
+ }
334
+ function isContractColumnName(value) {
335
+ return isColumnName(value, contractColumnNames);
336
+ }
337
+ function isTokenIdColumnName(value) {
338
+ return isColumnName(value, tokenIdColumnNames);
339
+ }
340
+ function isChainIdColumnName(value) {
341
+ return isColumnName(value, chainIdColumnNames);
342
+ }
343
+ function isColumnName(value, candidates) {
344
+ const normalized = normalizeColumnName(value);
345
+ return candidates.some((candidate) => normalizeColumnName(candidate) === normalized);
346
+ }
347
+ function normalizeColumnName(value) {
348
+ return value.trim().replace(/[\s_-]+/g, "").toLowerCase();
349
+ }
350
+ function buildMerkleLevels(leaves) {
351
+ if (leaves.length === 0) {
352
+ return [];
353
+ }
354
+ if (leaves.length === 1) {
355
+ const [leaf] = leaves;
356
+ return leaf === void 0 ? [] : [[leaf]];
357
+ }
358
+ const level = [...leaves];
359
+ return [
360
+ level,
361
+ ...buildMerkleLevels(nextMerkleLevel(level))
362
+ ];
363
+ }
364
+ function nextMerkleLevel(level) {
365
+ return level.filter((_node, index) => index % 2 === 0).map((node, pairIndex) => {
366
+ const sibling = level[pairIndex * 2 + 1];
367
+ return sibling === void 0 ? node : parentHash(node, sibling);
368
+ });
369
+ }
370
+ function buildMerkleProof(levels, leafIndex) {
371
+ return levels.slice(0, -1).reduce((state, level) => {
372
+ const siblingIndex = state.index % 2 === 0 ? state.index + 1 : state.index - 1;
373
+ const sibling = level[siblingIndex];
374
+ return {
375
+ index: Math.floor(state.index / 2),
376
+ proof: sibling === void 0 ? state.proof : [...state.proof, sibling]
377
+ };
378
+ }, { index: leafIndex, proof: [] }).proof;
379
+ }
380
+ function parentHash(a, b) {
381
+ const [left, right] = a <= b ? [a, b] : [b, a];
382
+ return keccak256(concatHex([left, right]));
383
+ }
384
+ function parseJson(content, label) {
385
+ try {
386
+ return JSON.parse(content);
387
+ } catch (error) {
388
+ const message = error instanceof Error ? error.message : "invalid JSON";
389
+ throw new Error(`Could not parse ${label}: ${message}`);
390
+ }
391
+ }
392
+ function isRecord(value) {
393
+ return typeof value === "object" && value !== null && !Array.isArray(value);
394
+ }
395
+
396
+ // src/sdk/merkle-core.ts
397
+ import { Buffer } from "buffer";
398
+ import { MerkleTree } from "merkletreejs";
399
+ import {
400
+ encodePacked as encodePacked2,
401
+ getAddress as getAddress2,
402
+ isAddress as isAddress2,
403
+ isAddressEqual as isAddressEqual2,
404
+ isHex as isHex2,
405
+ keccak256 as keccak2562
406
+ } from "viem";
407
+ function hexBuffer(hex) {
408
+ return Buffer.from(hex.startsWith("0x") ? hex.slice(2) : hex, "hex");
409
+ }
410
+ function tokenLeaf(contract, tokenId) {
411
+ const packed = encodePacked2(["address", "uint256"], [contract, tokenId]);
412
+ return hexBuffer(keccak2562(packed));
413
+ }
414
+ function addressLeaf(address) {
415
+ return hexBuffer(keccak2562(address));
416
+ }
417
+ function parseBytes32(value, field) {
418
+ if (!isHex2(value) || value.length !== 66) {
419
+ throw new Error(`${field} must be a 0x-prefixed bytes32 hex string`);
420
+ }
421
+ return value;
422
+ }
423
+ function parseBytes32Array(values, field) {
424
+ return values.map((value, index) => parseBytes32(value, `${field}[${index}]`));
425
+ }
426
+ function compareTokenEntries(a, b) {
427
+ if (!isAddressEqual2(a.contract, b.contract)) {
428
+ return a.contract.localeCompare(b.contract);
429
+ }
430
+ return a.tokenId.localeCompare(b.tokenId);
431
+ }
432
+ function normalizeTokenEntry(token) {
433
+ if (!isAddress2(token.contract)) {
434
+ throw new Error(`Invalid token contract address: ${token.contract}`);
435
+ }
436
+ return {
437
+ contract: getAddress2(token.contract),
438
+ tokenId: String(token.tokenId),
439
+ tokenIdBigInt: toInteger(token.tokenId, "tokenId")
440
+ };
441
+ }
442
+ function buildBatchListingTree(tokens) {
443
+ if (tokens.length < 2) {
444
+ throw new Error("buildBatchListingTree requires at least two tokens");
445
+ }
446
+ const sorted = tokens.map(normalizeTokenEntry).sort(compareTokenEntries);
447
+ const leaves = sorted.map((token) => tokenLeaf(token.contract, token.tokenIdBigInt));
448
+ const tree = new MerkleTree(leaves, (data) => hexBuffer(keccak2562(data)), {
449
+ sortPairs: true
450
+ });
451
+ return {
452
+ root: parseBytes32(tree.getHexRoot(), "root"),
453
+ tree,
454
+ sortedTokens: sorted.map(({ contract, tokenId }) => ({ contract, tokenId }))
455
+ };
456
+ }
457
+ function buildAllowListTree(addresses) {
458
+ if (addresses.length < 2) {
459
+ throw new Error("buildAllowListTree requires at least two addresses");
460
+ }
461
+ const sorted = addresses.map((address) => {
462
+ if (!isAddress2(address)) throw new Error(`Invalid allowlist address: ${address}`);
463
+ return getAddress2(address);
464
+ }).sort((a, b) => a.localeCompare(b));
465
+ const leaves = sorted.map(addressLeaf);
466
+ const tree = new MerkleTree(leaves, (data) => hexBuffer(keccak2562(data)), {
467
+ sortPairs: true
468
+ });
469
+ return {
470
+ root: parseBytes32(tree.getHexRoot(), "root"),
471
+ tree,
472
+ sortedAddresses: sorted
473
+ };
474
+ }
475
+ function getTokenProof(tree, contract, tokenId) {
476
+ const leaf = tokenLeaf(getAddress2(contract), tokenId);
477
+ return parseBytes32Array(tree.getHexProof(leaf), "proof");
478
+ }
479
+ function getAddressProof(tree, address) {
480
+ const leaf = addressLeaf(getAddress2(address));
481
+ return parseBytes32Array(tree.getHexProof(leaf), "proof");
482
+ }
483
+ function buildMerkleProofArtifact(artifact, contract, tokenId, buyer) {
484
+ const tokenIdBig = toInteger(tokenId, "tokenId");
485
+ const contractChecksum = getAddress2(contract);
486
+ const found = artifact.tokens.find(
487
+ (token) => isAddressEqual2(token.contract, contractChecksum) && BigInt(token.tokenId) === tokenIdBig
488
+ );
489
+ if (found === void 0) {
490
+ throw new Error(
491
+ `Token ${contractChecksum}/${tokenIdBig.toString()} is not in this root artifact's token set`
492
+ );
493
+ }
494
+ const { tree, root } = buildBatchListingTree(
495
+ artifact.tokens.map((token) => ({ contract: token.contract, tokenId: token.tokenId }))
496
+ );
497
+ if (root !== artifact.root) {
498
+ throw new Error(
499
+ `Recomputed NFT tree root (${root}) does not match artifact root (${artifact.root}). Artifact is corrupt or tree encoding has drifted.`
500
+ );
501
+ }
502
+ const allowListProofFields = buildAllowListProofFields(artifact, buyer);
503
+ return {
504
+ root: artifact.root,
505
+ contract: contractChecksum,
506
+ tokenId: tokenIdBig.toString(),
507
+ proof: getTokenProof(tree, contractChecksum, tokenIdBig),
508
+ ...allowListProofFields ?? {}
509
+ };
510
+ }
511
+ function buildAllowListProofFields(artifact, buyer) {
512
+ if (artifact.allowList === void 0) return void 0;
513
+ if (buyer === void 0) {
514
+ throw new Error(
515
+ "This root has an allowlist; pass buyer address to buildMerkleProofArtifact to include allowListProof"
516
+ );
517
+ }
518
+ if (!isAddress2(buyer)) throw new Error(`Invalid buyer address: ${buyer}`);
519
+ const buyerChecksum = getAddress2(buyer);
520
+ const inAllowList = artifact.allowList.addresses.some((address) => isAddressEqual2(address, buyerChecksum));
521
+ if (!inAllowList) {
522
+ throw new Error(`Buyer ${buyerChecksum} is not in the allowlist`);
523
+ }
524
+ const { tree, root } = buildAllowListTree(artifact.allowList.addresses);
525
+ if (root !== artifact.allowList.root) {
526
+ throw new Error(
527
+ `Recomputed allowlist root (${root}) does not match artifact (${artifact.allowList.root})`
528
+ );
529
+ }
530
+ return {
531
+ allowListProof: getAddressProof(tree, buyerChecksum),
532
+ allowListAddress: buyerChecksum
533
+ };
534
+ }
535
+
536
+ // src/sdk/public-utils.ts
537
+ function buildUtilsTree(params) {
538
+ return buildBatchTokenTreeArtifact(params);
539
+ }
540
+ function getUtilsTreeProof(params) {
541
+ return getBatchTokenProof(params);
542
+ }
543
+ function verifyUtilsTreeProof(params) {
544
+ return verifyBatchTokenProof(params);
545
+ }
546
+ function buildUtilsMerkleProof(params) {
547
+ return buildMerkleProofArtifact(params.artifact, params.contract, params.tokenId, params.buyer);
548
+ }
549
+ export {
550
+ buildUtilsMerkleProof,
551
+ buildUtilsTree,
552
+ getUtilsTreeProof,
553
+ verifyUtilsTreeProof
554
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rareprotocol/rare-cli",
3
- "version": "0.4.1",
3
+ "version": "1.0.0",
4
4
  "description": "CLI tool for interacting with the RARE protocol smart contracts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,18 +29,29 @@
29
29
  "LICENSE"
30
30
  ],
31
31
  "exports": {
32
- ".": {
33
- "import": "./dist/index.js"
34
- },
35
32
  "./client": {
36
33
  "import": "./dist/client.js",
37
34
  "types": "./dist/client.d.ts"
35
+ },
36
+ "./contracts": {
37
+ "import": "./dist/contracts.js",
38
+ "types": "./dist/contracts.d.ts"
39
+ },
40
+ "./utils": {
41
+ "import": "./dist/utils.js",
42
+ "types": "./dist/utils.d.ts"
38
43
  }
39
44
  },
40
45
  "typesVersions": {
41
46
  "*": {
42
47
  "client": [
43
48
  "dist/client.d.ts"
49
+ ],
50
+ "contracts": [
51
+ "dist/contracts.d.ts"
52
+ ],
53
+ "utils": [
54
+ "dist/utils.d.ts"
44
55
  ]
45
56
  }
46
57
  },
@@ -48,17 +59,52 @@
48
59
  "rare": "./dist/index.js"
49
60
  },
50
61
  "scripts": {
51
- "build": "tsup",
62
+ "typecheck": "tsc --noEmit",
63
+ "build": "npm run typecheck && tsup",
52
64
  "dev": "tsup --watch",
65
+ "docs:generate": "typedoc && tsx scripts/postprocess-sdk-docs.ts && tsx scripts/generate-sdk-method-docs.ts && tsx scripts/generate-cli-docs.ts",
66
+ "docs:serve": "npm run docs:generate && docusaurus start docs-site --no-open",
67
+ "docs:build": "npm run docs:generate && docusaurus build docs-site --out-dir ../.docs-build",
68
+ "docs:preview": "npm run docs:build && docusaurus serve docs-site --dir ../.docs-build --no-open",
69
+ "generate:types": "set -a; [ -f .env ] && . ./.env; set +a; openapi-typescript ${RARE_API_BASE_URL:-https://api.superrare.com}/doc -o src/data-access/schema.d.ts",
70
+ "knip": "knip",
71
+ "lint": "eslint .",
72
+ "lint:fix": "eslint . --fix",
73
+ "test": "npm run build && vitest run test/unit test/integration test/e2e --config vitest.config.ts",
74
+ "test:live": "npm run build && vitest run test/unit test/integration test/e2e test/e2e-live --config vitest.config.ts",
75
+ "test:clear-locks": "node scripts/clear-locks.mjs",
76
+ "test:coverage": "npm run build && vitest run test/unit test/integration test/e2e --coverage --config vitest.config.ts",
53
77
  "prepare": "npm run build",
54
78
  "prepublishOnly": "npm run build"
55
79
  },
56
80
  "dependencies": {
57
- "commander": "^12.0.0",
58
- "viem": "^2.0.0"
81
+ "commander": "12.1.0",
82
+ "merkletreejs": "0.6.0",
83
+ "openapi-fetch": "0.17.0",
84
+ "viem": "2.48.8"
59
85
  },
60
86
  "devDependencies": {
61
- "tsup": "^8.0.0",
62
- "typescript": "^5.0.0"
87
+ "@docusaurus/core": "^3.10.1",
88
+ "@docusaurus/preset-classic": "^3.10.1",
89
+ "@eslint/js": "10.0.1",
90
+ "@types/node": "25.6.0",
91
+ "@typescript-eslint/parser": "8.59.3",
92
+ "@typescript-eslint/rule-tester": "8.59.2",
93
+ "@typescript-eslint/utils": "8.59.2",
94
+ "@vitest/coverage-v8": "4.1.5",
95
+ "eslint": "10.3.0",
96
+ "eslint-plugin-functional": "9.0.4",
97
+ "globals": "17.6.0",
98
+ "knip": "5.88.1",
99
+ "openapi-typescript": "7.13.0",
100
+ "tslib": "^2.8.1",
101
+ "tsup": "8.5.1",
102
+ "tsx": "^4.22.3",
103
+ "typedoc": "^0.28.19",
104
+ "typedoc-plugin-markdown": "^4.11.0",
105
+ "typescript": "5.9.3",
106
+ "typescript-eslint": "8.59.2",
107
+ "vitest": "4.1.5",
108
+ "webpack": "5.95.0"
63
109
  }
64
110
  }