@cloakely/sdk 0.3.1 → 0.5.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
@@ -53,7 +53,10 @@ export interface AuctionState {
53
53
  clearingPrice: bigint;
54
54
  sold: bigint;
55
55
  raised: bigint;
56
+ /** The market Cloak trades: a CloakV2Market in front of a Uniswap V2 pool, or (earlier launches) a CloakPair. */
56
57
  pair: Address;
58
+ /** The launch's Uniswap V2 pool (token / WHYPE), tradable and tracked everywhere. Zero for a CloakPair market. */
59
+ uniPair: Address;
57
60
  tokenName: string;
58
61
  tokenSymbol: string;
59
62
  /** Launch fee on a successful raise, fixed when the auction was created. */
@@ -117,6 +120,7 @@ export declare class Cloak {
117
120
  creatorAlloc: bigint;
118
121
  vesting: Address;
119
122
  factory: Address;
123
+ index: bigint;
120
124
  legacy?: boolean;
121
125
  }[]>;
122
126
  private listLegacy;
@@ -217,8 +221,15 @@ export declare class Cloak {
217
221
  readonly name: "config_";
218
222
  readonly type: "address";
219
223
  readonly internalType: "contract CloakConfig";
224
+ }, {
225
+ readonly name: "seeder_";
226
+ readonly type: "address";
227
+ readonly internalType: "contract IMarketSeeder";
220
228
  }];
221
229
  readonly stateMutability: "nonpayable";
230
+ }, {
231
+ readonly type: "receive";
232
+ readonly stateMutability: "payable";
222
233
  }, {
223
234
  readonly type: "function";
224
235
  readonly name: "DEAD";
@@ -341,6 +352,28 @@ export declare class Cloak {
341
352
  }];
342
353
  readonly outputs: readonly [];
343
354
  readonly stateMutability: "nonpayable";
355
+ }, {
356
+ readonly type: "function";
357
+ readonly name: "claimPrivate";
358
+ readonly inputs: readonly [{
359
+ readonly name: "id";
360
+ readonly type: "uint256";
361
+ readonly internalType: "uint256";
362
+ }, {
363
+ readonly name: "pubKey";
364
+ readonly type: "uint256";
365
+ readonly internalType: "uint256";
366
+ }, {
367
+ readonly name: "blinding";
368
+ readonly type: "uint256";
369
+ readonly internalType: "uint256";
370
+ }, {
371
+ readonly name: "memo";
372
+ readonly type: "bytes";
373
+ readonly internalType: "bytes";
374
+ }];
375
+ readonly outputs: readonly [];
376
+ readonly stateMutability: "nonpayable";
344
377
  }, {
345
378
  readonly type: "function";
346
379
  readonly name: "claimedCount";
@@ -807,6 +840,16 @@ export declare class Cloak {
807
840
  readonly internalType: "uint256";
808
841
  }];
809
842
  readonly stateMutability: "view";
843
+ }, {
844
+ readonly type: "function";
845
+ readonly name: "seeder";
846
+ readonly inputs: readonly [];
847
+ readonly outputs: readonly [{
848
+ readonly name: "";
849
+ readonly type: "address";
850
+ readonly internalType: "contract IMarketSeeder";
851
+ }];
852
+ readonly stateMutability: "view";
810
853
  }, {
811
854
  readonly type: "function";
812
855
  readonly name: "settle";
@@ -869,6 +912,16 @@ export declare class Cloak {
869
912
  readonly internalType: "uint256";
870
913
  }];
871
914
  readonly stateMutability: "view";
915
+ }, {
916
+ readonly type: "function";
917
+ readonly name: "uniPair";
918
+ readonly inputs: readonly [];
919
+ readonly outputs: readonly [{
920
+ readonly name: "";
921
+ readonly type: "address";
922
+ readonly internalType: "address";
923
+ }];
924
+ readonly stateMutability: "view";
872
925
  }, {
873
926
  readonly type: "function";
874
927
  readonly name: "withdrawProceeds";
@@ -915,6 +968,16 @@ export declare class Cloak {
915
968
  readonly internalType: "uint256";
916
969
  }];
917
970
  readonly anonymous: false;
971
+ }, {
972
+ readonly type: "event";
973
+ readonly name: "ClaimedPrivately";
974
+ readonly inputs: readonly [{
975
+ readonly name: "id";
976
+ readonly type: "uint256";
977
+ readonly indexed: true;
978
+ readonly internalType: "uint256";
979
+ }];
980
+ readonly anonymous: false;
918
981
  }, {
919
982
  readonly type: "event";
920
983
  readonly name: "Committed";
@@ -1150,6 +1213,26 @@ export declare class Cloak {
1150
1213
  readonly internalType: "uint256";
1151
1214
  }];
1152
1215
  readonly anonymous: false;
1216
+ }, {
1217
+ readonly type: "event";
1218
+ readonly name: "UniswapMarket";
1219
+ readonly inputs: readonly [{
1220
+ readonly name: "uniPair";
1221
+ readonly type: "address";
1222
+ readonly indexed: true;
1223
+ readonly internalType: "address";
1224
+ }, {
1225
+ readonly name: "market";
1226
+ readonly type: "address";
1227
+ readonly indexed: false;
1228
+ readonly internalType: "address";
1229
+ }, {
1230
+ readonly name: "lpShares";
1231
+ readonly type: "uint256";
1232
+ readonly indexed: false;
1233
+ readonly internalType: "uint256";
1234
+ }];
1235
+ readonly anonymous: false;
1153
1236
  }], "Committed", true, bigint, bigint>>;
1154
1237
  /**
1155
1238
  * Decrypt every sealed bid whose drand round has passed and reveal them in batches. Anyone can run this — it needs no
package/dist/cloak.js CHANGED
@@ -120,47 +120,34 @@ export class Cloak {
120
120
  async listLaunches() {
121
121
  if (!this.factory)
122
122
  throw new Error("Cloak: factory address required");
123
- const legacy = [];
124
- for (const f of this.legacyFactories)
125
- legacy.push(...(await this.listLegacy(f)));
126
- return [...(await this.listCurrent()), ...legacy];
123
+ // every factory, and every launch in it, read at once (a multicall client folds them into a few requests)
124
+ const [current, ...legacy] = await Promise.all([this.listCurrent(), ...this.legacyFactories.map((f) => this.listLegacy(f))]);
125
+ return [...current, ...legacy.flat()];
127
126
  }
128
127
  async listLegacy(factory) {
129
128
  const n = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launchesCount" });
130
- const out = [];
131
- for (let i = 0n; i < n; i++) {
129
+ return Promise.all(Array.from({ length: Number(n) }, async (_, k) => {
130
+ const i = BigInt(k);
132
131
  // a factory from v2 on returns a vesting contract too; the first one returns five fields
133
132
  const six = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launches", args: [i] }).catch(() => null);
134
133
  const [auction, token, creator, launchpad, creatorAlloc] = six ?? (await this.publicClient.readContract({ address: factory, abi: LEGACY_LAUNCHES_ABI, functionName: "launches", args: [i] }));
135
- out.push({ auction, token, creator, launchpad, creatorAlloc, vesting: (six?.[5] ?? zeroAddress), factory, legacy: true });
136
- }
137
- return out;
134
+ return { auction, token, creator, launchpad, creatorAlloc, vesting: (six?.[5] ?? zeroAddress), factory, index: i, legacy: true };
135
+ }));
138
136
  }
139
137
  async listCurrent() {
140
- if (!this.factory)
141
- throw new Error("Cloak: factory address required");
142
- const n = await this.publicClient.readContract({
143
- address: this.factory,
144
- abi: cloakFactoryAbi,
145
- functionName: "launchesCount",
146
- });
147
- const out = [];
148
- for (let i = 0n; i < n; i++) {
149
- const [auction, token, creator, launchpad, creatorAlloc, vesting] = await this.publicClient.readContract({
150
- address: this.factory,
151
- abi: cloakFactoryAbi,
152
- functionName: "launches",
153
- args: [i],
154
- });
155
- out.push({ auction, token, creator, launchpad, creatorAlloc, vesting, factory: this.factory });
156
- }
157
- return out;
138
+ const factory = this.factory;
139
+ const n = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launchesCount" });
140
+ return Promise.all(Array.from({ length: Number(n) }, async (_, k) => {
141
+ const index = BigInt(k);
142
+ const [auction, token, creator, launchpad, creatorAlloc, vesting] = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launches", args: [index] });
143
+ return { auction, token, creator, launchpad, creatorAlloc, vesting, factory, index };
144
+ }));
158
145
  }
159
146
  // ───────────── reads ─────────────
160
147
  async getAuction(auction) {
161
148
  const c = { address: auction, abi: cloakAuctionAbi };
162
149
  const r = this.publicClient;
163
- const [params, token, phase, commits, reveals, totalDeposits, totalDemand, settled, failed, clearingTick, clearingPrice, sold, raised, pair, launchFeeBps, bidCap, depositCap, creatorOwed, feesOwed] = await Promise.all([
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([
164
151
  r.readContract({ ...c, functionName: "params" }),
165
152
  r.readContract({ ...c, functionName: "token" }),
166
153
  r.readContract({ ...c, functionName: "phase" }),
@@ -181,6 +168,8 @@ export class Cloak {
181
168
  r.readContract({ ...c, functionName: "depositCap" }).catch(() => 0n),
182
169
  r.readContract({ ...c, functionName: "creatorOwed" }).catch(() => 0n),
183
170
  r.readContract({ ...c, functionName: "feesOwed" }).catch(() => 0n),
171
+ // launches from the Uniswap factory on
172
+ r.readContract({ ...c, functionName: "uniPair" }).catch(() => zeroAddress),
184
173
  ]);
185
174
  const [creator, saleSupply, lpReserve, minPrice, tickSize, numTicks, commitEnd, revealEnd, lpBps, noRevealPenaltyBps, lpLockUntil, tlockRound] = params;
186
175
  const [tokenName, tokenSymbol] = await Promise.all([
@@ -214,6 +203,7 @@ export class Cloak {
214
203
  sold,
215
204
  raised,
216
205
  pair,
206
+ uniPair,
217
207
  tokenName,
218
208
  tokenSymbol,
219
209
  launchFeeBps,
@@ -1,28 +1,42 @@
1
- import type { Address } from "viem";
2
- /** An earlier deployment on the same chain: its launches still list, and `migrator` moves its private balances here. */
1
+ import type { Address, PublicClient } from "viem";
2
+ /** An earlier deployment on the same chain: its launches still list, and the migrator moves its private balances here. */
3
3
  export interface PreviousDeployment {
4
4
  version: number;
5
5
  factory: Address;
6
6
  router: Address;
7
7
  routerBlock: bigint;
8
8
  batcher?: Address;
9
- /** Moves this deployment's private balances into the current one (`CloakPrivate.migrateFrom(…, { migrator })`). */
9
+ /** Moved its balances into v3 (v1 and v2 only; from v4 on, the current deployment's `migrator` moves every version). */
10
10
  migrator?: Address;
11
+ /** Standalone vaults outside its router (v2's USDC vault), moved too. */
12
+ pools?: {
13
+ pool: Address;
14
+ fromBlock: bigint;
15
+ }[];
11
16
  }
12
17
  /** Where Cloak lives on each chain, so integrators never copy addresses by hand. */
13
18
  export interface CloakDeployment {
14
19
  chainId: number;
15
- /** Which Cloak deployment this is (3 on Elysium testnet). */
20
+ /** Which Cloak deployment this is (4 on Elysium testnet). */
16
21
  version: number;
22
+ /** CloakConfig: the rules and the registry of every Cloak contract. `liveDeployment` reads the current ones from it. */
17
23
  config: Address;
24
+ /** CloakVaults: the permanent vaults (v4 on). */
25
+ vaults?: Address;
26
+ /** First block of the permanent vaults: scan private notes from here, whichever router is current. */
27
+ vaultsBlock?: bigint;
18
28
  factory: Address;
19
29
  /** Block the factory was deployed in: scan launch events from here. */
20
30
  factoryBlock: bigint;
31
+ /** Earlier factories of this same deployment (newest first): their launches still list, settle and trade. */
32
+ earlierFactories?: Address[];
33
+ /** Block of this deployment's first factory: scan its launches' bids from here (defaults to `factoryBlock`). */
34
+ firstFactoryBlock?: bigint;
21
35
  router: Address;
22
36
  /** Block the router was deployed in: scan private-pool events from here. */
23
37
  routerBlock: bigint;
24
38
  batcher: Address;
25
- /** The migrator from the newest earlier deployment (each one in `previous` has its own). */
39
+ /** Moves balances from every earlier deployment into the permanent vaults. */
26
40
  migrator?: Address;
27
41
  hypePool: Address;
28
42
  whype: Address;
@@ -42,5 +56,12 @@ export interface CloakDeployment {
42
56
  previous?: PreviousDeployment[];
43
57
  }
44
58
  export declare const deployments: Record<number, CloakDeployment>;
59
+ /** Every factory before `d.factory` whose launches should still list, newest first: this deployment's, then earlier ones'. */
60
+ export declare function legacyFactories(d: CloakDeployment): Address[];
45
61
  /** The Cloak deployment on `chainId`, or an error naming the chains Cloak is on. */
46
62
  export declare function deploymentFor(chainId: number): CloakDeployment;
63
+ /**
64
+ * `base` with the current factory, router, batcher, migrator and vaults read from CloakConfig's registry, so a router or
65
+ * batcher added on-chain is picked up without a new SDK. Earlier deployments keep what `base` knows about them.
66
+ */
67
+ export declare function liveDeployment(publicClient: PublicClient, base: CloakDeployment): Promise<CloakDeployment>;
@@ -1,31 +1,51 @@
1
+ import { privateRouterAbi } from "./abis.js";
2
+ import { readRegistry } from "./registry.js";
1
3
  export const deployments = {
2
- // Cloak v3 (Oct 30): deployments/elysium-testnet-v3.json
4
+ // Cloak v4 (Oct 31), the permanent vaults, plus the Uniswap launch factory (Nov 2): deployments/elysium-testnet-v4.json. CloakConfig's registry lists all of it;
5
+ // liveDeployment() reads the current contracts from there.
3
6
  99801: {
4
7
  chainId: 99801,
5
- version: 3,
6
- config: "0x9d30c7b23fa2d5c40779d3db3bb4292e18c94373",
7
- factory: "0xd0f4aacf339f07211a9155194925b2f94e250e29",
8
- factoryBlock: 19096456n,
9
- router: "0x78319bee7c21ca411166acc67c750aca10280e86",
10
- routerBlock: 19096485n,
11
- batcher: "0x9902d86c5be81f6c6ce059acf4c89193d06069c4",
12
- migrator: "0x295f816aa6dac1901bd793120248d8f9cbbd5eec",
13
- hypePool: "0x786886a61Dd77581ef03787c5939fC44B004064d",
8
+ version: 4,
9
+ config: "0x9fdf166f0671166148ea98cdd3ef0118d256167f",
10
+ vaults: "0x24bc1d369aab3a2d741a48f8cac1af5dfd9f3d43",
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"],
16
+ firstFactoryBlock: 19484794n,
17
+ router: "0x343ab9a7aac93cee28321410510ea24417cc6386",
18
+ routerBlock: 19485063n,
19
+ batcher: "0xc69e4bca18020ae93bc9f17093985bfb139a3203",
20
+ migrator: "0x86c5d8035224493f10938aaa07b179be316dadf4",
21
+ hypePool: "0x3ACB22829bb422446caaDd8546e65ab530A53031",
14
22
  whype: "0xe44b27113D4b947c0B1bDf4a93878D0e115f4bdD",
15
23
  relayerUrl: "https://cloak-keeper.onrender.com",
16
24
  circuitsUrl: "https://cloak.boblabs.tech/circuits",
17
25
  assets: [
18
26
  // Elysium testnet USDC (bridged from Circle's USDC on HyperEVM): private sends and withdrawals
19
- { symbol: "USDC", token: "0x7D29d8047B905000459c0E80c34a26CeedcB47b2", pool: "0x61de30970e3c53665ce5a89323284e7ab379fa08", decimals: 6, fromBlock: 18329466n },
27
+ { symbol: "USDC", token: "0x7D29d8047B905000459c0E80c34a26CeedcB47b2", pool: "0x3d5cB722490D9908a118f8B3151f83fcE943E4F0", decimals: 6, fromBlock: 19485039n },
20
28
  ],
21
29
  previous: [
30
+ {
31
+ version: 3,
32
+ factory: "0xd0f4aacf339f07211a9155194925b2f94e250e29",
33
+ router: "0x78319bee7c21ca411166acc67c750aca10280e86",
34
+ routerBlock: 19096485n,
35
+ batcher: "0x9902d86c5be81f6c6ce059acf4c89193d06069c4",
36
+ pools: [
37
+ { pool: "0x61de30970e3c53665ce5a89323284e7ab379fa08", fromBlock: 18329466n },
38
+ ],
39
+ },
22
40
  {
23
41
  version: 2,
24
42
  factory: "0xc1fc14e84cbbd10f75dad9d91bf50f47c48757fc",
25
43
  router: "0xd51e731467c49991496329c2b7979cb1aa220ee2",
26
44
  routerBlock: 15856031n,
27
45
  batcher: "0xa13ffd763a8cccfea1b868c616291723579ddb77",
28
- migrator: "0x295f816aa6dac1901bd793120248d8f9cbbd5eec",
46
+ pools: [
47
+ { pool: "0x61de30970e3c53665ce5a89323284e7ab379fa08", fromBlock: 18329466n },
48
+ ],
29
49
  },
30
50
  {
31
51
  version: 1,
@@ -33,11 +53,14 @@ export const deployments = {
33
53
  router: "0xfa2574cfc3afb8f7d3a0651fa2111ff4e3d2fc41",
34
54
  routerBlock: 10973723n,
35
55
  batcher: "0xf6582cc5a84fccf74e571e652fb7b603a2c25812",
36
- migrator: "0x5f3d82a6aa1b20ba9d0f58a98d1c4933c115a0da",
37
56
  },
38
57
  ],
39
58
  },
40
59
  };
60
+ /** Every factory before `d.factory` whose launches should still list, newest first: this deployment's, then earlier ones'. */
61
+ export function legacyFactories(d) {
62
+ return [...(d.earlierFactories ?? []), ...(d.previous ?? []).map((p) => p.factory)].filter((f) => f && !/^0x0{40}$/i.test(f));
63
+ }
41
64
  /** The Cloak deployment on `chainId`, or an error naming the chains Cloak is on. */
42
65
  export function deploymentFor(chainId) {
43
66
  const d = deployments[chainId];
@@ -45,3 +68,46 @@ export function deploymentFor(chainId) {
45
68
  throw new Error(`Cloak isn't deployed on chain ${chainId} (available: ${Object.keys(deployments).join(", ")})`);
46
69
  return d;
47
70
  }
71
+ /**
72
+ * `base` with the current factory, router, batcher, migrator and vaults read from CloakConfig's registry, so a router or
73
+ * batcher added on-chain is picked up without a new SDK. Earlier deployments keep what `base` knows about them.
74
+ */
75
+ export async function liveDeployment(publicClient, base) {
76
+ const reg = await readRegistry(publicClient, base.config).catch(() => undefined);
77
+ if (!reg)
78
+ return base;
79
+ const [hypePool, whype] = await Promise.all([
80
+ publicClient.readContract({ address: reg.router, abi: privateRouterAbi, functionName: "hypePool" }),
81
+ publicClient.readContract({ address: reg.router, abi: privateRouterAbi, functionName: "whype" }),
82
+ ]);
83
+ const known = new Map((base.previous ?? []).map((p) => [p.version, p]));
84
+ return {
85
+ ...base,
86
+ version: reg.version,
87
+ vaults: reg.vaults ?? base.vaults,
88
+ vaultsBlock: reg.vaultsBlock ?? base.vaultsBlock,
89
+ factory: reg.factory,
90
+ factoryBlock: reg.factoryBlock,
91
+ earlierFactories: reg.earlierFactories.length ? reg.earlierFactories : base.earlierFactories,
92
+ router: reg.router,
93
+ routerBlock: reg.routerBlock,
94
+ batcher: reg.batcher ?? base.batcher,
95
+ migrator: reg.migrator ?? base.migrator,
96
+ hypePool,
97
+ whype,
98
+ previous: reg.previous.length
99
+ ? reg.previous.map((p) => {
100
+ const k = known.get(p.version);
101
+ return {
102
+ version: p.version,
103
+ factory: p.factory ?? k?.factory ?? ("0x" + "0".repeat(40)),
104
+ router: p.router ?? k?.router ?? ("0x" + "0".repeat(40)),
105
+ routerBlock: p.routerBlock ?? k?.routerBlock ?? 0n,
106
+ batcher: p.batcher ?? k?.batcher,
107
+ migrator: k?.migrator,
108
+ pools: p.pools.length ? p.pools.map((x) => ({ pool: x.addr, fromBlock: x.startBlock })) : k?.pools,
109
+ };
110
+ })
111
+ : base.previous,
112
+ };
113
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./abis.js";
2
2
  export * from "./chains.js";
3
3
  export * from "./deployments.js";
4
+ export * from "./registry.js";
4
5
  export * from "./seal.js";
5
6
  export * from "./cloak.js";
6
7
  export * from "./private/index.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./abis.js";
2
2
  export * from "./chains.js";
3
3
  export * from "./deployments.js";
4
+ export * from "./registry.js";
4
5
  export * from "./seal.js";
5
6
  export * from "./cloak.js";
6
7
  export * from "./private/index.js";
@@ -70,17 +70,32 @@ export declare class CloakPrivate {
70
70
  })[];
71
71
  constructor(publicClient: PublicClient, router: Address, keys: CloakKeys, prover: () => Promise<Prover>, submit: Submitter, fromBlock?: bigint, batcher?: Address | undefined, migrator?: Address | undefined);
72
72
  /**
73
- * Derive the private keys from one EIP-712 signature bound to this chain and router, so a signature asked for by any
74
- * other site or contract can't reproduce them. With `verify`, the wallet signs twice and the keys are refused if the
75
- * two signatures differ (some smart and MPC wallets sign differently each time, which would hide earlier notes).
73
+ * Derive the wallet's Cloak private account from one EIP-712 signature bound to this chain (never to a contract, so
74
+ * the account survives every upgrade). With `verify`, the wallet signs twice and the keys are refused if the two
75
+ * signatures differ (some smart and MPC wallets sign differently each time, which would hide earlier notes).
76
76
  */
77
77
  static keysFromWallet(wallet: Wallet, opts: {
78
78
  chainId: number;
79
- router: Address;
80
79
  verify?: boolean;
81
80
  }): Promise<CloakKeys>;
81
+ /** Keys of a v2 or v3 account, bound to that deployment's router. Only needed to move an old balance across. */
82
+ static routerKeysFromWallet(wallet: Wallet, opts: {
83
+ chainId: number;
84
+ router: Address;
85
+ }): Promise<CloakKeys>;
86
+ /** The keys an earlier deployment's notes were made for: v1 a plain message, v2 and v3 their router, v4 on the account. */
87
+ static keysForVersion(wallet: Wallet, opts: {
88
+ chainId: number;
89
+ version: number;
90
+ router: Address;
91
+ }): Promise<CloakKeys>;
92
+ private static signedKeys;
82
93
  /** Keys of the first Cloak deployment (plain signed message). Only needed to move an old balance across. */
83
94
  static legacyKeysFromWallet(wallet: Wallet): Promise<CloakKeys>;
95
+ /**
96
+ * Read the router's vaults: HYPE first, then every launch market. A vault without a launch pair (USDC) is an asset,
97
+ * not a market, and is added to `assets` instead.
98
+ */
84
99
  loadMarkets(): Promise<PrivateMarket[]>;
85
100
  get hypeMarket(): PrivateMarket;
86
101
  market(token: Address): PrivateMarket;
@@ -116,6 +131,15 @@ export declare class CloakPrivate {
116
131
  * one step is public and lands on a one-time key. Asks the wallet to approve the pool first if needed.
117
132
  */
118
133
  shieldToken(wallet: Wallet, token: Address, amount: bigint, onStatus?: Status): Promise<Hex>;
134
+ /**
135
+ * Claim a won sealed bid straight into the private vault (v4 launches): the tokens become a note for a one-time key,
136
+ * so later trades can't be linked to the bid. Sent from the bidder's wallet (only the bidder can claim this way); the
137
+ * HYPE refund, if any, still goes to the wallet. The token's vault must exist (the keeper opens it at settlement).
138
+ */
139
+ claimPrivately(wallet: Wallet, auction: Address, bidId: bigint): Promise<{
140
+ txHash: Hex;
141
+ tokens: bigint;
142
+ }>;
119
143
  roundInfo(): Promise<{
120
144
  round: bigint;
121
145
  endsAt: number;
@@ -1,6 +1,6 @@
1
1
  import { encodeAbiParameters, parseEventLogs, zeroAddress } from "viem";
2
- import { cloakMigratorAbi, cloakPairAbi, privateBatcherAbi, privateRouterAbi, shieldedPoolAbi } from "../abis.js";
3
- import { ACCOUNT_MESSAGE, ACCOUNT_TYPED_DATA, CloakKeys, MAIN_KEY, noteCommitment, encryptFullMemo, encryptRouterMemo, formatCloakAddress, nullifierOf, parseCloakAddress, randomField, randomKeyIndex, readMemo, } from "./crypto.js";
2
+ import { cloakAuctionAbi, cloakMigratorAbi, cloakPairAbi, privateBatcherAbi, privateRouterAbi, shieldedPoolAbi } from "../abis.js";
3
+ 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
4
  import { MerkleTree } from "./tree.js";
5
5
  import { MOVE_DATA_ABI, ORDER_DATA_ABI, SWAP_DATA_ABI, buildTransaction, selectNotes, splitStandard } from "./tx.js";
6
6
  /** Leaf positions per tree epoch (a pool starts a new tree after 2^20 leaves). */
@@ -11,6 +11,8 @@ const ERC20_ABI = [
11
11
  { type: "function", name: "allowance", stateMutability: "view", inputs: [{ type: "address" }, { type: "address" }], outputs: [{ type: "uint256" }] },
12
12
  { type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
13
13
  { type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }] },
14
+ { type: "function", name: "symbol", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
15
+ { type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
14
16
  ];
15
17
  const json = (v) => JSON.stringify(v, (_, x) => (typeof x === "bigint" ? x.toString() : x));
16
18
  /** POST shielded calls to a Cloak relayer (`services/keeper` serves `/relay` and `/quote`). */
@@ -96,14 +98,30 @@ export class CloakPrivate {
96
98
  this.migrator = migrator;
97
99
  }
98
100
  /**
99
- * Derive the private keys from one EIP-712 signature bound to this chain and router, so a signature asked for by any
100
- * other site or contract can't reproduce them. With `verify`, the wallet signs twice and the keys are refused if the
101
- * two signatures differ (some smart and MPC wallets sign differently each time, which would hide earlier notes).
101
+ * Derive the wallet's Cloak private account from one EIP-712 signature bound to this chain (never to a contract, so
102
+ * the account survives every upgrade). With `verify`, the wallet signs twice and the keys are refused if the two
103
+ * signatures differ (some smart and MPC wallets sign differently each time, which would hide earlier notes).
102
104
  */
103
105
  static async keysFromWallet(wallet, opts) {
104
- const req = { account: wallet.account, ...ACCOUNT_TYPED_DATA(opts.chainId, opts.router) };
106
+ return CloakPrivate.signedKeys(wallet, ACCOUNT_TYPED_DATA(opts.chainId), opts.verify);
107
+ }
108
+ /** Keys of a v2 or v3 account, bound to that deployment's router. Only needed to move an old balance across. */
109
+ static async routerKeysFromWallet(wallet, opts) {
110
+ return CloakPrivate.signedKeys(wallet, ROUTER_ACCOUNT_TYPED_DATA(opts.chainId, opts.router), false);
111
+ }
112
+ /** The keys an earlier deployment's notes were made for: v1 a plain message, v2 and v3 their router, v4 on the account. */
113
+ static async keysForVersion(wallet, opts) {
114
+ if (opts.version <= 1)
115
+ return CloakPrivate.legacyKeysFromWallet(wallet);
116
+ if (opts.version <= 3)
117
+ return CloakPrivate.routerKeysFromWallet(wallet, opts);
118
+ return CloakPrivate.keysFromWallet(wallet, opts);
119
+ }
120
+ static async signedKeys(wallet, typed, verify) {
121
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
+ const req = { account: wallet.account, ...typed };
105
123
  const sig = await wallet.signTypedData(req);
106
- if (opts.verify) {
124
+ if (verify) {
107
125
  const again = await wallet.signTypedData(req);
108
126
  if (again.toLowerCase() !== sig.toLowerCase()) {
109
127
  throw new Error("This wallet gives a different signature each time, so it can't hold a Cloak private account. Use a standard wallet such as MetaMask or Rabby.");
@@ -116,20 +134,42 @@ export class CloakPrivate {
116
134
  const sig = await wallet.signMessage({ account: wallet.account, message: ACCOUNT_MESSAGE });
117
135
  return CloakKeys.fromSignature(sig);
118
136
  }
137
+ /**
138
+ * Read the router's vaults: HYPE first, then every launch market. A vault without a launch pair (USDC) is an asset,
139
+ * not a market, and is added to `assets` instead.
140
+ */
119
141
  async loadMarkets() {
120
142
  const c = { address: this.router, abi: privateRouterAbi };
121
- const n = await this.publicClient.readContract({ ...c, functionName: "tokensCount" });
143
+ const [n, whype] = await Promise.all([
144
+ this.publicClient.readContract({ ...c, functionName: "tokensCount" }),
145
+ this.publicClient.readContract({ ...c, functionName: "whype" }),
146
+ ]);
147
+ const tokens = await Promise.all(Array.from({ length: Number(n) }, (_, i) => this.publicClient.readContract({ ...c, functionName: "tokens", args: [BigInt(i)] })));
148
+ const rows = await Promise.all(tokens.map((token) => Promise.all([
149
+ this.publicClient.readContract({ ...c, functionName: "poolOf", args: [token] }),
150
+ this.publicClient.readContract({ ...c, functionName: "pairOf", args: [token] }),
151
+ ])));
122
152
  const out = [];
123
- for (let i = 0n; i < n; i++) {
124
- const token = await this.publicClient.readContract({ ...c, functionName: "tokens", args: [i] });
125
- const [pool, pair] = await Promise.all([
126
- this.publicClient.readContract({ ...c, functionName: "poolOf", args: [token] }),
127
- this.publicClient.readContract({ ...c, functionName: "pairOf", args: [token] }),
128
- ]);
129
- out.push({ token, pool, pair: i === 0n ? null : pair });
153
+ let hype;
154
+ for (let i = 0; i < tokens.length; i++) {
155
+ const [pool, pair] = rows[i];
156
+ const token = tokens[i];
157
+ if (token.toLowerCase() === whype.toLowerCase())
158
+ hype = { token, pool, pair: null };
159
+ else if (pair !== zeroAddress)
160
+ out.push({ token, pool, pair });
161
+ else if (!this.assets.some((a) => a.pool.toLowerCase() === pool.toLowerCase())) {
162
+ const [symbol, decimals] = await Promise.all([
163
+ this.publicClient.readContract({ address: token, abi: ERC20_ABI, functionName: "symbol" }).catch(() => "?"),
164
+ this.publicClient.readContract({ address: token, abi: ERC20_ABI, functionName: "decimals" }).catch(() => 18),
165
+ ]);
166
+ this.addAsset({ symbol, token, pool, decimals: Number(decimals), fromBlock: this.fromBlock });
167
+ }
130
168
  }
131
- this.markets = out;
132
- return out;
169
+ if (!hype)
170
+ throw new Error("the router has no HYPE vault");
171
+ this.markets = [hype, ...out];
172
+ return this.markets;
133
173
  }
134
174
  get hypeMarket() {
135
175
  const m = this.markets[0];
@@ -343,6 +383,29 @@ export class CloakPrivate {
343
383
  throw new Error("shield reverted");
344
384
  return hash;
345
385
  }
386
+ /**
387
+ * Claim a won sealed bid straight into the private vault (v4 launches): the tokens become a note for a one-time key,
388
+ * so later trades can't be linked to the bid. Sent from the bidder's wallet (only the bidder can claim this way); the
389
+ * HYPE refund, if any, still goes to the wallet. The token's vault must exist (the keeper opens it at settlement).
390
+ */
391
+ async claimPrivately(wallet, auction, bidId) {
392
+ const [tokens] = await this.publicClient.readContract({ address: auction, abi: cloakAuctionAbi, functionName: "allocationOf", args: [bidId] });
393
+ if (tokens === 0n)
394
+ throw new Error("this bid won no tokens");
395
+ const keyIndex = randomKeyIndex();
396
+ const k = await this.keys.key(keyIndex);
397
+ const blinding = randomField();
398
+ const hash = await wallet.writeContract({
399
+ address: auction,
400
+ abi: cloakAuctionAbi,
401
+ functionName: "claimPrivate",
402
+ args: [bidId, k.pubKey, blinding, encryptFullMemo(this.keys.encPub, tokens, blinding, keyIndex)],
403
+ });
404
+ const r = await this.publicClient.waitForTransactionReceipt({ hash });
405
+ if (r.status !== "success")
406
+ throw new Error("private claim reverted");
407
+ return { txHash: hash, tokens };
408
+ }
346
409
  // ───────────── batch auctions (hide trade size) ─────────────
347
410
  async roundInfo() {
348
411
  if (!this.batcher)
@@ -571,11 +634,12 @@ export class CloakPrivate {
571
634
  async migrateFrom(legacy, onStatus = () => { }, opts = {}) {
572
635
  const migrator = opts.migrator ?? this.migratorAddress();
573
636
  const moved = [];
574
- const markets = await legacy.loadMarkets();
637
+ const markets = [...(await legacy.loadMarkets()), ...legacy.assets];
575
638
  await this.loadMarkets();
639
+ const here = [...this.markets, ...this.assets];
576
640
  for (const m of markets) {
577
- if (!this.markets.some((x) => x.token.toLowerCase() === m.token.toLowerCase()))
578
- continue; // no pool here yet
641
+ if (!here.some((x) => x.token.toLowerCase() === m.token.toLowerCase()))
642
+ continue; // no vault here yet
579
643
  for (;;) {
580
644
  const live = (await legacy.sync(m.pool)).sort((a, b) => (a.amount > b.amount ? -1 : 1));
581
645
  if (!live.length)