@cloakely/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.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * WL spot sales: a partner collection gives out whitelist spots and they're sold as a Cloak drop.
3
+ *
4
+ * The drop's supply is the number of spots and its sealed auction picks the winners (the floor, ties and refunds work
5
+ * as in any drop). CloakSpotBook marks the drop as a spot sale. Once it settles, each winning bid names the wallets to
6
+ * whitelist, one per spot it won, and can change them until the deadline. The book holds the list the partner uses.
7
+ * Each winner also gets the drop's NFT as a receipt.
8
+ */
9
+ import { type Address, type Hex } from "viem";
10
+ import { CloakDrops, type CreateDropOptions, type DropBid } from "./drops.js";
11
+ /** How long winners have to name their wallets, by default, once bids are opened. */
12
+ export declare const SPOT_DEFAULT_WALLET_DAYS = 3;
13
+ export declare const SPOT_MAX_WALLET_DAYS = 90;
14
+ export interface SpotSale {
15
+ drop: Address;
16
+ partner: string;
17
+ /** Unix seconds: winners name or change wallets until then. */
18
+ deadline: number;
19
+ }
20
+ /** One winning bid and the wallets it named (none yet: `wallets` is empty). */
21
+ export interface SpotWinner {
22
+ bidId: bigint;
23
+ bidder: Address;
24
+ spots: number;
25
+ wallets: Address[];
26
+ }
27
+ export type CreateSpotSaleOptions = Omit<CreateDropOptions, "gtd" | "baseURI" | "spots"> & {
28
+ /** The partner collection giving the spots. */
29
+ partner: string;
30
+ /** Days after the reveal window that winners have to name wallets (default 3). */
31
+ walletDays?: number;
32
+ baseURI?: string;
33
+ };
34
+ export declare class CloakSpots {
35
+ readonly drops: CloakDrops;
36
+ readonly book: Address;
37
+ constructor(drops: CloakDrops, book: Address);
38
+ private get pc();
39
+ private send;
40
+ /**
41
+ * Create a drop that sells `supply` WL spots for `partner`, then mark it as a spot sale. Two transactions; if the
42
+ * second fails, `open(drop, …)` finishes the job.
43
+ */
44
+ create(o: CreateSpotSaleOptions): Promise<{
45
+ drop: Address;
46
+ txHash: Hex;
47
+ openTx: Hex;
48
+ deadline: number;
49
+ }>;
50
+ /** Mark an existing drop (the caller's, not settled yet) as a WL spot sale. */
51
+ open(drop: Address, partner: string, deadline: number): Promise<Hex>;
52
+ /** The sale on a drop, or null if the drop isn't a WL spot sale. */
53
+ sale(drop: Address): Promise<SpotSale | null>;
54
+ /** Every drop marked as a spot sale, oldest first. */
55
+ sales(): Promise<Address[]>;
56
+ /** Every winning bid (once the drop is settled) with the wallets it has named so far. */
57
+ winners(drop: Address, bids?: DropBid[]): Promise<SpotWinner[]>;
58
+ /** Name the wallets to whitelist for a winning bid of the connected wallet: one per spot. Replaces earlier ones. */
59
+ setWallets(drop: Address, bidId: bigint, wallets: string[]): Promise<Hex>;
60
+ /**
61
+ * The whitelist as CSV, one row per spot: the wallet to whitelist (or blank if the winner hasn't named one), the
62
+ * bid and the bidder. A partner whitelists the `wallet` column.
63
+ */
64
+ static csv(winners: SpotWinner[]): string;
65
+ }
package/dist/spots.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * WL spot sales: a partner collection gives out whitelist spots and they're sold as a Cloak drop.
3
+ *
4
+ * The drop's supply is the number of spots and its sealed auction picks the winners (the floor, ties and refunds work
5
+ * as in any drop). CloakSpotBook marks the drop as a spot sale. Once it settles, each winning bid names the wallets to
6
+ * whitelist, one per spot it won, and can change them until the deadline. The book holds the list the partner uses.
7
+ * Each winner also gets the drop's NFT as a receipt.
8
+ */
9
+ import { getAddress, isAddress } from "viem";
10
+ import { cloakSpotBookAbi } from "./abis.js";
11
+ /** How long winners have to name their wallets, by default, once bids are opened. */
12
+ export const SPOT_DEFAULT_WALLET_DAYS = 3;
13
+ export const SPOT_MAX_WALLET_DAYS = 90;
14
+ export class CloakSpots {
15
+ drops;
16
+ book;
17
+ constructor(drops, book) {
18
+ this.drops = drops;
19
+ this.book = book;
20
+ }
21
+ get pc() {
22
+ return this.drops.publicClient;
23
+ }
24
+ async send(hash) {
25
+ const r = await this.pc.waitForTransactionReceipt({ hash });
26
+ if (r.status !== "success")
27
+ throw new Error(`transaction reverted: ${hash}`);
28
+ return r;
29
+ }
30
+ /**
31
+ * Create a drop that sells `supply` WL spots for `partner`, then mark it as a spot sale. Two transactions; if the
32
+ * second fails, `open(drop, …)` finishes the job.
33
+ */
34
+ async create(o) {
35
+ const partner = o.partner.trim();
36
+ if (!partner || partner.length > 200)
37
+ throw new Error("Name the partner collection (up to 200 characters).");
38
+ const days = o.walletDays ?? SPOT_DEFAULT_WALLET_DAYS;
39
+ if (!(days > 0 && days <= SPOT_MAX_WALLET_DAYS))
40
+ throw new Error(`Winners get 1 to ${SPOT_MAX_WALLET_DAYS} days to name wallets.`);
41
+ const { drop, txHash } = await this.drops.create({ ...o, baseURI: o.baseURI ?? "", maxPerBid: o.maxPerBid ?? 1, spots: { partner } });
42
+ const s = await this.drops.get(drop);
43
+ const deadline = s.revealEnd + Math.round(days * 86400);
44
+ const openTx = await this.open(drop, partner, deadline);
45
+ return { drop, txHash, openTx, deadline };
46
+ }
47
+ /** Mark an existing drop (the caller's, not settled yet) as a WL spot sale. */
48
+ async open(drop, partner, deadline) {
49
+ const w = this.drops.walletClient;
50
+ if (!w)
51
+ throw new Error("CloakSpots: a walletClient is required for this call");
52
+ const all = await this.drops.list();
53
+ const index = all.findIndex((a) => a.toLowerCase() === drop.toLowerCase());
54
+ if (index < 0)
55
+ throw new Error("That isn't a Cloak drop.");
56
+ const { request } = await this.pc.simulateContract({ address: this.book, abi: cloakSpotBookAbi, functionName: "open", args: [BigInt(index), partner.trim(), BigInt(deadline)], account: w.account });
57
+ const h = await w.writeContract(request);
58
+ await this.send(h);
59
+ return h;
60
+ }
61
+ /** The sale on a drop, or null if the drop isn't a WL spot sale. */
62
+ async sale(drop) {
63
+ const s = await this.pc.readContract({ address: this.book, abi: cloakSpotBookAbi, functionName: "sale", args: [drop] });
64
+ return s.open ? { drop, partner: s.partner, deadline: Number(s.deadline) } : null;
65
+ }
66
+ /** Every drop marked as a spot sale, oldest first. */
67
+ async sales() {
68
+ const n = Number(await this.pc.readContract({ address: this.book, abi: cloakSpotBookAbi, functionName: "salesCount" }));
69
+ const out = [];
70
+ for (let i = 0; i < n; i++)
71
+ out.push(await this.pc.readContract({ address: this.book, abi: cloakSpotBookAbi, functionName: "sales", args: [BigInt(i)] }));
72
+ return out;
73
+ }
74
+ /** Every winning bid (once the drop is settled) with the wallets it has named so far. */
75
+ async winners(drop, bids) {
76
+ const all = bids ?? (await this.drops.bids(drop));
77
+ const won = all.filter((b) => b.nfts > 0);
78
+ const [ids, wallets] = await this.pc.readContract({ address: this.book, abi: cloakSpotBookAbi, functionName: "list", args: [drop] });
79
+ return won.map((b) => ({ bidId: b.id, bidder: b.bidder, spots: b.nfts, wallets: wallets.filter((_, i) => ids[i] === b.id) }));
80
+ }
81
+ /** Name the wallets to whitelist for a winning bid of the connected wallet: one per spot. Replaces earlier ones. */
82
+ async setWallets(drop, bidId, wallets) {
83
+ const w = this.drops.walletClient;
84
+ if (!w)
85
+ throw new Error("CloakSpots: a walletClient is required for this call");
86
+ const list = wallets.map((a) => a.trim());
87
+ const bad = list.find((a) => !isAddress(a));
88
+ if (bad !== undefined)
89
+ throw new Error(`${bad || "An empty line"} isn't a wallet address.`);
90
+ const clean = list.map((a) => getAddress(a));
91
+ if (new Set(clean.map((a) => a.toLowerCase())).size !== clean.length)
92
+ throw new Error("Each spot needs a different wallet.");
93
+ const { request } = await this.pc.simulateContract({ address: this.book, abi: cloakSpotBookAbi, functionName: "setWallets", args: [drop, bidId, clean], account: w.account });
94
+ const h = await w.writeContract(request);
95
+ await this.send(h);
96
+ return h;
97
+ }
98
+ /**
99
+ * The whitelist as CSV, one row per spot: the wallet to whitelist (or blank if the winner hasn't named one), the
100
+ * bid and the bidder. A partner whitelists the `wallet` column.
101
+ */
102
+ static csv(winners) {
103
+ const rows = ["wallet,bid,bidder,status"];
104
+ for (const w of winners)
105
+ for (let i = 0; i < w.spots; i++)
106
+ rows.push(`${w.wallets[i] ?? ""},${w.bidId},${w.bidder},${w.wallets[i] ? "named" : "not named yet"}`);
107
+ return rows.join("\n") + "\n";
108
+ }
109
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloakely/sdk",
3
- "version": "0.8.0",
4
- "description": "Cloak on Elysium: sealed-bid token launches and private trading (shielded balances, private swaps, batch auctions, private sends) with on-device zero-knowledge proofs.",
3
+ "version": "0.8.2",
4
+ "description": "Cloak on Elysium: sealed-bid token launches, sealed NFT drops and WL spot sales, and private trading (shielded balances, private swaps, batch auctions, private sends) with on-device zero-knowledge proofs.",
5
5
  "keywords": [
6
6
  "cloak",
7
7
  "elysium",