@cloakely/sdk 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cloak.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { type Account, type Address, type Chain, type Hex, type PublicClient, type Transport, type WalletClient } from "viem";
2
+ /** `lpLockUntil` for "locked forever, the creator earns the pool's trading fees". */
3
+ export declare const LOCK_FOREVER_FEES: bigint;
2
4
  import { type BidPreimage } from "./seal.js";
3
5
  export type Wallet = WalletClient<Transport, Chain, Account>;
4
6
  export interface LaunchParams {
@@ -21,8 +23,11 @@ export interface LaunchParams {
21
23
  revealEnd: number;
22
24
  /** Share of the raise that seeds the pool, in bps (default 5000). */
23
25
  lpBps?: number;
24
- /** Unix seconds until LP shares can be released to the creator; 0 / "burn" burns them (default). */
25
- lpLockUntil?: number | "burn";
26
+ /**
27
+ * What happens to the pool's LP: "burn" (default) burns it; "fees" locks it forever and pays the creator the pool's
28
+ * 0.3% trading fees (CloakFeeLocker); a unix time locks it until then, when the creator can take it.
29
+ */
30
+ lpLockUntil?: number | "burn" | "fees";
26
31
  noRevealPenaltyBps?: number;
27
32
  creator?: Address;
28
33
  metadataURI?: string;
@@ -57,6 +62,8 @@ export interface AuctionState {
57
62
  pair: Address;
58
63
  /** The launch's Uniswap V2 pool (token / WHYPE), tradable and tracked everywhere. Zero for a CloakPair market. */
59
64
  uniPair: Address;
65
+ /** The LP is locked forever and the creator earns its trading fees (see `creatorFees`). */
66
+ creatorFees: boolean;
60
67
  tokenName: string;
61
68
  tokenSymbol: string;
62
69
  /** Launch fee on a successful raise, fixed when the auction was created. */
@@ -126,6 +133,20 @@ export declare class Cloak {
126
133
  private listLegacy;
127
134
  private listCurrent;
128
135
  getAuction(auction: Address): Promise<AuctionState>;
136
+ /**
137
+ * The creator's trading fees for a launch whose LP is locked forever: what can be claimed now (in tokens and WHYPE),
138
+ * who receives it, and the locker position. `null` for launches without creator fees.
139
+ */
140
+ creatorFees(auction: Address): Promise<{
141
+ locker: Address;
142
+ position: bigint;
143
+ beneficiary: Address;
144
+ claimableLp: bigint;
145
+ tokens: bigint;
146
+ whype: bigint;
147
+ } | null>;
148
+ /** Pay a launch's earned trading fees to its creator (anyone can send this; the fees always go to the creator). */
149
+ claimCreatorFees(auction: Address): Promise<Hex>;
129
150
  /** Revealed demand per tick (empty until the reveal phase — that is the point). */
130
151
  demandCurve(auction: Address): Promise<{
131
152
  tick: number;
@@ -225,6 +246,10 @@ export declare class Cloak {
225
246
  readonly name: "seeder_";
226
247
  readonly type: "address";
227
248
  readonly internalType: "contract IMarketSeeder";
249
+ }, {
250
+ readonly name: "feeLocker_";
251
+ readonly type: "address";
252
+ readonly internalType: "contract IFeeLocker";
228
253
  }];
229
254
  readonly stateMutability: "nonpayable";
230
255
  }, {
@@ -260,6 +285,16 @@ export declare class Cloak {
260
285
  readonly internalType: "uint256";
261
286
  }];
262
287
  readonly stateMutability: "view";
288
+ }, {
289
+ readonly type: "function";
290
+ readonly name: "LOCK_FOREVER_FEES";
291
+ readonly inputs: readonly [];
292
+ readonly outputs: readonly [{
293
+ readonly name: "";
294
+ readonly type: "uint64";
295
+ readonly internalType: "uint64";
296
+ }];
297
+ readonly stateMutability: "view";
263
298
  }, {
264
299
  readonly type: "function";
265
300
  readonly name: "MAX_TICKS";
@@ -482,6 +517,16 @@ export declare class Cloak {
482
517
  readonly internalType: "uint256";
483
518
  }];
484
519
  readonly stateMutability: "view";
520
+ }, {
521
+ readonly type: "function";
522
+ readonly name: "creatorFees";
523
+ readonly inputs: readonly [];
524
+ readonly outputs: readonly [{
525
+ readonly name: "";
526
+ readonly type: "bool";
527
+ readonly internalType: "bool";
528
+ }];
529
+ readonly stateMutability: "view";
485
530
  }, {
486
531
  readonly type: "function";
487
532
  readonly name: "creatorOwed";
@@ -546,6 +591,26 @@ export declare class Cloak {
546
591
  readonly internalType: "bool";
547
592
  }];
548
593
  readonly stateMutability: "view";
594
+ }, {
595
+ readonly type: "function";
596
+ readonly name: "feeLocker";
597
+ readonly inputs: readonly [];
598
+ readonly outputs: readonly [{
599
+ readonly name: "";
600
+ readonly type: "address";
601
+ readonly internalType: "contract IFeeLocker";
602
+ }];
603
+ readonly stateMutability: "view";
604
+ }, {
605
+ readonly type: "function";
606
+ readonly name: "feePosition";
607
+ readonly inputs: readonly [];
608
+ readonly outputs: readonly [{
609
+ readonly name: "";
610
+ readonly type: "uint256";
611
+ readonly internalType: "uint256";
612
+ }];
613
+ readonly stateMutability: "view";
549
614
  }, {
550
615
  readonly type: "function";
551
616
  readonly name: "feesOwed";
@@ -1008,6 +1073,21 @@ export declare class Cloak {
1008
1073
  readonly internalType: "bytes";
1009
1074
  }];
1010
1075
  readonly anonymous: false;
1076
+ }, {
1077
+ readonly type: "event";
1078
+ readonly name: "CreatorFeesOn";
1079
+ readonly inputs: readonly [{
1080
+ readonly name: "locker";
1081
+ readonly type: "address";
1082
+ readonly indexed: false;
1083
+ readonly internalType: "address";
1084
+ }, {
1085
+ readonly name: "position";
1086
+ readonly type: "uint256";
1087
+ readonly indexed: false;
1088
+ readonly internalType: "uint256";
1089
+ }];
1090
+ readonly anonymous: false;
1011
1091
  }, {
1012
1092
  readonly type: "event";
1013
1093
  readonly name: "DustSwept";
package/dist/cloak.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { zeroAddress, encodeAbiParameters, keccak256, parseEventLogs, toHex, } from "viem";
2
- import { cloakAuctionAbi, cloakFactoryAbi, cloakPairAbi, cloakTokenAbi } from "./abis.js";
2
+ import { cloakAuctionAbi, cloakFactoryAbi, cloakFeeLockerAbi, cloakPairAbi, cloakTokenAbi } from "./abis.js";
3
+ /** `lpLockUntil` for "locked forever, the creator earns the pool's trading fees". */
4
+ export const LOCK_FOREVER_FEES = 2n ** 64n - 1n;
3
5
  import { drandRoundAt, sealBid, unsealBid } from "./seal.js";
4
6
  const WAD = 10n ** 18n;
5
7
  const PHASES = ["commit", "reveal", "settle", "settled"];
@@ -88,7 +90,7 @@ export class Cloak {
88
90
  if (!this.factory)
89
91
  throw new Error("Cloak: factory address required");
90
92
  const w = this.wallet();
91
- const lock = p.lpLockUntil === undefined || p.lpLockUntil === "burn" ? 0 : p.lpLockUntil;
93
+ const lock = p.lpLockUntil === undefined || p.lpLockUntil === "burn" ? 0n : p.lpLockUntil === "fees" ? LOCK_FOREVER_FEES : BigInt(p.lpLockUntil);
92
94
  const params = {
93
95
  creator: p.creator ?? w.account.address,
94
96
  saleSupply: p.saleSupply,
@@ -100,7 +102,7 @@ export class Cloak {
100
102
  revealEnd: BigInt(p.revealEnd),
101
103
  lpBps: p.lpBps ?? 5000,
102
104
  noRevealPenaltyBps: p.noRevealPenaltyBps ?? 1000,
103
- lpLockUntil: BigInt(lock),
105
+ lpLockUntil: lock,
104
106
  tlockRound: BigInt(drandRoundAt(p.commitEnd)),
105
107
  };
106
108
  const txHash = await w.writeContract({
@@ -147,7 +149,7 @@ export class Cloak {
147
149
  async getAuction(auction) {
148
150
  const c = { address: auction, abi: cloakAuctionAbi };
149
151
  const r = this.publicClient;
150
- const [params, token, phase, commits, reveals, totalDeposits, totalDemand, settled, failed, clearingTick, clearingPrice, sold, raised, pair, launchFeeBps, bidCap, depositCap, creatorOwed, feesOwed, uniPair] = await Promise.all([
152
+ const [params, token, phase, commits, reveals, totalDeposits, totalDemand, settled, failed, clearingTick, clearingPrice, sold, raised, pair, launchFeeBps, bidCap, depositCap, creatorOwed, feesOwed, uniPair, creatorFees] = await Promise.all([
151
153
  r.readContract({ ...c, functionName: "params" }),
152
154
  r.readContract({ ...c, functionName: "token" }),
153
155
  r.readContract({ ...c, functionName: "phase" }),
@@ -170,6 +172,7 @@ export class Cloak {
170
172
  r.readContract({ ...c, functionName: "feesOwed" }).catch(() => 0n),
171
173
  // launches from the Uniswap factory on
172
174
  r.readContract({ ...c, functionName: "uniPair" }).catch(() => zeroAddress),
175
+ r.readContract({ ...c, functionName: "creatorFees" }).catch(() => false),
173
176
  ]);
174
177
  const [creator, saleSupply, lpReserve, minPrice, tickSize, numTicks, commitEnd, revealEnd, lpBps, noRevealPenaltyBps, lpLockUntil, tlockRound] = params;
175
178
  const [tokenName, tokenSymbol] = await Promise.all([
@@ -204,6 +207,7 @@ export class Cloak {
204
207
  raised,
205
208
  pair,
206
209
  uniPair,
210
+ creatorFees,
207
211
  tokenName,
208
212
  tokenSymbol,
209
213
  launchFeeBps,
@@ -213,6 +217,40 @@ export class Cloak {
213
217
  feesOwed,
214
218
  };
215
219
  }
220
+ /**
221
+ * The creator's trading fees for a launch whose LP is locked forever: what can be claimed now (in tokens and WHYPE),
222
+ * who receives it, and the locker position. `null` for launches without creator fees.
223
+ */
224
+ async creatorFees(auction) {
225
+ const c = { address: auction, abi: cloakAuctionAbi };
226
+ const on = await this.publicClient.readContract({ ...c, functionName: "creatorFees" }).catch(() => false);
227
+ if (!on)
228
+ return null;
229
+ const [locker, position, token, uniPair] = await Promise.all([
230
+ this.publicClient.readContract({ ...c, functionName: "feeLocker" }),
231
+ this.publicClient.readContract({ ...c, functionName: "feePosition" }),
232
+ this.publicClient.readContract({ ...c, functionName: "token" }),
233
+ this.publicClient.readContract({ ...c, functionName: "uniPair" }),
234
+ ]);
235
+ const l = { address: locker, abi: cloakFeeLockerAbi };
236
+ const [pos, claimableLp, [a0, a1], token0] = await Promise.all([
237
+ this.publicClient.readContract({ ...l, functionName: "positions", args: [position] }),
238
+ this.publicClient.readContract({ ...l, functionName: "claimable", args: [position] }),
239
+ this.publicClient.readContract({ ...l, functionName: "claimableAmounts", args: [position] }),
240
+ this.publicClient.readContract({ address: uniPair, abi: [{ type: "function", name: "token0", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }], functionName: "token0" }),
241
+ ]);
242
+ const tokenIs0 = token0.toLowerCase() === token.toLowerCase();
243
+ return { locker, position, beneficiary: pos.beneficiary, claimableLp, tokens: tokenIs0 ? a0 : a1, whype: tokenIs0 ? a1 : a0 };
244
+ }
245
+ /** Pay a launch's earned trading fees to its creator (anyone can send this; the fees always go to the creator). */
246
+ async claimCreatorFees(auction) {
247
+ const f = await this.creatorFees(auction);
248
+ if (!f)
249
+ throw new Error("this launch has no creator fees");
250
+ if (f.claimableLp === 0n)
251
+ throw new Error("no fees earned yet");
252
+ return this.send(await this.wallet().writeContract({ address: f.locker, abi: cloakFeeLockerAbi, functionName: "claim", args: [f.position] })).then((r) => r.transactionHash);
253
+ }
216
254
  /** Revealed demand per tick (empty until the reveal phase — that is the point). */
217
255
  async demandCurve(auction) {
218
256
  const s = await this.getAuction(auction);
@@ -32,12 +32,22 @@ export interface CloakDeployment {
32
32
  earlierFactories?: Address[];
33
33
  /** Block of this deployment's first factory: scan its launches' bids from here (defaults to `factoryBlock`). */
34
34
  firstFactoryBlock?: bigint;
35
+ /** CloakSeeder: opens each settled launch's market (a Uniswap V2 pool where the chain has one). */
36
+ seeder?: Address;
37
+ /** CloakFeeLocker: holds "locked forever" LP and pays its trading fees to the creator. */
38
+ feeLocker?: Address;
35
39
  router: Address;
36
40
  /** Block the router was deployed in: scan private-pool events from here. */
37
41
  routerBlock: bigint;
38
42
  batcher: Address;
39
43
  /** Moves balances from every earlier deployment into the permanent vaults. */
40
44
  migrator?: Address;
45
+ /** SharedVault: one private vault for every new Uniswap launch token (testnet experiment). */
46
+ shared?: Address;
47
+ /** Block the shared vault was deployed in: scan its notes from here. */
48
+ sharedBlock?: bigint;
49
+ /** SharedRouter: private buys, sells and hidden sends for the shared vault. */
50
+ sharedRouter?: Address;
41
51
  hypePool: Address;
42
52
  whype: Address;
43
53
  /** Hosted relayer (POST /relay, GET /quote). */
@@ -9,11 +9,18 @@ export const deployments = {
9
9
  config: "0x9fdf166f0671166148ea98cdd3ef0118d256167f",
10
10
  vaults: "0x24bc1d369aab3a2d741a48f8cac1af5dfd9f3d43",
11
11
  vaultsBlock: 19485039n,
12
- // the Uniswap launch factory (Nov 2): every launch opens a Uniswap V2 pool at its clearing price
13
- factory: "0xeac84944f5a8343edf49cde8ef74d71f4908a656",
14
- factoryBlock: 21221250n,
15
- earlierFactories: ["0xa88df63d7d44acc66052e5cbe5ba9503bf940993"],
12
+ // the creator-fee launch factory (Nov 2): every launch opens a Uniswap V2 pool at its clearing price, and its LP can
13
+ // be locked forever with the trading fees going to the creator
14
+ factory: "0xd179bad69715f4bd4796dd7a4103be143fba02ad",
15
+ factoryBlock: 21312271n,
16
+ earlierFactories: ["0xeac84944f5a8343edf49cde8ef74d71f4908a656", "0xa88df63d7d44acc66052e5cbe5ba9503bf940993"],
16
17
  firstFactoryBlock: 19484794n,
18
+ seeder: "0x633aa82b806dafc0ae4c84418d23f3726139a88d",
19
+ feeLocker: "0x3b74062a72b16f9a221d63515518a635414501d8",
20
+ // the shared launch-token vault (Nov 2, testnet experiment): new Uniswap launches keep their private balances here
21
+ shared: "0x0ce2ecb0195e07899377e3d7b01e927ab3f87610",
22
+ sharedBlock: 21426599n,
23
+ sharedRouter: "0xe2a0769a5acbd987c1b6725247c56187a112dc7e",
17
24
  router: "0x343ab9a7aac93cee28321410510ea24417cc6386",
18
25
  routerBlock: 19485063n,
19
26
  batcher: "0xc69e4bca18020ae93bc9f17093985bfb139a3203",
@@ -93,6 +100,9 @@ export async function liveDeployment(publicClient, base) {
93
100
  routerBlock: reg.routerBlock,
94
101
  batcher: reg.batcher ?? base.batcher,
95
102
  migrator: reg.migrator ?? base.migrator,
103
+ shared: reg.shared ?? base.shared,
104
+ sharedBlock: reg.sharedBlock ?? base.sharedBlock,
105
+ sharedRouter: reg.sharedRouter ?? base.sharedRouter,
96
106
  hypePool,
97
107
  whype,
98
108
  previous: reg.previous.length
@@ -1,5 +1,5 @@
1
1
  import { type Address, type Hex, type PublicClient } from "viem";
2
- import type { Wallet } from "../cloak.js";
2
+ import { type Wallet } from "../cloak.js";
3
3
  import { CloakKeys } from "./crypto.js";
4
4
  import { MerkleTree } from "./tree.js";
5
5
  import type { ProofArgs, Prover } from "./prover.js";
@@ -114,7 +114,11 @@ export declare class CloakPrivate {
114
114
  relayFee(kind: RelayKind, pool: Address): Promise<RelayQuote>;
115
115
  /** What a withdrawal of `amount` from `pool` delivers after the protocol's withdrawal fee. */
116
116
  afterFee(pool: Address, amount: bigint): Promise<bigint>;
117
- private changeOutput;
117
+ changeOutput(amount: bigint): Promise<{
118
+ amount: bigint;
119
+ pubKey: bigint;
120
+ memo: (b: bigint) => `0x${string}`;
121
+ }>;
118
122
  dummy(): Promise<{
119
123
  dummyPubKey: bigint;
120
124
  dummyMemo: (b: bigint) => `0x${string}`;
@@ -123,7 +127,7 @@ export declare class CloakPrivate {
123
127
  * Get 1–2 notes (from one tree epoch) covering `target`, merging notes privately first when the balance is spread
124
128
  * across many. Each merge is a relayed transaction and pays the relayer's fee from the merged notes.
125
129
  */
126
- private inputsFor;
130
+ inputsFor(pool: Address, target: bigint, onStatus: Status): Promise<OwnedNote[]>;
127
131
  /** Public HYPE → private balance. This one step is public (your wallet deposits); everything after is not. */
128
132
  shieldHype(wallet: Wallet, amount: bigint): Promise<Hex>;
129
133
  /**
@@ -140,6 +144,54 @@ export declare class CloakPrivate {
140
144
  txHash: Hex;
141
145
  tokens: bigint;
142
146
  }>;
147
+ /**
148
+ * One-time bid wallet `n` for `auction`. The private balance pays its deposit and a little gas through a relayed
149
+ * withdrawal, so on-chain the bid comes from a fresh address nobody can tie to this account's wallet.
150
+ */
151
+ bidWallet(auction: Address, n: number): {
152
+ n: number;
153
+ address: Address;
154
+ wallet: Wallet;
155
+ };
156
+ /** Gas a bid wallet keeps for its own transactions: the bid, a private claim, and moving what's left back in. */
157
+ bidGasReserve(): Promise<bigint>;
158
+ /** This account's bid wallets for `auction` that have been used (newest last), found from the seed alone. */
159
+ bidWallets(auction: Address): Promise<{
160
+ n: number;
161
+ address: Address;
162
+ wallet: Wallet;
163
+ }[]>;
164
+ /**
165
+ * Place a sealed bid paid from the private HYPE balance: a relayed private withdrawal funds a fresh one-time wallet
166
+ * with the deposit plus gas, and that wallet seals the bid. Keep the returned secret like any bid's (auto-reveal makes
167
+ * losing it harmless). After settlement, `finishPrivateBids` claims the tokens privately and moves the refund back.
168
+ */
169
+ bidPrivately(auction: Address, opts: {
170
+ maxPrice: bigint;
171
+ quantity: bigint;
172
+ deposit?: bigint;
173
+ }, onStatus?: Status): Promise<{
174
+ secret: import("../cloak.js").BidSecret;
175
+ bidWallet: `0x${string}`;
176
+ n: number;
177
+ fundTx: `0x${string}`;
178
+ }>;
179
+ /**
180
+ * After a launch settles: for every bid this account placed from its private balance, claim won tokens straight into
181
+ * the private vault (or, if a cap blocks that, to the bid wallet and then in), collect refunds, and move the bid
182
+ * wallet's HYPE back into the private balance. Before settlement it only moves back HYPE from a one-time wallet whose
183
+ * bid never went through. Safe to call again; it only does what's left.
184
+ */
185
+ finishPrivateBids(auction: Address, onStatus?: Status,
186
+ /** The shared launch-token vault, for launches whose token lives there (claimed to the bid wallet, then moved in). */
187
+ shared?: {
188
+ isAdmitted(token: Address): Promise<boolean>;
189
+ shield(wallet: Wallet, token: Address, amount: bigint): Promise<Hex>;
190
+ }): Promise<{
191
+ claimed: number;
192
+ movedHype: bigint;
193
+ movedTokens: bigint;
194
+ }>;
143
195
  roundInfo(): Promise<{
144
196
  round: bigint;
145
197
  endsAt: number;
@@ -1,5 +1,7 @@
1
- import { encodeAbiParameters, parseEventLogs, zeroAddress } from "viem";
2
- import { cloakAuctionAbi, cloakMigratorAbi, cloakPairAbi, privateBatcherAbi, privateRouterAbi, shieldedPoolAbi } from "../abis.js";
1
+ import { createWalletClient, custom, encodeAbiParameters, parseEventLogs, zeroAddress } from "viem";
2
+ import { privateKeyToAccount } from "viem/accounts";
3
+ import { cloakAuctionAbi, cloakConfigAbi, cloakMigratorAbi, cloakPairAbi, privateBatcherAbi, privateRouterAbi, shieldedPoolAbi } from "../abis.js";
4
+ import { Cloak } from "../cloak.js";
3
5
  import { ACCOUNT_MESSAGE, ACCOUNT_TYPED_DATA, ROUTER_ACCOUNT_TYPED_DATA, CloakKeys, MAIN_KEY, noteCommitment, encryptFullMemo, encryptRouterMemo, formatCloakAddress, nullifierOf, parseCloakAddress, randomField, randomKeyIndex, readMemo, } from "./crypto.js";
4
6
  import { MerkleTree } from "./tree.js";
5
7
  import { MOVE_DATA_ABI, ORDER_DATA_ABI, SWAP_DATA_ABI, buildTransaction, selectNotes, splitStandard } from "./tx.js";
@@ -406,6 +408,134 @@ export class CloakPrivate {
406
408
  throw new Error("private claim reverted");
407
409
  return { txHash: hash, tokens };
408
410
  }
411
+ // ───────────── sealed bids paid from the private balance ─────────────
412
+ /**
413
+ * One-time bid wallet `n` for `auction`. The private balance pays its deposit and a little gas through a relayed
414
+ * withdrawal, so on-chain the bid comes from a fresh address nobody can tie to this account's wallet.
415
+ */
416
+ bidWallet(auction, n) {
417
+ const account = privateKeyToAccount(this.keys.bidWalletKey(auction, n));
418
+ const chain = this.publicClient.chain;
419
+ if (!chain)
420
+ throw new Error("the public client needs a chain");
421
+ const wallet = createWalletClient({ account, chain, transport: custom({ request: (a) => this.publicClient.request(a) }) });
422
+ return { n, address: account.address, wallet };
423
+ }
424
+ /** Gas a bid wallet keeps for its own transactions: the bid, a private claim, and moving what's left back in. */
425
+ async bidGasReserve() {
426
+ const gp = await this.publicClient.getGasPrice();
427
+ return (gp < 100000000n ? 100000000n : gp) * 6000000n;
428
+ }
429
+ /** This account's bid wallets for `auction` that have been used (newest last), found from the seed alone. */
430
+ async bidWallets(auction) {
431
+ const out = [];
432
+ for (let n = 0; n < 64; n++) {
433
+ const w = this.bidWallet(auction, n);
434
+ const [nonce, bal] = await Promise.all([this.publicClient.getTransactionCount({ address: w.address }), this.publicClient.getBalance({ address: w.address })]);
435
+ if (nonce === 0 && bal === 0n)
436
+ break;
437
+ out.push(w);
438
+ }
439
+ return out;
440
+ }
441
+ /**
442
+ * Place a sealed bid paid from the private HYPE balance: a relayed private withdrawal funds a fresh one-time wallet
443
+ * with the deposit plus gas, and that wallet seals the bid. Keep the returned secret like any bid's (auto-reveal makes
444
+ * losing it harmless). After settlement, `finishPrivateBids` claims the tokens privately and moves the refund back.
445
+ */
446
+ async bidPrivately(auction, opts, onStatus = () => { }) {
447
+ const reader = new Cloak(this.publicClient);
448
+ const s = await reader.getAuction(auction);
449
+ if (s.phase !== "commit")
450
+ throw new Error("commit phase is over");
451
+ const q = reader.quoteBid(s, opts.maxPrice, opts.quantity);
452
+ const deposit = opts.deposit ?? q.deposit;
453
+ if (deposit < q.cost)
454
+ throw new Error("deposit is below the bid's cost");
455
+ const used = await this.bidWallets(auction);
456
+ const bw = this.bidWallet(auction, used.length);
457
+ const gas = await this.bidGasReserve();
458
+ // the vault keeps its withdrawal fee from what arrives, so send enough that deposit + gas land in full
459
+ const config = await this.publicClient.readContract({ address: this.router, abi: privateRouterAbi, functionName: "config" });
460
+ const feeBps = BigInt(await this.publicClient.readContract({ address: config, abi: cloakConfigAbi, functionName: "withdrawFeeBps" }));
461
+ const want = deposit + gas;
462
+ const send = (want * 10000n + (10000n - feeBps) - 1n) / (10000n - feeBps);
463
+ onStatus("Paying the deposit from your private balance…");
464
+ const fund = await this.unshieldHype(bw.address, send, onStatus);
465
+ const r = await this.publicClient.waitForTransactionReceipt({ hash: fund });
466
+ if (r.status !== "success")
467
+ throw new Error("funding the bid wallet reverted");
468
+ for (let i = 0; i < 30 && (await this.publicClient.getBalance({ address: bw.address })) < want; i++)
469
+ await new Promise((x) => setTimeout(x, 1000));
470
+ if ((await this.publicClient.getBalance({ address: bw.address })) < deposit)
471
+ throw new Error("the bid wallet wasn't funded; nothing was bid, and the HYPE is in the one-time wallet (finishPrivateBids moves it back)");
472
+ onStatus("Sealing the bid from a one-time wallet…");
473
+ const secret = await new Cloak(this.publicClient, bw.wallet).bid(auction, { maxPrice: opts.maxPrice, quantity: opts.quantity, deposit });
474
+ return { secret, bidWallet: bw.address, n: bw.n, fundTx: fund };
475
+ }
476
+ /**
477
+ * After a launch settles: for every bid this account placed from its private balance, claim won tokens straight into
478
+ * the private vault (or, if a cap blocks that, to the bid wallet and then in), collect refunds, and move the bid
479
+ * wallet's HYPE back into the private balance. Before settlement it only moves back HYPE from a one-time wallet whose
480
+ * bid never went through. Safe to call again; it only does what's left.
481
+ */
482
+ async finishPrivateBids(auction, onStatus = () => { },
483
+ /** The shared launch-token vault, for launches whose token lives there (claimed to the bid wallet, then moved in). */
484
+ shared) {
485
+ const reader = new Cloak(this.publicClient);
486
+ const s = await reader.getAuction(auction);
487
+ let claimed = 0;
488
+ let movedHype = 0n;
489
+ let movedTokens = 0n;
490
+ const vaultOpen = !s.failed && this.markets.some((m) => m.token.toLowerCase() === s.token.toLowerCase());
491
+ const inShared = !s.failed && !vaultOpen && !!shared && (await shared.isAdmitted(s.token));
492
+ for (const bw of await this.bidWallets(auction)) {
493
+ const c = new Cloak(this.publicClient, bw.wallet);
494
+ const bids = await reader.bidsOf(auction, bw.address);
495
+ // before settlement only a wallet whose bid never went through has anything to move back
496
+ if (!s.settled && bids.length > 0)
497
+ continue;
498
+ for (const b of bids) {
499
+ if (b.claimed)
500
+ continue;
501
+ onStatus(`Claiming bid #${b.id}…`);
502
+ if (b.tokens > 0n && vaultOpen) {
503
+ try {
504
+ await this.claimPrivately(bw.wallet, auction, b.id);
505
+ claimed++;
506
+ continue;
507
+ }
508
+ catch { } // over the vault's deposit cap: claim to the bid wallet instead
509
+ }
510
+ await c.claim(auction, [b.id]);
511
+ claimed++;
512
+ }
513
+ const owed = await this.publicClient.readContract({ address: auction, abi: cloakAuctionAbi, functionName: "refundOwed", args: [bw.address] }).catch(() => 0n);
514
+ if (owed > 0n) {
515
+ const h = await bw.wallet.writeContract({ address: auction, abi: cloakAuctionAbi, functionName: "withdrawRefund", args: [bw.address] });
516
+ await this.publicClient.waitForTransactionReceipt({ hash: h });
517
+ }
518
+ if (vaultOpen || inShared) {
519
+ const t = await this.publicClient.readContract({ address: s.token, abi: ERC20_ABI, functionName: "balanceOf", args: [bw.address] });
520
+ if (t > 0n) {
521
+ onStatus("Moving tokens into your private balance…");
522
+ try {
523
+ await (inShared ? shared.shield(bw.wallet, s.token, t) : this.shieldToken(bw.wallet, s.token, t));
524
+ movedTokens += t;
525
+ }
526
+ catch { } // a cap may block it; the tokens stay in the one-time wallet, which nobody can tie to you
527
+ }
528
+ }
529
+ const bal = await this.publicClient.getBalance({ address: bw.address });
530
+ const keep = (await this.bidGasReserve()) / 3n; // gas for this last shield
531
+ if (bal > keep * 2n) {
532
+ onStatus("Moving the refund back into your private balance…");
533
+ await this.shieldHype(bw.wallet, bal - keep);
534
+ movedHype += bal - keep;
535
+ }
536
+ }
537
+ return { claimed, movedHype, movedTokens };
538
+ }
409
539
  // ───────────── batch auctions (hide trade size) ─────────────
410
540
  async roundInfo() {
411
541
  if (!this.batcher)
@@ -70,11 +70,19 @@ export declare class CloakKeys {
70
70
  static fromSignature(sig: Hex): CloakKeys;
71
71
  key(index: number): Promise<SpendKey>;
72
72
  main(): Promise<SpendKey>;
73
+ /**
74
+ * The private key of one-time bid wallet `n` for `auction`: a fresh address that bids from the private balance, so the
75
+ * bid can't be linked to the account's wallet. Derived from this account's seed, so the same signature always finds
76
+ * it again (on any device).
77
+ */
78
+ bidWalletKey(auction: Hex, n: number): Hex;
73
79
  }
74
80
  export interface Note {
75
81
  amount: bigint;
76
82
  pubKey: bigint;
77
83
  blinding: bigint;
84
+ /** Shared vault only: the token's address as a number. Notes of single-token vaults have none. */
85
+ asset?: bigint;
78
86
  }
79
87
  export declare const noteCommitment: (n: Note) => Promise<bigint>;
80
88
  export declare function nullifierOf(n: Note, leafIndex: number, privKey: bigint): Promise<bigint>;
@@ -77,8 +77,16 @@ export class CloakKeys {
77
77
  main() {
78
78
  return this.key(MAIN_KEY);
79
79
  }
80
+ /**
81
+ * The private key of one-time bid wallet `n` for `auction`: a fresh address that bids from the private balance, so the
82
+ * bid can't be linked to the account's wallet. Derived from this account's seed, so the same signature always finds
83
+ * it again (on any device).
84
+ */
85
+ bidWalletKey(auction, n) {
86
+ return keccak256(concat([toBytes("cloak-bidwallet-v1"), this.seed, toBytes(auction.toLowerCase()), toBytes(toHex(n, { size: 4 }))]));
87
+ }
80
88
  }
81
- export const noteCommitment = (n) => poseidon([n.amount, n.pubKey, n.blinding]);
89
+ export const noteCommitment = (n) => n.asset === undefined ? poseidon([n.amount, n.pubKey, n.blinding]) : poseidon([n.amount, n.pubKey, n.blinding, n.asset]);
82
90
  export async function nullifierOf(n, leafIndex, privKey) {
83
91
  const c = await noteCommitment(n);
84
92
  const sig = await poseidon([privKey, c, BigInt(leafIndex)]);
@@ -3,3 +3,4 @@ export * from "./tree.js";
3
3
  export * from "./prover.js";
4
4
  export * from "./tx.js";
5
5
  export * from "./client.js";
6
+ export * from "./shared.js";
@@ -3,3 +3,4 @@ export * from "./tree.js";
3
3
  export * from "./prover.js";
4
4
  export * from "./tx.js";
5
5
  export * from "./client.js";
6
+ export * from "./shared.js";
@@ -7,10 +7,14 @@ export interface ProofArgs {
7
7
  extDataHash: bigint;
8
8
  inputNullifiers: [bigint, bigint];
9
9
  outputCommitments: [bigint, bigint];
10
+ /** Shared-vault proofs only: the token named publicly, or 0 for a send that hides it. */
11
+ publicAsset?: bigint;
10
12
  }
11
13
  export type Prover = (input: Record<string, unknown>) => Promise<ProofArgs>;
12
14
  /** Groth16 prover for the 2-in/2-out circuit (`transaction2x2`). Runs on the user's device. */
13
15
  export declare function createProver(wasm: Uint8Array, zkey: Uint8Array): Prover;
16
+ /** Groth16 prover for the shared launch-token vault (`transactionMA2x2`, 8 public signals). */
17
+ export declare function createSharedProver(wasm: Uint8Array, zkey: Uint8Array): Prover;
14
18
  /** Hosted circuit artifacts. */
15
19
  export declare const CIRCUITS_URL = "https://cloak.boblabs.tech/circuits";
16
20
  /** SHA-256 of the artifacts this SDK version was built and tested against. */
@@ -29,3 +33,18 @@ export declare function loadProver(baseUrl?: string, opts?: {
29
33
  zkey: string;
30
34
  } | null;
31
35
  }): Promise<Prover>;
36
+ /** SHA-256 of the shared-vault circuit artifacts this SDK version was built and tested against. */
37
+ export declare const SHARED_CIRCUIT_HASHES: {
38
+ readonly wasm: "55712f66e67fd9345daac5c881c2e73c546864dbd1e23481463b603b47aa53ed";
39
+ readonly zkey: "86ecee336f022ac1d2dbcc4db7e85ef838aee34c4d75fe8bc7446db86f3b97b3";
40
+ };
41
+ /**
42
+ * Fetch the shared launch-token vault's circuit (`transactionMA2x2`, 15 MB, once) and build its prover. Checked against
43
+ * `SHARED_CIRCUIT_HASHES` like `loadProver`; pass `hashes: null` to skip that when serving your own build.
44
+ */
45
+ export declare function loadSharedProver(baseUrl?: string, opts?: {
46
+ hashes?: {
47
+ wasm: string;
48
+ zkey: string;
49
+ } | null;
50
+ }): Promise<Prover>;