@cloakely/sdk 0.4.0 → 0.6.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/README.md +14 -8
- package/dist/abis.d.ts +786 -0
- package/dist/abis.js +1016 -0
- package/dist/bytecode.d.ts +5 -2
- package/dist/bytecode.js +1 -1
- package/dist/cloak.d.ts +132 -2
- package/dist/cloak.js +59 -31
- package/dist/deployments.d.ts +10 -0
- package/dist/deployments.js +14 -3
- package/dist/private/client.d.ts +44 -1
- package/dist/private/client.js +129 -2
- package/dist/private/crypto.d.ts +6 -0
- package/dist/private/crypto.js +8 -0
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +1 -0
- package/package.json +1 -1
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
|
-
/**
|
|
25
|
-
|
|
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;
|
|
@@ -53,7 +58,12 @@ export interface AuctionState {
|
|
|
53
58
|
clearingPrice: bigint;
|
|
54
59
|
sold: bigint;
|
|
55
60
|
raised: bigint;
|
|
61
|
+
/** The market Cloak trades: a CloakV2Market in front of a Uniswap V2 pool, or (earlier launches) a CloakPair. */
|
|
56
62
|
pair: Address;
|
|
63
|
+
/** The launch's Uniswap V2 pool (token / WHYPE), tradable and tracked everywhere. Zero for a CloakPair market. */
|
|
64
|
+
uniPair: Address;
|
|
65
|
+
/** The LP is locked forever and the creator earns its trading fees (see `creatorFees`). */
|
|
66
|
+
creatorFees: boolean;
|
|
57
67
|
tokenName: string;
|
|
58
68
|
tokenSymbol: string;
|
|
59
69
|
/** Launch fee on a successful raise, fixed when the auction was created. */
|
|
@@ -123,6 +133,20 @@ export declare class Cloak {
|
|
|
123
133
|
private listLegacy;
|
|
124
134
|
private listCurrent;
|
|
125
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>;
|
|
126
150
|
/** Revealed demand per tick (empty until the reveal phase — that is the point). */
|
|
127
151
|
demandCurve(auction: Address): Promise<{
|
|
128
152
|
tick: number;
|
|
@@ -218,8 +242,19 @@ export declare class Cloak {
|
|
|
218
242
|
readonly name: "config_";
|
|
219
243
|
readonly type: "address";
|
|
220
244
|
readonly internalType: "contract CloakConfig";
|
|
245
|
+
}, {
|
|
246
|
+
readonly name: "seeder_";
|
|
247
|
+
readonly type: "address";
|
|
248
|
+
readonly internalType: "contract IMarketSeeder";
|
|
249
|
+
}, {
|
|
250
|
+
readonly name: "feeLocker_";
|
|
251
|
+
readonly type: "address";
|
|
252
|
+
readonly internalType: "contract IFeeLocker";
|
|
221
253
|
}];
|
|
222
254
|
readonly stateMutability: "nonpayable";
|
|
255
|
+
}, {
|
|
256
|
+
readonly type: "receive";
|
|
257
|
+
readonly stateMutability: "payable";
|
|
223
258
|
}, {
|
|
224
259
|
readonly type: "function";
|
|
225
260
|
readonly name: "DEAD";
|
|
@@ -250,6 +285,16 @@ export declare class Cloak {
|
|
|
250
285
|
readonly internalType: "uint256";
|
|
251
286
|
}];
|
|
252
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";
|
|
253
298
|
}, {
|
|
254
299
|
readonly type: "function";
|
|
255
300
|
readonly name: "MAX_TICKS";
|
|
@@ -472,6 +517,16 @@ export declare class Cloak {
|
|
|
472
517
|
readonly internalType: "uint256";
|
|
473
518
|
}];
|
|
474
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";
|
|
475
530
|
}, {
|
|
476
531
|
readonly type: "function";
|
|
477
532
|
readonly name: "creatorOwed";
|
|
@@ -536,6 +591,26 @@ export declare class Cloak {
|
|
|
536
591
|
readonly internalType: "bool";
|
|
537
592
|
}];
|
|
538
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";
|
|
539
614
|
}, {
|
|
540
615
|
readonly type: "function";
|
|
541
616
|
readonly name: "feesOwed";
|
|
@@ -830,6 +905,16 @@ export declare class Cloak {
|
|
|
830
905
|
readonly internalType: "uint256";
|
|
831
906
|
}];
|
|
832
907
|
readonly stateMutability: "view";
|
|
908
|
+
}, {
|
|
909
|
+
readonly type: "function";
|
|
910
|
+
readonly name: "seeder";
|
|
911
|
+
readonly inputs: readonly [];
|
|
912
|
+
readonly outputs: readonly [{
|
|
913
|
+
readonly name: "";
|
|
914
|
+
readonly type: "address";
|
|
915
|
+
readonly internalType: "contract IMarketSeeder";
|
|
916
|
+
}];
|
|
917
|
+
readonly stateMutability: "view";
|
|
833
918
|
}, {
|
|
834
919
|
readonly type: "function";
|
|
835
920
|
readonly name: "settle";
|
|
@@ -892,6 +977,16 @@ export declare class Cloak {
|
|
|
892
977
|
readonly internalType: "uint256";
|
|
893
978
|
}];
|
|
894
979
|
readonly stateMutability: "view";
|
|
980
|
+
}, {
|
|
981
|
+
readonly type: "function";
|
|
982
|
+
readonly name: "uniPair";
|
|
983
|
+
readonly inputs: readonly [];
|
|
984
|
+
readonly outputs: readonly [{
|
|
985
|
+
readonly name: "";
|
|
986
|
+
readonly type: "address";
|
|
987
|
+
readonly internalType: "address";
|
|
988
|
+
}];
|
|
989
|
+
readonly stateMutability: "view";
|
|
895
990
|
}, {
|
|
896
991
|
readonly type: "function";
|
|
897
992
|
readonly name: "withdrawProceeds";
|
|
@@ -978,6 +1073,21 @@ export declare class Cloak {
|
|
|
978
1073
|
readonly internalType: "bytes";
|
|
979
1074
|
}];
|
|
980
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;
|
|
981
1091
|
}, {
|
|
982
1092
|
readonly type: "event";
|
|
983
1093
|
readonly name: "DustSwept";
|
|
@@ -1183,6 +1293,26 @@ export declare class Cloak {
|
|
|
1183
1293
|
readonly internalType: "uint256";
|
|
1184
1294
|
}];
|
|
1185
1295
|
readonly anonymous: false;
|
|
1296
|
+
}, {
|
|
1297
|
+
readonly type: "event";
|
|
1298
|
+
readonly name: "UniswapMarket";
|
|
1299
|
+
readonly inputs: readonly [{
|
|
1300
|
+
readonly name: "uniPair";
|
|
1301
|
+
readonly type: "address";
|
|
1302
|
+
readonly indexed: true;
|
|
1303
|
+
readonly internalType: "address";
|
|
1304
|
+
}, {
|
|
1305
|
+
readonly name: "market";
|
|
1306
|
+
readonly type: "address";
|
|
1307
|
+
readonly indexed: false;
|
|
1308
|
+
readonly internalType: "address";
|
|
1309
|
+
}, {
|
|
1310
|
+
readonly name: "lpShares";
|
|
1311
|
+
readonly type: "uint256";
|
|
1312
|
+
readonly indexed: false;
|
|
1313
|
+
readonly internalType: "uint256";
|
|
1314
|
+
}];
|
|
1315
|
+
readonly anonymous: false;
|
|
1186
1316
|
}], "Committed", true, bigint, bigint>>;
|
|
1187
1317
|
/**
|
|
1188
1318
|
* 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
|
@@ -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" ?
|
|
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:
|
|
105
|
+
lpLockUntil: lock,
|
|
104
106
|
tlockRound: BigInt(drandRoundAt(p.commitEnd)),
|
|
105
107
|
};
|
|
106
108
|
const txHash = await w.writeContract({
|
|
@@ -120,47 +122,34 @@ export class Cloak {
|
|
|
120
122
|
async listLaunches() {
|
|
121
123
|
if (!this.factory)
|
|
122
124
|
throw new Error("Cloak: factory address required");
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return [...(await this.listCurrent()), ...legacy];
|
|
125
|
+
// every factory, and every launch in it, read at once (a multicall client folds them into a few requests)
|
|
126
|
+
const [current, ...legacy] = await Promise.all([this.listCurrent(), ...this.legacyFactories.map((f) => this.listLegacy(f))]);
|
|
127
|
+
return [...current, ...legacy.flat()];
|
|
127
128
|
}
|
|
128
129
|
async listLegacy(factory) {
|
|
129
130
|
const n = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launchesCount" });
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
return Promise.all(Array.from({ length: Number(n) }, async (_, k) => {
|
|
132
|
+
const i = BigInt(k);
|
|
132
133
|
// a factory from v2 on returns a vesting contract too; the first one returns five fields
|
|
133
134
|
const six = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launches", args: [i] }).catch(() => null);
|
|
134
135
|
const [auction, token, creator, launchpad, creatorAlloc] = six ?? (await this.publicClient.readContract({ address: factory, abi: LEGACY_LAUNCHES_ABI, functionName: "launches", args: [i] }));
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
return out;
|
|
136
|
+
return { auction, token, creator, launchpad, creatorAlloc, vesting: (six?.[5] ?? zeroAddress), factory, index: i, legacy: true };
|
|
137
|
+
}));
|
|
138
138
|
}
|
|
139
139
|
async listCurrent() {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
abi: cloakFactoryAbi,
|
|
145
|
-
|
|
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, index: i });
|
|
156
|
-
}
|
|
157
|
-
return out;
|
|
140
|
+
const factory = this.factory;
|
|
141
|
+
const n = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launchesCount" });
|
|
142
|
+
return Promise.all(Array.from({ length: Number(n) }, async (_, k) => {
|
|
143
|
+
const index = BigInt(k);
|
|
144
|
+
const [auction, token, creator, launchpad, creatorAlloc, vesting] = await this.publicClient.readContract({ address: factory, abi: cloakFactoryAbi, functionName: "launches", args: [index] });
|
|
145
|
+
return { auction, token, creator, launchpad, creatorAlloc, vesting, factory, index };
|
|
146
|
+
}));
|
|
158
147
|
}
|
|
159
148
|
// ───────────── reads ─────────────
|
|
160
149
|
async getAuction(auction) {
|
|
161
150
|
const c = { address: auction, abi: cloakAuctionAbi };
|
|
162
151
|
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([
|
|
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([
|
|
164
153
|
r.readContract({ ...c, functionName: "params" }),
|
|
165
154
|
r.readContract({ ...c, functionName: "token" }),
|
|
166
155
|
r.readContract({ ...c, functionName: "phase" }),
|
|
@@ -181,6 +170,9 @@ export class Cloak {
|
|
|
181
170
|
r.readContract({ ...c, functionName: "depositCap" }).catch(() => 0n),
|
|
182
171
|
r.readContract({ ...c, functionName: "creatorOwed" }).catch(() => 0n),
|
|
183
172
|
r.readContract({ ...c, functionName: "feesOwed" }).catch(() => 0n),
|
|
173
|
+
// launches from the Uniswap factory on
|
|
174
|
+
r.readContract({ ...c, functionName: "uniPair" }).catch(() => zeroAddress),
|
|
175
|
+
r.readContract({ ...c, functionName: "creatorFees" }).catch(() => false),
|
|
184
176
|
]);
|
|
185
177
|
const [creator, saleSupply, lpReserve, minPrice, tickSize, numTicks, commitEnd, revealEnd, lpBps, noRevealPenaltyBps, lpLockUntil, tlockRound] = params;
|
|
186
178
|
const [tokenName, tokenSymbol] = await Promise.all([
|
|
@@ -214,6 +206,8 @@ export class Cloak {
|
|
|
214
206
|
sold,
|
|
215
207
|
raised,
|
|
216
208
|
pair,
|
|
209
|
+
uniPair,
|
|
210
|
+
creatorFees,
|
|
217
211
|
tokenName,
|
|
218
212
|
tokenSymbol,
|
|
219
213
|
launchFeeBps,
|
|
@@ -223,6 +217,40 @@ export class Cloak {
|
|
|
223
217
|
feesOwed,
|
|
224
218
|
};
|
|
225
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
|
+
}
|
|
226
254
|
/** Revealed demand per tick (empty until the reveal phase — that is the point). */
|
|
227
255
|
async demandCurve(auction) {
|
|
228
256
|
const s = await this.getAuction(auction);
|
package/dist/deployments.d.ts
CHANGED
|
@@ -28,6 +28,14 @@ export interface CloakDeployment {
|
|
|
28
28
|
factory: Address;
|
|
29
29
|
/** Block the factory was deployed in: scan launch events from here. */
|
|
30
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;
|
|
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;
|
|
31
39
|
router: Address;
|
|
32
40
|
/** Block the router was deployed in: scan private-pool events from here. */
|
|
33
41
|
routerBlock: bigint;
|
|
@@ -52,6 +60,8 @@ export interface CloakDeployment {
|
|
|
52
60
|
previous?: PreviousDeployment[];
|
|
53
61
|
}
|
|
54
62
|
export declare const deployments: Record<number, CloakDeployment>;
|
|
63
|
+
/** Every factory before `d.factory` whose launches should still list, newest first: this deployment's, then earlier ones'. */
|
|
64
|
+
export declare function legacyFactories(d: CloakDeployment): Address[];
|
|
55
65
|
/** The Cloak deployment on `chainId`, or an error naming the chains Cloak is on. */
|
|
56
66
|
export declare function deploymentFor(chainId: number): CloakDeployment;
|
|
57
67
|
/**
|
package/dist/deployments.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { privateRouterAbi } from "./abis.js";
|
|
2
2
|
import { readRegistry } from "./registry.js";
|
|
3
3
|
export const deployments = {
|
|
4
|
-
// Cloak v4 (Oct 31), the permanent vaults: deployments/elysium-testnet-v4.json. CloakConfig's registry lists all of it;
|
|
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
5
|
// liveDeployment() reads the current contracts from there.
|
|
6
6
|
99801: {
|
|
7
7
|
chainId: 99801,
|
|
@@ -9,8 +9,14 @@ export const deployments = {
|
|
|
9
9
|
config: "0x9fdf166f0671166148ea98cdd3ef0118d256167f",
|
|
10
10
|
vaults: "0x24bc1d369aab3a2d741a48f8cac1af5dfd9f3d43",
|
|
11
11
|
vaultsBlock: 19485039n,
|
|
12
|
-
factory:
|
|
13
|
-
|
|
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"],
|
|
17
|
+
firstFactoryBlock: 19484794n,
|
|
18
|
+
seeder: "0x633aa82b806dafc0ae4c84418d23f3726139a88d",
|
|
19
|
+
feeLocker: "0x3b74062a72b16f9a221d63515518a635414501d8",
|
|
14
20
|
router: "0x343ab9a7aac93cee28321410510ea24417cc6386",
|
|
15
21
|
routerBlock: 19485063n,
|
|
16
22
|
batcher: "0xc69e4bca18020ae93bc9f17093985bfb139a3203",
|
|
@@ -54,6 +60,10 @@ export const deployments = {
|
|
|
54
60
|
],
|
|
55
61
|
},
|
|
56
62
|
};
|
|
63
|
+
/** Every factory before `d.factory` whose launches should still list, newest first: this deployment's, then earlier ones'. */
|
|
64
|
+
export function legacyFactories(d) {
|
|
65
|
+
return [...(d.earlierFactories ?? []), ...(d.previous ?? []).map((p) => p.factory)].filter((f) => f && !/^0x0{40}$/i.test(f));
|
|
66
|
+
}
|
|
57
67
|
/** The Cloak deployment on `chainId`, or an error naming the chains Cloak is on. */
|
|
58
68
|
export function deploymentFor(chainId) {
|
|
59
69
|
const d = deployments[chainId];
|
|
@@ -81,6 +91,7 @@ export async function liveDeployment(publicClient, base) {
|
|
|
81
91
|
vaultsBlock: reg.vaultsBlock ?? base.vaultsBlock,
|
|
82
92
|
factory: reg.factory,
|
|
83
93
|
factoryBlock: reg.factoryBlock,
|
|
94
|
+
earlierFactories: reg.earlierFactories.length ? reg.earlierFactories : base.earlierFactories,
|
|
84
95
|
router: reg.router,
|
|
85
96
|
routerBlock: reg.routerBlock,
|
|
86
97
|
batcher: reg.batcher ?? base.batcher,
|
package/dist/private/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Address, type Hex, type PublicClient } from "viem";
|
|
2
|
-
import type
|
|
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";
|
|
@@ -140,6 +140,49 @@ export declare class CloakPrivate {
|
|
|
140
140
|
txHash: Hex;
|
|
141
141
|
tokens: bigint;
|
|
142
142
|
}>;
|
|
143
|
+
/**
|
|
144
|
+
* One-time bid wallet `n` for `auction`. The private balance pays its deposit and a little gas through a relayed
|
|
145
|
+
* withdrawal, so on-chain the bid comes from a fresh address nobody can tie to this account's wallet.
|
|
146
|
+
*/
|
|
147
|
+
bidWallet(auction: Address, n: number): {
|
|
148
|
+
n: number;
|
|
149
|
+
address: Address;
|
|
150
|
+
wallet: Wallet;
|
|
151
|
+
};
|
|
152
|
+
/** Gas a bid wallet keeps for its own transactions: the bid, a private claim, and moving what's left back in. */
|
|
153
|
+
bidGasReserve(): Promise<bigint>;
|
|
154
|
+
/** This account's bid wallets for `auction` that have been used (newest last), found from the seed alone. */
|
|
155
|
+
bidWallets(auction: Address): Promise<{
|
|
156
|
+
n: number;
|
|
157
|
+
address: Address;
|
|
158
|
+
wallet: Wallet;
|
|
159
|
+
}[]>;
|
|
160
|
+
/**
|
|
161
|
+
* Place a sealed bid paid from the private HYPE balance: a relayed private withdrawal funds a fresh one-time wallet
|
|
162
|
+
* with the deposit plus gas, and that wallet seals the bid. Keep the returned secret like any bid's (auto-reveal makes
|
|
163
|
+
* losing it harmless). After settlement, `finishPrivateBids` claims the tokens privately and moves the refund back.
|
|
164
|
+
*/
|
|
165
|
+
bidPrivately(auction: Address, opts: {
|
|
166
|
+
maxPrice: bigint;
|
|
167
|
+
quantity: bigint;
|
|
168
|
+
deposit?: bigint;
|
|
169
|
+
}, onStatus?: Status): Promise<{
|
|
170
|
+
secret: import("../cloak.js").BidSecret;
|
|
171
|
+
bidWallet: `0x${string}`;
|
|
172
|
+
n: number;
|
|
173
|
+
fundTx: `0x${string}`;
|
|
174
|
+
}>;
|
|
175
|
+
/**
|
|
176
|
+
* After a launch settles: for every bid this account placed from its private balance, claim won tokens straight into
|
|
177
|
+
* the private vault (or, if a cap blocks that, to the bid wallet and then in), collect refunds, and move the bid
|
|
178
|
+
* wallet's HYPE back into the private balance. Before settlement it only moves back HYPE from a one-time wallet whose
|
|
179
|
+
* bid never went through. Safe to call again; it only does what's left.
|
|
180
|
+
*/
|
|
181
|
+
finishPrivateBids(auction: Address, onStatus?: Status): Promise<{
|
|
182
|
+
claimed: number;
|
|
183
|
+
movedHype: bigint;
|
|
184
|
+
movedTokens: bigint;
|
|
185
|
+
}>;
|
|
143
186
|
roundInfo(): Promise<{
|
|
144
187
|
round: bigint;
|
|
145
188
|
endsAt: number;
|
package/dist/private/client.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { encodeAbiParameters, parseEventLogs, zeroAddress } from "viem";
|
|
2
|
-
import {
|
|
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,131 @@ 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
|
+
const reader = new Cloak(this.publicClient);
|
|
484
|
+
const s = await reader.getAuction(auction);
|
|
485
|
+
let claimed = 0;
|
|
486
|
+
let movedHype = 0n;
|
|
487
|
+
let movedTokens = 0n;
|
|
488
|
+
const vaultOpen = !s.failed && this.markets.some((m) => m.token.toLowerCase() === s.token.toLowerCase());
|
|
489
|
+
for (const bw of await this.bidWallets(auction)) {
|
|
490
|
+
const c = new Cloak(this.publicClient, bw.wallet);
|
|
491
|
+
const bids = await reader.bidsOf(auction, bw.address);
|
|
492
|
+
// before settlement only a wallet whose bid never went through has anything to move back
|
|
493
|
+
if (!s.settled && bids.length > 0)
|
|
494
|
+
continue;
|
|
495
|
+
for (const b of bids) {
|
|
496
|
+
if (b.claimed)
|
|
497
|
+
continue;
|
|
498
|
+
onStatus(`Claiming bid #${b.id}…`);
|
|
499
|
+
if (b.tokens > 0n && vaultOpen) {
|
|
500
|
+
try {
|
|
501
|
+
await this.claimPrivately(bw.wallet, auction, b.id);
|
|
502
|
+
claimed++;
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
catch { } // over the vault's deposit cap: claim to the bid wallet instead
|
|
506
|
+
}
|
|
507
|
+
await c.claim(auction, [b.id]);
|
|
508
|
+
claimed++;
|
|
509
|
+
}
|
|
510
|
+
const owed = await this.publicClient.readContract({ address: auction, abi: cloakAuctionAbi, functionName: "refundOwed", args: [bw.address] }).catch(() => 0n);
|
|
511
|
+
if (owed > 0n) {
|
|
512
|
+
const h = await bw.wallet.writeContract({ address: auction, abi: cloakAuctionAbi, functionName: "withdrawRefund", args: [bw.address] });
|
|
513
|
+
await this.publicClient.waitForTransactionReceipt({ hash: h });
|
|
514
|
+
}
|
|
515
|
+
if (vaultOpen) {
|
|
516
|
+
const t = await this.publicClient.readContract({ address: s.token, abi: ERC20_ABI, functionName: "balanceOf", args: [bw.address] });
|
|
517
|
+
if (t > 0n) {
|
|
518
|
+
onStatus("Moving tokens into your private balance…");
|
|
519
|
+
try {
|
|
520
|
+
await this.shieldToken(bw.wallet, s.token, t);
|
|
521
|
+
movedTokens += t;
|
|
522
|
+
}
|
|
523
|
+
catch { } // a cap may block it; the tokens stay in the one-time wallet, which nobody can tie to you
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const bal = await this.publicClient.getBalance({ address: bw.address });
|
|
527
|
+
const keep = (await this.bidGasReserve()) / 3n; // gas for this last shield
|
|
528
|
+
if (bal > keep * 2n) {
|
|
529
|
+
onStatus("Moving the refund back into your private balance…");
|
|
530
|
+
await this.shieldHype(bw.wallet, bal - keep);
|
|
531
|
+
movedHype += bal - keep;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return { claimed, movedHype, movedTokens };
|
|
535
|
+
}
|
|
409
536
|
// ───────────── batch auctions (hide trade size) ─────────────
|
|
410
537
|
async roundInfo() {
|
|
411
538
|
if (!this.batcher)
|
package/dist/private/crypto.d.ts
CHANGED
|
@@ -70,6 +70,12 @@ 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;
|
package/dist/private/crypto.js
CHANGED
|
@@ -77,6 +77,14 @@ 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
89
|
export const noteCommitment = (n) => poseidon([n.amount, n.pubKey, n.blinding]);
|
|
82
90
|
export async function nullifierOf(n, leafIndex, privKey) {
|