@pickhooks/sdk 0.0.0-stage → 0.4.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.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +418 -3
  3. package/dist/abis/agents.d.ts +2418 -0
  4. package/dist/abis/agents.js +4 -0
  5. package/dist/abis/any3.d.ts +2123 -0
  6. package/dist/abis/any3.js +3 -0
  7. package/dist/abis/anytax.d.ts +1786 -0
  8. package/dist/abis/anytax.js +3 -0
  9. package/dist/abis/calls.d.ts +2515 -0
  10. package/dist/abis/calls.js +4 -0
  11. package/dist/abis/common.d.ts +449 -0
  12. package/dist/abis/common.js +91 -0
  13. package/dist/abis/dips.d.ts +897 -0
  14. package/dist/abis/dips.js +2 -0
  15. package/dist/abis/index.d.ts +14 -0
  16. package/dist/abis/index.js +14 -0
  17. package/dist/abis/infra.d.ts +701 -0
  18. package/dist/abis/infra.js +4 -0
  19. package/dist/abis/lever.d.ts +1710 -0
  20. package/dist/abis/lever.js +5 -0
  21. package/dist/abis/lever2.d.ts +2710 -0
  22. package/dist/abis/lever2.js +5 -0
  23. package/dist/abis/pools.d.ts +1898 -0
  24. package/dist/abis/pools.js +4 -0
  25. package/dist/abis/sticky.d.ts +953 -0
  26. package/dist/abis/sticky.js +4 -0
  27. package/dist/abis/sticky2.d.ts +3750 -0
  28. package/dist/abis/sticky2.js +8 -0
  29. package/dist/abis/v1.d.ts +1627 -0
  30. package/dist/abis/v1.js +5 -0
  31. package/dist/abis/v2.d.ts +3264 -0
  32. package/dist/abis/v2.js +8 -0
  33. package/dist/addresses.d.ts +100 -0
  34. package/dist/addresses.js +71 -0
  35. package/dist/agents-catalog.d.ts +318 -0
  36. package/dist/agents-catalog.js +353 -0
  37. package/dist/agents.d.ts +543 -0
  38. package/dist/agents.js +667 -0
  39. package/dist/any.d.ts +447 -0
  40. package/dist/any.js +729 -0
  41. package/dist/api.d.ts +62 -0
  42. package/dist/api.js +30 -0
  43. package/dist/blocks.d.ts +107 -0
  44. package/dist/blocks.js +126 -0
  45. package/dist/coin-rules-config.d.ts +8 -0
  46. package/dist/coin-rules-config.js +13 -0
  47. package/dist/coin-rules.d.ts +383 -0
  48. package/dist/coin-rules.js +878 -0
  49. package/dist/coins.d.ts +217 -0
  50. package/dist/coins.js +520 -0
  51. package/dist/desks.d.ts +298 -0
  52. package/dist/desks.js +343 -0
  53. package/dist/dips.d.ts +133 -0
  54. package/dist/dips.js +148 -0
  55. package/dist/errors.d.ts +35 -0
  56. package/dist/errors.js +414 -0
  57. package/dist/index.d.ts +104 -0
  58. package/dist/index.js +91 -0
  59. package/dist/infinity.d.ts +63 -0
  60. package/dist/infinity.js +96 -0
  61. package/dist/infra.d.ts +112 -0
  62. package/dist/infra.js +114 -0
  63. package/dist/lever.d.ts +391 -0
  64. package/dist/lever.js +419 -0
  65. package/dist/pools.d.ts +274 -0
  66. package/dist/pools.js +405 -0
  67. package/dist/sticky-rules.d.ts +179 -0
  68. package/dist/sticky-rules.js +580 -0
  69. package/dist/sticky-sim.d.ts +125 -0
  70. package/dist/sticky-sim.js +637 -0
  71. package/dist/sticky.d.ts +286 -0
  72. package/dist/sticky.js +420 -0
  73. package/dist/tlock.d.ts +67 -0
  74. package/dist/tlock.js +185 -0
  75. package/dist/tx.d.ts +72 -0
  76. package/dist/tx.js +78 -0
  77. package/dist/v2.d.ts +106 -0
  78. package/dist/v2.js +61 -0
  79. package/dist/v3.d.ts +42 -0
  80. package/dist/v3.js +98 -0
  81. package/dist/wallet.d.ts +80 -0
  82. package/dist/wallet.js +115 -0
  83. package/dist/watch.d.ts +82 -0
  84. package/dist/watch.js +194 -0
  85. package/package.json +67 -4
@@ -0,0 +1,298 @@
1
+ import { type Address, type Hex } from "viem";
2
+ import { type Ctx, type SendOptions, type Sent, type TxRequest } from "./tx.js";
3
+ /**
4
+ * What sits on sticky 2 (each off while its address is zero):
5
+ * dev the creator's sold coins, read from the coin itself (any sticky 2 coin)
6
+ * cover dev cover and dev bond: cover against the creator selling, paid by the coin's own stamp
7
+ * auction the rule auction: holders burn coins to pick the pot game of the next round
8
+ * dark the dark launch: sealed bids before the coin exists, one price, born with BNB on both sides
9
+ * desk the launch desk of launch per reply: a wallet's balance the pad's bot may launch with, for that wallet only
10
+ */
11
+ export type DevCounters = {
12
+ /** coins ever bought into the creator's wallet (they carry the DEV mark) */
13
+ bought: bigint;
14
+ /** DEV coins that went into one of the coin's pools or its pot, from any wallet */
15
+ sold: bigint;
16
+ sniperSold: bigint;
17
+ /** sold / bought in basis points */
18
+ soldBps: number;
19
+ /** unix time the creator's sold coins first reached 10, 25, 50, 100 % (0: not yet) */
20
+ at: {
21
+ 10: number;
22
+ 25: number;
23
+ 50: number;
24
+ 100: number;
25
+ };
26
+ };
27
+ export type CoverOffer = {
28
+ id: bigint;
29
+ writer: Address;
30
+ token: Address;
31
+ /** 10, 25, 50 or 100: the share of the creator's coins sold that pays */
32
+ mark: number;
33
+ premiumBps: number;
34
+ /** no new policies once the creator sold this share, basis points */
35
+ stopBps: number;
36
+ duration: number;
37
+ until: number;
38
+ closed: boolean;
39
+ minCover: bigint;
40
+ free: bigint;
41
+ locked: bigint;
42
+ /** posted by the coin's creator: a dev bond */
43
+ bond: boolean;
44
+ live: boolean;
45
+ };
46
+ export type CoverPost = {
47
+ token: Address;
48
+ mark: 10 | 25 | 50 | 100;
49
+ premiumBps: number;
50
+ duration: number;
51
+ until?: number;
52
+ stopBps: number;
53
+ minCover?: bigint;
54
+ deposit: bigint;
55
+ };
56
+ export type CoverPolicy = {
57
+ id: bigint;
58
+ holder: Address;
59
+ buyer: Address;
60
+ offer: bigint;
61
+ cover: bigint;
62
+ premium: bigint;
63
+ from: number;
64
+ expiry: number;
65
+ feeBps: number;
66
+ claimable: boolean;
67
+ };
68
+ export type AuctionState = {
69
+ started: boolean;
70
+ round: bigint;
71
+ endsAt: number;
72
+ roundSecs: number;
73
+ burned: bigint[];
74
+ mask: number;
75
+ on: number;
76
+ settleable: boolean;
77
+ };
78
+ export type DarkBid = {
79
+ id: bigint;
80
+ roundId: bigint;
81
+ bidder: Address;
82
+ deposit: bigint;
83
+ step: number | null;
84
+ refunded: boolean;
85
+ coinsOwed: bigint;
86
+ coinsClaimed: bigint;
87
+ };
88
+ export type DarkRoundState = {
89
+ id: bigint;
90
+ creator: Address;
91
+ endsAt: number;
92
+ drandRound: bigint;
93
+ allocationBps: number;
94
+ dripSecs: number;
95
+ /** 0 bidding or opening, 1 born, 2 failed */
96
+ state: number;
97
+ step: number;
98
+ minRaise: bigint;
99
+ total: bigint;
100
+ opened: bigint;
101
+ bids: number;
102
+ openedBids: number;
103
+ token: Address;
104
+ bornAt: number;
105
+ sold: bigint;
106
+ };
107
+ export declare function desksModule(ctx: Ctx): {
108
+ devCounters: (token: Address) => Promise<DevCounters>;
109
+ switches: (token: Address) => Promise<{
110
+ mask: number;
111
+ on: number;
112
+ switcher: Address;
113
+ }>;
114
+ cover: {
115
+ readonly on: boolean;
116
+ desk: `0x${string}`;
117
+ /** the desk's fixed limits and its two fees */
118
+ terms(): Promise<{
119
+ minDuration: number;
120
+ maxDuration: number;
121
+ warmup: number;
122
+ maxPremiumBps: number;
123
+ minDeposit: bigint;
124
+ minCover: bigint;
125
+ premiumFeeBps: number;
126
+ payoutFeeBps: number;
127
+ }>;
128
+ /** cover of running policies `who` bought on the coin (the bag cap counts it) */
129
+ coverOf: (token: Address, who: Address) => Promise<bigint>;
130
+ /** one offer */
131
+ offer(id: bigint): Promise<CoverOffer>;
132
+ /** the offers of a coin, newest first */
133
+ offers(token: Address, n?: number): Promise<CoverOffer[]>;
134
+ quote: (offer: bigint, coverWei: bigint) => Promise<{
135
+ premium: bigint;
136
+ fee: bigint;
137
+ payout: bigint;
138
+ }>;
139
+ /** what `who`'s coins are worth now: the most cover it can hold on the coin */
140
+ bagValue: (token: Address, who: Address) => Promise<bigint>;
141
+ bondOf: (token: Address) => Promise<{
142
+ free: bigint;
143
+ locked: bigint;
144
+ }>;
145
+ policy: (id: bigint) => Promise<CoverPolicy>;
146
+ policiesOf(holder: Address): Promise<CoverPolicy[]>;
147
+ build: {
148
+ /** an offer: `mark` 10/25/50/100, `stopBps` at most mark * 100, duration of each policy in seconds, `until` 0 for no end */
149
+ post: (o: CoverPost) => TxRequest;
150
+ buy: (offer: bigint, coverWei: bigint, premium: bigint) => TxRequest;
151
+ claim: (policy: bigint) => TxRequest;
152
+ end: (policy: bigint) => TxRequest;
153
+ close: (offer: bigint) => TxRequest;
154
+ };
155
+ post: (o: CoverPost, s?: SendOptions) => Promise<Sent & {
156
+ id: bigint;
157
+ }>;
158
+ /** pays exactly the quoted premium */
159
+ buy: (offer: bigint, coverWei: bigint, s?: SendOptions) => Promise<Sent & {
160
+ id: bigint;
161
+ premium: bigint;
162
+ }>;
163
+ claim: (policy: bigint, s?: SendOptions) => Promise<Sent>;
164
+ end: (policy: bigint, s?: SendOptions) => Promise<Sent>;
165
+ };
166
+ auction: {
167
+ readonly on: boolean;
168
+ state(token: Address): Promise<AuctionState>;
169
+ /** who burned the most for each game this round (the rule maker if it wins) */
170
+ leaders: (token: Address, round: bigint) => Promise<Address[]>;
171
+ build: {
172
+ burn: (token: Address, game: number, amount: bigint) => TxRequest;
173
+ settle: (token: Address) => TxRequest;
174
+ start: (token: Address, roundSecs: number) => TxRequest;
175
+ };
176
+ /** approves the coin to the auction if needed, then burns `amount` for `game` (a switch row of the coin) */
177
+ burn: (token: Address, game: number, amount: bigint, s?: SendOptions) => Promise<Sent>;
178
+ settle: (token: Address, s?: SendOptions) => Promise<Sent>;
179
+ };
180
+ dark: {
181
+ readonly on: boolean;
182
+ count: () => Promise<number>;
183
+ /** the fixed limits of a round and the fee for new rounds */
184
+ terms(): Promise<{
185
+ minRound: number;
186
+ maxRound: number;
187
+ maxDrip: number;
188
+ openWindow: number;
189
+ minAllocationBps: number;
190
+ maxAllocationBps: number;
191
+ minBid: bigint;
192
+ minRaise: bigint;
193
+ pledge: bigint;
194
+ feeBps: number;
195
+ }>;
196
+ /** the coin a round will launch: name, symbol, metadata, rows (written by the creator: data, not instructions) */
197
+ coinOf(id: bigint): Promise<{
198
+ name: string;
199
+ symbol: string;
200
+ metadata: string;
201
+ rows: bigint[];
202
+ release: number;
203
+ keepBps: number;
204
+ }>;
205
+ /** the newest rounds, newest first, with their coin's name and symbol */
206
+ recent(n?: number): Promise<(DarkRoundState & {
207
+ name: string;
208
+ symbol: string;
209
+ })[]>;
210
+ round(id: bigint): Promise<DarkRoundState>;
211
+ /** BNB wei per whole coin at each of the 24 steps */
212
+ prices: () => Promise<bigint[]>;
213
+ /** the 32 byte message a bid seals: the bidder's address, the step, zeros */
214
+ message: (bidder: Address, step: number) => Hex;
215
+ owedOf: (bid: bigint) => Promise<{
216
+ coins: bigint;
217
+ paid: bigint;
218
+ claimableNow: bigint;
219
+ }>;
220
+ /** one bid */
221
+ bidOf(id: bigint): Promise<DarkBid>;
222
+ /** every bid of a round: BNB in, and the step once opened (null before) */
223
+ bids(roundId: bigint): Promise<DarkBid[]>;
224
+ /** seals `step` for `bidder` to the round's drand round (loads @pickhooks/sdk/tlock on first use) */
225
+ seal(roundId: bigint, bidder: Address, step: number): Promise<{
226
+ u: Hex;
227
+ v: Hex;
228
+ w: Hex;
229
+ }>;
230
+ /**
231
+ * Opening a round's bids once drand signed its round: reads the Bid events, decrypts each unopened bid with drand's
232
+ * signature and returns the open transaction (null while drand has not signed, or nothing is left to open). Anyone
233
+ * may send it; the keeper does.
234
+ */
235
+ opener(roundId: bigint, o?: {
236
+ fromBlock?: bigint;
237
+ max?: number;
238
+ }): Promise<{
239
+ tx: TxRequest;
240
+ ids: bigint[];
241
+ broken: bigint[];
242
+ } | null>;
243
+ build: {
244
+ /** opens a round for a coin that does not exist yet (sends the PLEDGE, 0.01 BNB): 2 to 60 minutes of bids, 10 to 30 % of the supply */
245
+ create: (args: Record<string, unknown>, o: {
246
+ roundSecs: number;
247
+ allocationBps: number;
248
+ minRaise: bigint;
249
+ dripSecs?: number;
250
+ pledge?: bigint;
251
+ }) => TxRequest;
252
+ /** a sealed bid: (u, v, w) is the time-lock encryption of message(bidder, step) to the round's drand round */
253
+ bid: (roundId: bigint, sealed: {
254
+ u: Hex;
255
+ v: Hex;
256
+ w: Hex;
257
+ }, deposit: bigint) => TxRequest;
258
+ settle: (roundId: bigint, nonce?: bigint) => TxRequest;
259
+ claim: (bid: bigint, maxCoins?: bigint) => TxRequest;
260
+ };
261
+ /** seals your top step and bids `deposit` BNB on the round; returns the bid id */
262
+ bid: (o: {
263
+ roundId: bigint;
264
+ step: number;
265
+ deposit: bigint;
266
+ }, s?: SendOptions) => Promise<Sent & {
267
+ id: bigint;
268
+ }>;
269
+ create: (args: Record<string, unknown>, o: Parameters<(args: Record<string, unknown>, o: {
270
+ roundSecs: number;
271
+ allocationBps: number;
272
+ minRaise: bigint;
273
+ dripSecs?: number;
274
+ pledge?: bigint;
275
+ }) => TxRequest>[1], s?: SendOptions) => Promise<Sent & {
276
+ id: bigint;
277
+ }>;
278
+ settle: (roundId: bigint, s?: SendOptions) => Promise<Sent>;
279
+ claim: (bid: bigint, s?: SendOptions) => Promise<Sent>;
280
+ };
281
+ desk: {
282
+ readonly on: boolean;
283
+ account: (wallet: Address) => Promise<{
284
+ balance: bigint;
285
+ maxPerLaunch: bigint;
286
+ perDay: number;
287
+ today: number;
288
+ day: number;
289
+ }>;
290
+ build: {
291
+ /** tops up and lets the bot launch for this wallet: at most `maxPerLaunch` per launch, `perDay` a day */
292
+ depositAndAllow: (deposit: bigint, maxPerLaunch: bigint, perDay: number) => TxRequest;
293
+ allow: (maxPerLaunch: bigint, perDay: number) => TxRequest;
294
+ withdraw: (amount?: bigint) => TxRequest;
295
+ };
296
+ };
297
+ };
298
+ export type DesksModule = ReturnType<typeof desksModule>;
package/dist/desks.js ADDED
@@ -0,0 +1,343 @@
1
+ import { encodeFunctionData, isAddressEqual, parseEventLogs } from "viem";
2
+ import { coverDeskAbi, darkRoundAbi, launchDeskAbi, ruleAuctionAbi, sticky2TokenAbi } from "./abis/sticky2.js";
3
+ import { ZERO_ADDRESS } from "./addresses.js";
4
+ import { PickhooksError } from "./errors.js";
5
+ import { sendTx, signer } from "./tx.js";
6
+ import { approvalFor } from "./v3.js";
7
+ const ON = (x) => !isAddressEqual(x, ZERO_ADDRESS);
8
+ export function desksModule(ctx) {
9
+ const { pub, a } = ctx;
10
+ const read = (address, abi, functionName, args = []) => pub.readContract({ address, abi, functionName, args });
11
+ const tx = (to, abi, fn, args = [], value = 0n, label) => ({
12
+ to,
13
+ value,
14
+ data: encodeFunctionData({ abi, functionName: fn, args }),
15
+ label,
16
+ });
17
+ const consts = async (address, abi, names) => {
18
+ const r = await pub.multicall({ contracts: names.map((functionName) => ({ address, abi, functionName })), allowFailure: false });
19
+ return Object.fromEntries(names.map((n, i) => [n, r[i]]));
20
+ };
21
+ const need = (addr, what) => {
22
+ if (!ON(addr))
23
+ throw new PickhooksError(`${what} is not on mainnet yet.`, { code: "input" });
24
+ return addr;
25
+ };
26
+ // ---------------------------------------------------------------- dev counters (any sticky 2 coin)
27
+ async function devCounters(token) {
28
+ const T = sticky2TokenAbi;
29
+ const [d, bps] = await Promise.all([
30
+ read(token, T, "dev"),
31
+ read(token, T, "devSoldBps"),
32
+ ]);
33
+ return { bought: d[0], sold: d[1], sniperSold: d[2], soldBps: Number(bps), at: { 10: Number(d[3]), 25: Number(d[4]), 50: Number(d[5]), 100: Number(d[6]) } };
34
+ }
35
+ /** The coin's switch rows (one bit per row), the one that is on, and its switcher (the rule auction). */
36
+ async function switches(token) {
37
+ const r = await read(token, sticky2TokenAbi, "switches");
38
+ return { mask: Number(r[0]), on: Number(r[1]), switcher: r[2] };
39
+ }
40
+ // ---------------------------------------------------------------- dev cover
41
+ const C = coverDeskAbi;
42
+ const coverQuote = async (offer, coverWei) => {
43
+ const r = await read(need(a.coverDesk, "dev cover"), C, "quote", [offer, coverWei]);
44
+ return { premium: r[0], fee: r[1], payout: r[2] };
45
+ };
46
+ async function coverPolicy(id) {
47
+ const desk = need(a.coverDesk, "dev cover");
48
+ const [p, claimable] = await Promise.all([read(desk, C, "policyOf", [id]), read(desk, C, "claimable", [id])]);
49
+ const x = p;
50
+ return { id, ...x, offer: BigInt(x.offer), from: Number(x.from), expiry: Number(x.expiry), feeBps: Number(x.feeBps), claimable };
51
+ }
52
+ const coverBuild = {
53
+ /** an offer: `mark` 10/25/50/100, `stopBps` at most mark * 100, duration of each policy in seconds, `until` 0 for no end */
54
+ post: (o) => tx(need(a.coverDesk, "dev cover"), C, "post", [o.token, o.mark, o.premiumBps, o.duration, o.until ?? 0, o.stopBps, o.minCover ?? 0n], o.deposit, "Posting the cover offer"),
55
+ buy: (offer, coverWei, premium) => tx(need(a.coverDesk, "dev cover"), C, "buy", [offer, coverWei], premium, "Buying the policy"),
56
+ claim: (policy) => tx(need(a.coverDesk, "dev cover"), C, "claim", [policy], 0n, "Claiming the cover"),
57
+ end: (policy) => tx(need(a.coverDesk, "dev cover"), C, "end", [policy], 0n, "Ending the policy"),
58
+ close: (offer) => tx(need(a.coverDesk, "dev cover"), C, "close", [offer], 0n, "Closing the offer"),
59
+ };
60
+ const cover = {
61
+ get on() {
62
+ return ON(a.coverDesk);
63
+ },
64
+ desk: a.coverDesk,
65
+ /** the desk's fixed limits and its two fees */
66
+ async terms() {
67
+ const c = await consts(need(a.coverDesk, "dev cover"), C, ["MIN_DURATION", "MAX_DURATION", "WARMUP", "MAX_PREMIUM_BPS", "MIN_DEPOSIT", "MIN_COVER", "premiumFeeBps", "payoutFeeBps"]);
68
+ return {
69
+ minDuration: Number(c.MIN_DURATION),
70
+ maxDuration: Number(c.MAX_DURATION),
71
+ warmup: Number(c.WARMUP),
72
+ maxPremiumBps: Number(c.MAX_PREMIUM_BPS),
73
+ minDeposit: BigInt(c.MIN_DEPOSIT),
74
+ minCover: BigInt(c.MIN_COVER),
75
+ premiumFeeBps: Number(c.premiumFeeBps),
76
+ payoutFeeBps: Number(c.payoutFeeBps),
77
+ };
78
+ },
79
+ /** cover of running policies `who` bought on the coin (the bag cap counts it) */
80
+ coverOf: (token, who) => read(need(a.coverDesk, "dev cover"), C, "coverOf", [token, who]),
81
+ /** one offer */
82
+ async offer(id) {
83
+ const desk = need(a.coverDesk, "dev cover");
84
+ const [o, live] = await Promise.all([read(desk, C, "offerOf", [id]), read(desk, C, "live", [id])]);
85
+ const x = o;
86
+ if (isAddressEqual(x.token, ZERO_ADDRESS))
87
+ throw new PickhooksError("No offer with this id.", { code: "input" });
88
+ const creator = await read(x.token, sticky2TokenAbi, "creator");
89
+ return { id, ...x, mark: Number(x.mark), premiumBps: Number(x.premiumBps), stopBps: Number(x.stopBps), duration: Number(x.duration), until: Number(x.until), bond: isAddressEqual(x.writer, creator), live };
90
+ },
91
+ /** the offers of a coin, newest first */
92
+ async offers(token, n = 50) {
93
+ const desk = need(a.coverDesk, "dev cover");
94
+ const [ids, list] = await read(desk, C, "offersOf", [token, BigInt(n)]);
95
+ if (!ids.length)
96
+ return [];
97
+ const [creator, lives] = await Promise.all([
98
+ read(token, sticky2TokenAbi, "creator"),
99
+ pub.multicall({ contracts: ids.map((id) => ({ address: desk, abi: C, functionName: "live", args: [id] })), allowFailure: true }),
100
+ ]);
101
+ return ids.map((id, i) => {
102
+ const o = list[i];
103
+ return { id, ...o, mark: Number(o.mark), premiumBps: Number(o.premiumBps), stopBps: Number(o.stopBps), duration: Number(o.duration), until: Number(o.until), bond: isAddressEqual(o.writer, creator), live: lives[i].status === "success" && lives[i].result === true };
104
+ });
105
+ },
106
+ quote: coverQuote,
107
+ /** what `who`'s coins are worth now: the most cover it can hold on the coin */
108
+ bagValue: (token, who) => read(need(a.coverDesk, "dev cover"), C, "bagValue", [token, who]),
109
+ bondOf: async (token) => {
110
+ const r = await read(need(a.coverDesk, "dev cover"), C, "bondOf", [token]);
111
+ return { free: r[0], locked: r[1] };
112
+ },
113
+ policy: coverPolicy,
114
+ async policiesOf(holder) {
115
+ const [ids] = await read(need(a.coverDesk, "dev cover"), C, "policyIdsOf", [holder, 0n, 200n]);
116
+ return Promise.all(ids.map((id) => coverPolicy(id)));
117
+ },
118
+ build: coverBuild,
119
+ post: async (o, s = {}) => {
120
+ const sent = await sendTx(ctx, coverBuild.post(o), s);
121
+ const ev = parseEventLogs({ abi: coverDeskAbi, logs: sent.receipt.logs, eventName: "OfferPosted" })[0];
122
+ return { ...sent, id: ev?.args.id ?? 0n };
123
+ },
124
+ /** pays exactly the quoted premium */
125
+ buy: async (offer, coverWei, s = {}) => {
126
+ const q = await coverQuote(offer, coverWei);
127
+ const sent = await sendTx(ctx, coverBuild.buy(offer, coverWei, q.premium), s);
128
+ const ev = parseEventLogs({ abi: coverDeskAbi, logs: sent.receipt.logs, eventName: "PolicyBought" })[0];
129
+ return { ...sent, id: ev?.args.id ?? 0n, premium: q.premium };
130
+ },
131
+ claim: (policy, s = {}) => sendTx(ctx, coverBuild.claim(policy), s),
132
+ end: (policy, s = {}) => sendTx(ctx, coverBuild.end(policy), s),
133
+ };
134
+ // ---------------------------------------------------------------- rule auction
135
+ const R = ruleAuctionAbi;
136
+ const auction = {
137
+ get on() {
138
+ return ON(a.ruleAuction);
139
+ },
140
+ async state(token) {
141
+ const au = need(a.ruleAuction, "the rule auction");
142
+ const [r, settleable] = await Promise.all([
143
+ read(au, R, "state", [token]),
144
+ read(au, R, "settleable", [token]),
145
+ ]);
146
+ return { round: r[0], endsAt: Number(r[1]), burned: [...r[2]], mask: Number(r[3]), on: Number(r[4]), roundSecs: Number(r[5]), started: r[6], settleable };
147
+ },
148
+ /** who burned the most for each game this round (the rule maker if it wins) */
149
+ leaders: async (token, round) => {
150
+ const au = need(a.ruleAuction, "the rule auction");
151
+ return (await pub.multicall({ contracts: Array.from({ length: 8 }, (_, g) => ({ address: au, abi: R, functionName: "leader", args: [token, round, g] })), allowFailure: false }));
152
+ },
153
+ build: {
154
+ burn: (token, game, amount) => tx(need(a.ruleAuction, "the rule auction"), R, "burn", [token, game, amount], 0n, "Burning for the game"),
155
+ settle: (token) => tx(need(a.ruleAuction, "the rule auction"), R, "settle", [token], 0n, "Settling the round"),
156
+ start: (token, roundSecs) => tx(need(a.ruleAuction, "the rule auction"), R, "start", [token, roundSecs], 0n, "Starting the auction"),
157
+ },
158
+ /** approves the coin to the auction if needed, then burns `amount` for `game` (a switch row of the coin) */
159
+ burn: async (token, game, amount, s = {}) => {
160
+ const { address } = await signer(ctx, s.wallet);
161
+ const ap = await approvalFor(ctx, address, token, amount, a.ruleAuction, "Allowing the auction");
162
+ if (ap)
163
+ await sendTx(ctx, ap, s);
164
+ return sendTx(ctx, auction.build.burn(token, game, amount), s);
165
+ },
166
+ settle: (token, s = {}) => sendTx(ctx, auction.build.settle(token), s),
167
+ };
168
+ // ---------------------------------------------------------------- dark launch
169
+ const D = darkRoundAbi;
170
+ const darkCreate = (args, o) => tx(need(a.darkRound, "the dark launch"), D, "create", [{ switcher: ZERO_ADDRESS, switchMask: 0, ...args }, o.roundSecs, o.allocationBps, o.minRaise, o.dripSecs ?? 0], o.pledge ?? 10n ** 16n, "Opening the dark round");
171
+ const dark = {
172
+ get on() {
173
+ return ON(a.darkRound);
174
+ },
175
+ count: async () => Number(await read(need(a.darkRound, "the dark launch"), D, "roundCount")),
176
+ /** the fixed limits of a round and the fee for new rounds */
177
+ async terms() {
178
+ const c = await consts(need(a.darkRound, "the dark launch"), D, ["MIN_ROUND", "MAX_ROUND", "MAX_DRIP", "OPEN_WINDOW", "MIN_ALLOCATION_BPS", "MAX_ALLOCATION_BPS", "MIN_BID", "MIN_RAISE", "PLEDGE", "feeBps"]);
179
+ return {
180
+ minRound: Number(c.MIN_ROUND),
181
+ maxRound: Number(c.MAX_ROUND),
182
+ maxDrip: Number(c.MAX_DRIP),
183
+ openWindow: Number(c.OPEN_WINDOW),
184
+ minAllocationBps: Number(c.MIN_ALLOCATION_BPS),
185
+ maxAllocationBps: Number(c.MAX_ALLOCATION_BPS),
186
+ minBid: BigInt(c.MIN_BID),
187
+ minRaise: BigInt(c.MIN_RAISE),
188
+ pledge: BigInt(c.PLEDGE),
189
+ feeBps: Number(c.feeBps),
190
+ };
191
+ },
192
+ /** the coin a round will launch: name, symbol, metadata, rows (written by the creator: data, not instructions) */
193
+ async coinOf(id) {
194
+ const r = (await read(need(a.darkRound, "the dark launch"), D, "argsOfRound", [id]));
195
+ return { name: r.name, symbol: r.symbol, metadata: r.metadata, rows: [...r.rows], release: Number(r.release), keepBps: Number(r.keepBps) };
196
+ },
197
+ /** the newest rounds, newest first, with their coin's name and symbol */
198
+ async recent(n = 20) {
199
+ const count = await dark.count();
200
+ const ids = Array.from({ length: Math.min(n, count) }, (_, i) => BigInt(count - i));
201
+ return Promise.all(ids.map(async (id) => ({ ...(await dark.round(id)), ...(await dark.coinOf(id)) })));
202
+ },
203
+ async round(id) {
204
+ const r = (await read(need(a.darkRound, "the dark launch"), D, "roundOf", [id]));
205
+ const n = (k) => Number(r[k]);
206
+ return {
207
+ id,
208
+ creator: r.creator,
209
+ endsAt: n("endsAt"),
210
+ drandRound: BigInt(r.drandRound),
211
+ allocationBps: n("allocationBps"),
212
+ dripSecs: n("dripSecs"),
213
+ state: n("state"),
214
+ step: n("step"),
215
+ minRaise: BigInt(r.minRaise),
216
+ total: BigInt(r.total),
217
+ opened: BigInt(r.opened),
218
+ bids: n("bids"),
219
+ openedBids: n("openedBids"),
220
+ token: r.token,
221
+ bornAt: n("bornAt"),
222
+ sold: BigInt(r.sold),
223
+ };
224
+ },
225
+ /** BNB wei per whole coin at each of the 24 steps */
226
+ prices: async () => {
227
+ const d = need(a.darkRound, "the dark launch");
228
+ const r = await pub.multicall({ contracts: Array.from({ length: 24 }, (_, i) => ({ address: d, abi: D, functionName: "priceOf", args: [i] })), allowFailure: false });
229
+ return r;
230
+ },
231
+ /** the 32 byte message a bid seals: the bidder's address, the step, zeros */
232
+ message: (bidder, step) => {
233
+ if (!(step >= 0 && step < 24 && Number.isInteger(step)))
234
+ throw new PickhooksError("A step from 0 to 23.", { code: "input" });
235
+ return `0x${bidder.slice(2).toLowerCase()}${step.toString(16).padStart(2, "0")}${"0".repeat(22)}`;
236
+ },
237
+ owedOf: async (bid) => {
238
+ const r = await read(need(a.darkRound, "the dark launch"), D, "owedOf", [bid]);
239
+ return { coins: r[0], paid: r[1], claimableNow: r[2] };
240
+ },
241
+ /** one bid */
242
+ async bidOf(id) {
243
+ const b = (await read(need(a.darkRound, "the dark launch"), D, "bidOf", [id]));
244
+ return { id, roundId: BigInt(b.round), bidder: b.bidder, deposit: b.deposit, step: Number(b.step) === 255 ? null : Number(b.step), refunded: b.refunded, coinsOwed: b.coinsOwed, coinsClaimed: b.coinsClaimed };
245
+ },
246
+ /** every bid of a round: BNB in, and the step once opened (null before) */
247
+ async bids(roundId) {
248
+ const d = need(a.darkRound, "the dark launch");
249
+ const ids = await read(d, D, "bidIdsOf", [roundId]);
250
+ if (!ids.length)
251
+ return [];
252
+ const r = (await pub.multicall({ contracts: ids.map((id) => ({ address: d, abi: D, functionName: "bidOf", args: [id] })), allowFailure: false }));
253
+ return ids.map((id, i) => ({ id, roundId, bidder: r[i].bidder, deposit: r[i].deposit, step: Number(r[i].step) === 255 ? null : Number(r[i].step), refunded: r[i].refunded, coinsOwed: r[i].coinsOwed, coinsClaimed: r[i].coinsClaimed }));
254
+ },
255
+ /** seals `step` for `bidder` to the round's drand round (loads @pickhooks/sdk/tlock on first use) */
256
+ async seal(roundId, bidder, step) {
257
+ const r = await dark.round(roundId);
258
+ if (r.state !== 0 || Date.now() / 1000 >= r.endsAt)
259
+ throw new PickhooksError("This round takes no more bids.", { code: "input" });
260
+ const TL = await import("./tlock.js");
261
+ const s = TL.encrypt(r.drandRound, dark.message(bidder, step));
262
+ return { u: s.u, v: s.v, w: s.w };
263
+ },
264
+ /**
265
+ * Opening a round's bids once drand signed its round: reads the Bid events, decrypts each unopened bid with drand's
266
+ * signature and returns the open transaction (null while drand has not signed, or nothing is left to open). Anyone
267
+ * may send it; the keeper does.
268
+ */
269
+ async opener(roundId, o = {}) {
270
+ const d = need(a.darkRound, "the dark launch");
271
+ const r = await dark.round(roundId);
272
+ if (r.state !== 0)
273
+ return null;
274
+ const TL = await import("./tlock.js");
275
+ const sig = await TL.fetchRoundSig(r.drandRound);
276
+ if (!sig)
277
+ return null;
278
+ const bidEvent = darkRoundAbi.find((x) => x.type === "event" && x.name === "Bid");
279
+ const logs = await pub.getLogs({ address: d, event: bidEvent, args: { round: roundId }, fromBlock: o.fromBlock ?? a.sticky2StartBlock, toBlock: "latest" });
280
+ const open = new Set((await dark.bids(roundId)).filter((b) => b.step !== null).map((b) => b.id.toString()));
281
+ const ids = [];
282
+ const broken = [];
283
+ const cols = [[], [], [], [], []];
284
+ for (const l of logs) {
285
+ if (open.has(l.args.id.toString()) || ids.length >= (o.max ?? 40))
286
+ continue;
287
+ const x = TL.decrypt(l.args, sig);
288
+ // what the contract accepts: the bid's own bidder, a step of the ladder, zeros; anything else stays unopened
289
+ const m = x && TL.parseDarkMessage(x.message);
290
+ if (!x || !m || !isAddressEqual(m.bidder, l.args.bidder) || m.step >= 24 || !m.clean) {
291
+ broken.push(l.args.id);
292
+ continue;
293
+ }
294
+ ids.push(l.args.id);
295
+ [l.args.u, l.args.v, l.args.w, x.sigma, x.message].forEach((v, i) => cols[i].push(v));
296
+ }
297
+ if (!ids.length)
298
+ return null;
299
+ return { tx: tx(d, D, "open", [ids, ...cols], 0n, `Opening ${ids.length} bids`), ids, broken };
300
+ },
301
+ build: {
302
+ /** opens a round for a coin that does not exist yet (sends the PLEDGE, 0.01 BNB): 2 to 60 minutes of bids, 10 to 30 % of the supply */
303
+ create: darkCreate,
304
+ /** a sealed bid: (u, v, w) is the time-lock encryption of message(bidder, step) to the round's drand round */
305
+ bid: (roundId, sealed, deposit) => tx(need(a.darkRound, "the dark launch"), D, "bid", [roundId, sealed.u, sealed.v, sealed.w], deposit, "Bidding sealed"),
306
+ settle: (roundId, nonce = 0n) => tx(need(a.darkRound, "the dark launch"), D, "settle", [roundId, nonce], 0n, "Settling the round"),
307
+ claim: (bid, maxCoins = 0n) => tx(need(a.darkRound, "the dark launch"), D, "claim", [bid, maxCoins], 0n, "Claiming"),
308
+ },
309
+ /** seals your top step and bids `deposit` BNB on the round; returns the bid id */
310
+ bid: async (o, s = {}) => {
311
+ const { address } = await signer(ctx, s.wallet);
312
+ const sealed = await dark.seal(o.roundId, address, o.step);
313
+ const sent = await sendTx(ctx, dark.build.bid(o.roundId, sealed, o.deposit), s);
314
+ const ev = parseEventLogs({ abi: darkRoundAbi, logs: sent.receipt.logs, eventName: "Bid" })[0];
315
+ return { ...sent, id: ev?.args.id ?? 0n };
316
+ },
317
+ create: async (args, o, s = {}) => {
318
+ const sent = await sendTx(ctx, darkCreate(args, o), s);
319
+ const ev = parseEventLogs({ abi: darkRoundAbi, logs: sent.receipt.logs, eventName: "RoundCreated" })[0];
320
+ return { ...sent, id: ev?.args.id ?? 0n };
321
+ },
322
+ settle: (roundId, s = {}) => sendTx(ctx, dark.build.settle(roundId), s),
323
+ claim: (bid, s = {}) => sendTx(ctx, dark.build.claim(bid), s),
324
+ };
325
+ // ---------------------------------------------------------------- launch desk
326
+ const L = launchDeskAbi;
327
+ const desk = {
328
+ get on() {
329
+ return ON(a.launchDesk);
330
+ },
331
+ account: async (wallet) => {
332
+ const r = await read(need(a.launchDesk, "the launch desk"), L, "accounts", [wallet]);
333
+ return { balance: r[0], maxPerLaunch: r[1], perDay: Number(r[2]), today: Number(r[3]), day: Number(r[4]) };
334
+ },
335
+ build: {
336
+ /** tops up and lets the bot launch for this wallet: at most `maxPerLaunch` per launch, `perDay` a day */
337
+ depositAndAllow: (deposit, maxPerLaunch, perDay) => tx(need(a.launchDesk, "the launch desk"), L, "depositAndAllow", [maxPerLaunch, perDay], deposit, "Topping up the launch desk"),
338
+ allow: (maxPerLaunch, perDay) => tx(need(a.launchDesk, "the launch desk"), L, "allow", [maxPerLaunch, perDay], 0n, "Setting the bot's limits"),
339
+ withdraw: (amount = 0n) => tx(need(a.launchDesk, "the launch desk"), L, "withdraw", [amount], 0n, "Taking BNB out of the launch desk"),
340
+ },
341
+ };
342
+ return { devCounters, switches, cover, auction, dark, desk };
343
+ }