@pickhooks/sdk 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +309 -3
- package/dist/abis/any3.d.ts +2123 -0
- package/dist/abis/any3.js +3 -0
- package/dist/abis/anytax.d.ts +1786 -0
- package/dist/abis/anytax.js +3 -0
- package/dist/abis/calls.d.ts +2515 -0
- package/dist/abis/calls.js +4 -0
- package/dist/abis/common.d.ts +449 -0
- package/dist/abis/common.js +91 -0
- package/dist/abis/dips.d.ts +897 -0
- package/dist/abis/dips.js +2 -0
- package/dist/abis/index.d.ts +12 -0
- package/dist/abis/index.js +12 -0
- package/dist/abis/infra.d.ts +701 -0
- package/dist/abis/infra.js +4 -0
- package/dist/abis/lever.d.ts +1710 -0
- package/dist/abis/lever.js +5 -0
- package/dist/abis/lever2.d.ts +2710 -0
- package/dist/abis/lever2.js +5 -0
- package/dist/abis/pools.d.ts +1898 -0
- package/dist/abis/pools.js +4 -0
- package/dist/abis/sticky.d.ts +953 -0
- package/dist/abis/sticky.js +4 -0
- package/dist/abis/v1.d.ts +1627 -0
- package/dist/abis/v1.js +5 -0
- package/dist/abis/v2.d.ts +3264 -0
- package/dist/abis/v2.js +8 -0
- package/dist/addresses.d.ts +87 -0
- package/dist/addresses.js +62 -0
- package/dist/any.d.ts +447 -0
- package/dist/any.js +729 -0
- package/dist/api.d.ts +62 -0
- package/dist/api.js +30 -0
- package/dist/blocks.d.ts +107 -0
- package/dist/blocks.js +126 -0
- package/dist/coin-rules-config.d.ts +8 -0
- package/dist/coin-rules-config.js +13 -0
- package/dist/coin-rules.d.ts +383 -0
- package/dist/coin-rules.js +878 -0
- package/dist/coins.d.ts +217 -0
- package/dist/coins.js +520 -0
- package/dist/dips.d.ts +133 -0
- package/dist/dips.js +148 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.js +347 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +58 -0
- package/dist/infinity.d.ts +63 -0
- package/dist/infinity.js +96 -0
- package/dist/infra.d.ts +112 -0
- package/dist/infra.js +114 -0
- package/dist/lever.d.ts +391 -0
- package/dist/lever.js +419 -0
- package/dist/pools.d.ts +274 -0
- package/dist/pools.js +405 -0
- package/dist/sticky-rules.d.ts +179 -0
- package/dist/sticky-rules.js +580 -0
- package/dist/sticky.d.ts +216 -0
- package/dist/sticky.js +292 -0
- package/dist/tx.d.ts +70 -0
- package/dist/tx.js +69 -0
- package/dist/v3.d.ts +42 -0
- package/dist/v3.js +98 -0
- package/package.json +58 -4
package/dist/coins.js
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import { encodeFunctionData, isAddressEqual, parseAbi, parseEventLogs } from "viem";
|
|
2
|
+
import { pancakeV2FactoryAbi, v3PoolAbi } from "./abis/common.js";
|
|
3
|
+
import { v1BuySimCode, v1FactoryAbi, v1LockerAbi, v1TokenAbi } from "./abis/v1.js";
|
|
4
|
+
import { arenaAbi, floorVaultAbi, payrollBoxAbi, v2FactoryAbi, v2LockerAbi, v2TokenAbi, voteBoxAbi } from "./abis/v2.js";
|
|
5
|
+
import { ZERO_ADDRESS } from "./addresses.js";
|
|
6
|
+
import { RULE_BY_KIND, buildRules, emptyExtras, kindsOf, launchPots, selectionProblem, worstScanner } from "./coin-rules.js";
|
|
7
|
+
import { PickhooksError, explain, toPickhooksError } from "./errors.js";
|
|
8
|
+
import { blockedKind } from "./infinity.js";
|
|
9
|
+
import { minusBps, sendTx, signer } from "./tx.js";
|
|
10
|
+
import { approvalFor, buildV3Buy, buildV3Sell, receivedOf, v3EstimateBuy, v3PriceBnb, v3Quote } from "./v3.js";
|
|
11
|
+
const simAbi = parseAbi(["function sim(address token, address to, uint256 amount)"]);
|
|
12
|
+
const pancakeV2RouterAbi = parseAbi(["function getAmountsIn(uint256 amountOut, address[] path) view returns (uint256[] amounts)"]);
|
|
13
|
+
const erc20Transfer = parseAbi(["function transfer(address to, uint256 amount) returns (bool)"]);
|
|
14
|
+
const randomSalt = () => {
|
|
15
|
+
const b = new Uint8Array(32);
|
|
16
|
+
globalThis.crypto.getRandomValues(b);
|
|
17
|
+
return `0x${Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("")}`;
|
|
18
|
+
};
|
|
19
|
+
/** A rule's sentence for a refusal: its name and the rule with the coin's numbers. */
|
|
20
|
+
export function ruleText(kind, rules, extras) {
|
|
21
|
+
const def = RULE_BY_KIND.get(kind);
|
|
22
|
+
if (!def)
|
|
23
|
+
return { name: `Rule ${kind}`, text: `Refused by rule ${kind}.` };
|
|
24
|
+
let line = def.line;
|
|
25
|
+
if (rules) {
|
|
26
|
+
try {
|
|
27
|
+
line = def.describe(rules, extras ?? undefined);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
/* a v2 rule read without its extras */
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { name: def.name, text: `Refused by the rule "${def.name}". ${line}` };
|
|
34
|
+
}
|
|
35
|
+
export function coinsModule(ctx) {
|
|
36
|
+
const { pub, a } = ctx;
|
|
37
|
+
const ABI = { 1: v1TokenAbi, 2: v2TokenAbi };
|
|
38
|
+
const FACTORY = {
|
|
39
|
+
1: { address: a.coinsV1Factory, abi: v1FactoryAbi },
|
|
40
|
+
2: { address: a.coinsV2Factory, abi: v2FactoryAbi },
|
|
41
|
+
};
|
|
42
|
+
const versions = new Map();
|
|
43
|
+
const call = (address, abi, functionName, args = []) => pub.readContract({ address, abi, functionName, args });
|
|
44
|
+
/** 2 for a v2 coin, 1 for a v1 coin, null for neither. */
|
|
45
|
+
async function versionOf(token) {
|
|
46
|
+
const hit = versions.get(token.toLowerCase());
|
|
47
|
+
if (hit)
|
|
48
|
+
return hit;
|
|
49
|
+
for (const v of [2, 1]) {
|
|
50
|
+
const is = await call(FACTORY[v].address, FACTORY[v].abi, "isCoin", [token]).catch(() => false);
|
|
51
|
+
if (is) {
|
|
52
|
+
versions.set(token.toLowerCase(), v);
|
|
53
|
+
return v;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
async function need(token) {
|
|
59
|
+
const v = await versionOf(token);
|
|
60
|
+
if (!v)
|
|
61
|
+
throw new PickhooksError("This is not a pickhooks coin with rules in the coin.", { code: "input" });
|
|
62
|
+
return v;
|
|
63
|
+
}
|
|
64
|
+
/** How many coins a factory launched. */
|
|
65
|
+
const count = async (version = 2) => Number(await call(FACTORY[version].address, FACTORY[version].abi, "coinCount"));
|
|
66
|
+
async function tokensOf(version, limit, offset) {
|
|
67
|
+
const n = await count(version);
|
|
68
|
+
const end = Math.max(0, n - offset);
|
|
69
|
+
const from = Math.max(0, end - limit);
|
|
70
|
+
if (end <= from)
|
|
71
|
+
return [];
|
|
72
|
+
const list = (await pub.multicall({
|
|
73
|
+
contracts: Array.from({ length: end - from }, (_, i) => ({ address: FACTORY[version].address, abi: FACTORY[version].abi, functionName: "coins", args: [BigInt(end - 1 - i)] })),
|
|
74
|
+
allowFailure: false,
|
|
75
|
+
}));
|
|
76
|
+
for (const t of list)
|
|
77
|
+
versions.set(t.toLowerCase(), version);
|
|
78
|
+
return list;
|
|
79
|
+
}
|
|
80
|
+
/** Coins newest first: `limit` of each version (v2 and v1 merged by launch time, or one version). */
|
|
81
|
+
async function coins(o = {}) {
|
|
82
|
+
const vs = o.version ? [o.version] : [2, 1];
|
|
83
|
+
const lists = await Promise.all(vs.map(async (v) => summaries(v, await tokensOf(v, o.limit ?? 50, o.offset ?? 0))));
|
|
84
|
+
return lists.flat().sort((x, y) => y.start - x.start);
|
|
85
|
+
}
|
|
86
|
+
/** One coin, or null when it is not one of the two factories' coins. */
|
|
87
|
+
async function coin(token) {
|
|
88
|
+
const v = await versionOf(token);
|
|
89
|
+
if (!v)
|
|
90
|
+
return null;
|
|
91
|
+
return (await summaries(v, [token]))[0] ?? null;
|
|
92
|
+
}
|
|
93
|
+
const FIELDS = ["name", "symbol", "metadata", "creator", "pool", "pool2", "start", "rules", "stats", "curveSupply", "jackpotPot", "jackpotLast", "jackpotTime", "rewardsPot"];
|
|
94
|
+
async function summaries(version, tokens) {
|
|
95
|
+
if (!tokens.length)
|
|
96
|
+
return [];
|
|
97
|
+
const abi = ABI[version];
|
|
98
|
+
const per = FIELDS.length;
|
|
99
|
+
const [res, head] = await Promise.all([
|
|
100
|
+
pub.multicall({ contracts: tokens.flatMap((t) => FIELDS.map((functionName) => ({ address: t, abi, functionName }))), allowFailure: true }),
|
|
101
|
+
pub.getBlock({ blockTag: "latest" }),
|
|
102
|
+
]);
|
|
103
|
+
const now = Number(head.timestamp);
|
|
104
|
+
const pools = tokens.map((_, i) => res[i * per + 4].result);
|
|
105
|
+
const masks = tokens.map((_, i) => (res[i * per + 7].result?.mask ?? 0n));
|
|
106
|
+
const kindsAll = masks.map((m) => kindsOf(m));
|
|
107
|
+
const [slots, untils, offs] = await Promise.all([
|
|
108
|
+
pub.multicall({ contracts: pools.map((p) => ({ address: (p ?? a.wbnb), abi: v3PoolAbi, functionName: "slot0" })), allowFailure: true }),
|
|
109
|
+
pub.multicall({
|
|
110
|
+
contracts: tokens.flatMap((t, i) => kindsAll[i].map((k) => ({ address: t, abi, functionName: "until", args: [k] }))),
|
|
111
|
+
allowFailure: true,
|
|
112
|
+
}),
|
|
113
|
+
version === 2 ? pub.multicall({ contracts: tokens.map((t) => ({ address: t, abi, functionName: "off" })), allowFailure: true }) : Promise.resolve([]),
|
|
114
|
+
]);
|
|
115
|
+
const extrasAll = version === 2 ? await Promise.all(tokens.map((t, i) => extrasOf(t, masks[i]).catch(() => null))) : tokens.map(() => null);
|
|
116
|
+
const out = [];
|
|
117
|
+
let u = 0;
|
|
118
|
+
tokens.forEach((token, i) => {
|
|
119
|
+
const g = (k) => res[i * per + k];
|
|
120
|
+
const kinds = kindsAll[i];
|
|
121
|
+
const untilOf = kinds.map(() => untils[u++]);
|
|
122
|
+
if (g(0).status !== "success" || !pools[i] || g(7).status !== "success")
|
|
123
|
+
return;
|
|
124
|
+
const rules = g(7).result;
|
|
125
|
+
const stats = g(8).result;
|
|
126
|
+
const curve = g(9).result ?? 0n;
|
|
127
|
+
const off = version === 2 && offs[i]?.status === "success" ? kindsOf(offs[i].result) : [];
|
|
128
|
+
const x = extrasAll[i];
|
|
129
|
+
const sl = slots[i];
|
|
130
|
+
const priceBnb = sl.status === "success" ? v3PriceBnb(sl.result[0], token, a.wbnb) : 0;
|
|
131
|
+
out.push({
|
|
132
|
+
token,
|
|
133
|
+
version,
|
|
134
|
+
name: String(g(0).result ?? ""),
|
|
135
|
+
symbol: String(g(1).result ?? ""),
|
|
136
|
+
metadata: String(g(2).result ?? ""),
|
|
137
|
+
creator: g(3).result,
|
|
138
|
+
pool: pools[i],
|
|
139
|
+
pair: g(5).result ?? ZERO_ADDRESS,
|
|
140
|
+
start: Number(g(6).result ?? 0),
|
|
141
|
+
rules: kinds.map((kind, j) => {
|
|
142
|
+
const def = RULE_BY_KIND.get(kind);
|
|
143
|
+
const until = untilOf[j]?.status === "success" ? Number(untilOf[j].result) : 0;
|
|
144
|
+
let text = def?.line ?? "";
|
|
145
|
+
try {
|
|
146
|
+
if (def)
|
|
147
|
+
text = def.describe(rules, x ?? undefined);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
/* keep the line */
|
|
151
|
+
}
|
|
152
|
+
return { kind, name: def?.name ?? `Rule ${kind}`, text, scanner: def?.scanner.level ?? "green", until, ended: off.includes(kind) || (until !== 0 && now >= until) };
|
|
153
|
+
}),
|
|
154
|
+
scanner: worstScanner(kinds),
|
|
155
|
+
raw: rules,
|
|
156
|
+
extras: x,
|
|
157
|
+
holders: Number(stats[0]),
|
|
158
|
+
buys: Number(stats[1]),
|
|
159
|
+
sells: Number(stats[2]),
|
|
160
|
+
soldBps: curve ? Number((stats[11] * 10000n) / curve) : 0,
|
|
161
|
+
jackpotPot: g(10).result ?? 0n,
|
|
162
|
+
jackpotLast: g(11).result ?? ZERO_ADDRESS,
|
|
163
|
+
jackpotTime: Number(g(12).result ?? 0),
|
|
164
|
+
rewardsPot: g(13).result ?? 0n,
|
|
165
|
+
priceBnb,
|
|
166
|
+
mcapBnb: priceBnb * 1e9,
|
|
167
|
+
chainNow: now,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
let termsCache = null;
|
|
173
|
+
/** The v2 factory's terms and its companions (vote box, duel arena, floor vault, payroll box, locker). */
|
|
174
|
+
function terms() {
|
|
175
|
+
termsCache ??= (async () => {
|
|
176
|
+
const fns = ["hooksBurn", "creatorBps", "seedBnb", "hooks", "voteBox", "arena", "floorVault", "payrollBox", "locker"];
|
|
177
|
+
const r = await pub.multicall({ contracts: fns.map((functionName) => ({ address: a.coinsV2Factory, abi: FACTORY[2].abi, functionName })), allowFailure: false });
|
|
178
|
+
return { hooksBurn: r[0], creatorBps: Number(r[1]), seedBnb: r[2], hooks: r[3], voteBox: r[4], arena: r[5], floorVault: r[6], payrollBox: r[7], locker: r[8] };
|
|
179
|
+
})().catch((e) => {
|
|
180
|
+
termsCache = null;
|
|
181
|
+
throw e;
|
|
182
|
+
});
|
|
183
|
+
return termsCache;
|
|
184
|
+
}
|
|
185
|
+
/** The v2 settings a coin launched with: what its v2 rules' sentences and buttons need. */
|
|
186
|
+
async function extrasOf(token, mask) {
|
|
187
|
+
const has = (k) => (mask & (1n << BigInt(k))) !== 0n;
|
|
188
|
+
const fns = ["buyoutMask", "votable", "quorumBps", "voteSeconds", "feed", "target", "sellsAtSeconds", "deadManSeconds", "payrollMin"];
|
|
189
|
+
const r = await pub.multicall({ contracts: fns.map((functionName) => ({ address: token, abi: ABI[2], functionName })), allowFailure: false });
|
|
190
|
+
const x = {
|
|
191
|
+
...emptyExtras(),
|
|
192
|
+
buyoutMask: r[0],
|
|
193
|
+
votable: r[1],
|
|
194
|
+
quorumBps: Number(r[2]),
|
|
195
|
+
voteSeconds: Number(r[3]),
|
|
196
|
+
feed: r[4],
|
|
197
|
+
target: r[5],
|
|
198
|
+
sellsAtSeconds: Number(r[6]),
|
|
199
|
+
deadManSeconds: Number(r[7]),
|
|
200
|
+
payrollMin: r[8],
|
|
201
|
+
};
|
|
202
|
+
const buyable = kindsOf(x.buyoutMask);
|
|
203
|
+
if (buyable.length) {
|
|
204
|
+
const prices = (await pub.multicall({ contracts: buyable.map((k) => ({ address: token, abi: ABI[2], functionName: "buyoutPrice", args: [k] })), allowFailure: false }));
|
|
205
|
+
x.buyoutPrice = [0, 1, 2, 3, 4, 5, 6, 7].map((j) => prices[j] ?? 0n);
|
|
206
|
+
}
|
|
207
|
+
if (has(41) || has(45) || has(46)) {
|
|
208
|
+
const t = await terms();
|
|
209
|
+
if (has(41)) {
|
|
210
|
+
const id = await call(t.arena, arenaAbi, "duelOf", [token]);
|
|
211
|
+
if (id > 0n) {
|
|
212
|
+
const d = await call(t.arena, arenaAbi, "get", [id]);
|
|
213
|
+
x.duelStake = d.stake;
|
|
214
|
+
x.duelOpponent = isAddressEqual(d.b, token) ? d.a : ZERO_ADDRESS;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (has(45)) {
|
|
218
|
+
const f = await call(t.floorVault, floorVaultAbi, "floors", [token]);
|
|
219
|
+
x.floorDeposit = f[0];
|
|
220
|
+
x.floorBps = Number(f[2]);
|
|
221
|
+
}
|
|
222
|
+
if (has(46)) {
|
|
223
|
+
const c = await call(t.payrollBox, payrollBoxAbi, "coins", [token]);
|
|
224
|
+
x.shiftSeconds = Number(c[5]);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return x;
|
|
228
|
+
}
|
|
229
|
+
// ------------------------------------------------ the dry runs
|
|
230
|
+
/**
|
|
231
|
+
* v2 coins count a transfer with the V3 pool as a trade only when the pool's price moved since the last counted trade,
|
|
232
|
+
* so the dry runs nudge the pool's slot0 by one through a state override, like a swap would.
|
|
233
|
+
*/
|
|
234
|
+
async function nudged(pool) {
|
|
235
|
+
const slot0 = await pub.getStorageAt({ address: pool, slot: "0x0" });
|
|
236
|
+
const value = `0x${(BigInt(slot0 ?? "0x0") + 1n).toString(16).padStart(64, "0")}`;
|
|
237
|
+
return [{ address: pool, stateDiff: [{ slot: `0x${"0".repeat(64)}`, value }] }];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Would this trade go through: the coin's own transfer of the trade in an eth_call (a buy: the coins leaving the pool
|
|
241
|
+
* to the wallet, a sell: the wallet's coins into the pool, a send: wallet to `to`). For a buy pass the coins it would get
|
|
242
|
+
* (quote()). The refusal names the rule with the coin's numbers.
|
|
243
|
+
*/
|
|
244
|
+
async function check(c) {
|
|
245
|
+
const v = await need(c.token);
|
|
246
|
+
const abi = ABI[v];
|
|
247
|
+
const pool = await call(c.token, abi, "pool");
|
|
248
|
+
const transfer = (to, amount) => encodeFunctionData({ abi: erc20Transfer, functionName: "transfer", args: [to, amount] });
|
|
249
|
+
try {
|
|
250
|
+
if (c.side === "send") {
|
|
251
|
+
await pub.call({ account: c.wallet, to: c.token, data: transfer(c.to ?? ZERO_ADDRESS, c.amount) });
|
|
252
|
+
}
|
|
253
|
+
else if (v === 2) {
|
|
254
|
+
const override = await nudged(pool);
|
|
255
|
+
if (c.side === "buy")
|
|
256
|
+
await pub.call({ account: pool, to: c.token, data: transfer(c.wallet, c.amount), stateOverride: override });
|
|
257
|
+
else
|
|
258
|
+
await pub.call({ account: c.wallet, to: c.token, data: transfer(pool, c.amount), stateOverride: override });
|
|
259
|
+
}
|
|
260
|
+
else if (c.side === "buy") {
|
|
261
|
+
// v1: a tiny forwarder on the pool's address moves the coins out exactly as a real buy would (msg.sender = pool,
|
|
262
|
+
// tx.origin = buyer), for this one eth_call
|
|
263
|
+
await pub.call({
|
|
264
|
+
account: c.wallet,
|
|
265
|
+
to: pool,
|
|
266
|
+
data: encodeFunctionData({ abi: simAbi, functionName: "sim", args: [c.token, c.wallet, c.amount] }),
|
|
267
|
+
stateOverride: [{ address: pool, code: v1BuySimCode }],
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
await pub.call({ account: c.wallet, to: c.token, data: transfer(pool, c.amount) });
|
|
272
|
+
}
|
|
273
|
+
return { ok: true };
|
|
274
|
+
}
|
|
275
|
+
catch (e) {
|
|
276
|
+
const kind = blockedKind(e);
|
|
277
|
+
if (kind === null)
|
|
278
|
+
return { ok: false, kind: 0, name: "", text: explain(e, "The coin refused this transfer.").text };
|
|
279
|
+
const rules = await call(c.token, abi, "rules");
|
|
280
|
+
const x = v === 2 ? await extrasOf(c.token, rules.mask).catch(() => null) : null;
|
|
281
|
+
return { ok: false, kind, ...ruleText(kind, rules, x) };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/** a contract refusal of one of these coins as a sentence that names the rule */
|
|
285
|
+
async function refusalOf(token, e, fallback) {
|
|
286
|
+
const kind = blockedKind(e);
|
|
287
|
+
if (kind === null)
|
|
288
|
+
return toPickhooksError(e, fallback);
|
|
289
|
+
const v = await need(token);
|
|
290
|
+
const rules = await call(token, ABI[v], "rules").catch(() => null);
|
|
291
|
+
const r = ruleText(kind, rules);
|
|
292
|
+
return new PickhooksError(r.text, { code: "Blocked", args: [kind], cause: e });
|
|
293
|
+
}
|
|
294
|
+
/** Coins a buy of `bnbIn` would get, estimated from the pool's liquidity at its price (see v3EstimateBuy); not a minOut. */
|
|
295
|
+
async function estimateBuy(token, bnbIn) {
|
|
296
|
+
const pool = await call(token, ABI[await need(token)], "pool");
|
|
297
|
+
return v3EstimateBuy(ctx, pool, token, bnbIn);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* What a trade gets in the V3 pool, from PancakeSwap's QuoterV2. A buy a rule refuses fails in the quoter without the
|
|
301
|
+
* reason, so it is checked again with an estimate of its coins and the refusal names the rule (for `wallet`, by default
|
|
302
|
+
* the quoter the quote ran as).
|
|
303
|
+
*/
|
|
304
|
+
async function quote(q) {
|
|
305
|
+
try {
|
|
306
|
+
return { amountOut: await v3Quote(ctx, q) };
|
|
307
|
+
}
|
|
308
|
+
catch (e) {
|
|
309
|
+
if (q.side === "buy" && blockedKind(e) === null) {
|
|
310
|
+
const est = await estimateBuy(q.token, q.amount).catch(() => 0n);
|
|
311
|
+
const c = est > 0n ? await check({ token: q.token, side: "buy", wallet: q.wallet ?? a.v3Quoter, amount: est }).catch(() => null) : null;
|
|
312
|
+
if (c && !c.ok && c.kind > 0)
|
|
313
|
+
throw new PickhooksError(c.text, { code: "Blocked", args: [c.kind], cause: e });
|
|
314
|
+
}
|
|
315
|
+
throw await refusalOf(q.token, e, "The quoter refused this trade.");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const refuse = (c) => {
|
|
319
|
+
if (!c.ok)
|
|
320
|
+
throw new PickhooksError(c.text, { code: "Blocked", args: [c.kind] });
|
|
321
|
+
};
|
|
322
|
+
/** Quotes, checks the coin's rules for this wallet, and buys through PancakeSwap V3. A refusing rule stops it before anything is signed. */
|
|
323
|
+
async function buy(b, s = {}) {
|
|
324
|
+
const { address } = await signer(ctx, s.wallet);
|
|
325
|
+
const q = await quote({ token: b.token, side: "buy", amount: b.bnbIn, wallet: address });
|
|
326
|
+
refuse(await check({ token: b.token, side: "buy", wallet: address, amount: q.amountOut }));
|
|
327
|
+
const minOut = b.minOut ?? minusBps(q.amountOut, b.slippageBps ?? 100);
|
|
328
|
+
try {
|
|
329
|
+
const sent = await sendTx(ctx, buildV3Buy(a, { token: b.token, bnbIn: b.bnbIn, minOut, recipient: address, deadline: b.deadline, gasPct: 150 }), s);
|
|
330
|
+
return { ...sent, quoted: q.amountOut, minOut, amountOut: receivedOf(sent.receipt, b.token, address) };
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
throw await refusalOf(b.token, e, "The buy would fail.");
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/** Checks the rules, approves the V3 router if needed, quotes and sells for BNB. */
|
|
337
|
+
async function sell(x, s = {}) {
|
|
338
|
+
const { address } = await signer(ctx, s.wallet);
|
|
339
|
+
refuse(await check({ token: x.token, side: "sell", wallet: address, amount: x.amount }));
|
|
340
|
+
const ap = await approvalFor(ctx, address, x.token, x.amount, a.v3Router, "Allowing PancakeSwap");
|
|
341
|
+
if (ap)
|
|
342
|
+
await sendTx(ctx, ap, s);
|
|
343
|
+
const q = await quote({ token: x.token, side: "sell", amount: x.amount });
|
|
344
|
+
const minOut = x.minOut ?? minusBps(q.amountOut, x.slippageBps ?? 100);
|
|
345
|
+
try {
|
|
346
|
+
const sent = await sendTx(ctx, buildV3Sell(a, { token: x.token, amount: x.amount, minOut, recipient: address, deadline: x.deadline, gasPct: 150 }), s);
|
|
347
|
+
return { ...sent, quoted: q.amountOut, minOut };
|
|
348
|
+
}
|
|
349
|
+
catch (e) {
|
|
350
|
+
throw await refusalOf(x.token, e, "The sell would fail.");
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// ------------------------------------------------ launch (v2 factory)
|
|
354
|
+
/** $hooks the launch burns for these rules, and the BNB the factory needs to buy them (quote + 3 % tax + 5 % room). */
|
|
355
|
+
async function burnFor(mask) {
|
|
356
|
+
const hooks = await call(a.coinsV2Factory, FACTORY[2].abi, "burnFor", [mask]);
|
|
357
|
+
if (hooks === 0n)
|
|
358
|
+
return { hooks, bnb: 0n };
|
|
359
|
+
const want = (hooks * 10000n) / 9500n;
|
|
360
|
+
const [bnbIn] = await call(a.v2Router, pancakeV2RouterAbi, "getAmountsIn", [want, [a.wbnb, a.hooks]]);
|
|
361
|
+
return { hooks, bnb: (bnbIn * 105n) / 100n };
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Launches a coin with up to eight rules on the v2 factory: checks the selection like the factory, builds the Rules and
|
|
365
|
+
* Extras structs, pays the $hooks burn of hooks rules (in BNB or $hooks), sends the seed, the deposits (duel stake,
|
|
366
|
+
* floor, payroll) and the first buy, then funds a jackpot or rewards pot the rules ask for. Gas: estimate + 5 %, never
|
|
367
|
+
* above BNB Chain's cap (a launch needs about 14.4M).
|
|
368
|
+
*/
|
|
369
|
+
async function launch(l, s = {}) {
|
|
370
|
+
const problem = selectionProblem(l.rules);
|
|
371
|
+
if (problem)
|
|
372
|
+
throw new PickhooksError(problem, { code: "input" });
|
|
373
|
+
const { address } = await signer(ctx, s.wallet);
|
|
374
|
+
const now = Number((await pub.getBlock({ blockTag: "latest" })).timestamp);
|
|
375
|
+
const built = buildRules(l.rules, now, l.keepBps ?? 10_000);
|
|
376
|
+
const t = await terms();
|
|
377
|
+
const burn = await burnFor(built.rules.mask);
|
|
378
|
+
const payInHooks = burn.hooks > 0n && l.payBurnIn === "hooks";
|
|
379
|
+
if (payInHooks) {
|
|
380
|
+
const ap = await approvalFor(ctx, address, a.hooks, burn.hooks, a.coinsV2Factory, "Allowing the factory to burn $hooks");
|
|
381
|
+
if (ap)
|
|
382
|
+
await sendTx(ctx, ap, s);
|
|
383
|
+
}
|
|
384
|
+
const burnBnb = burn.hooks > 0n && !payInHooks ? burn.bnb : 0n;
|
|
385
|
+
const deposits = await call(a.coinsV2Factory, FACTORY[2].abi, "depositsFor", [built.rules.mask, built.extras, burnBnb]);
|
|
386
|
+
const args = {
|
|
387
|
+
name: l.name,
|
|
388
|
+
symbol: l.symbol,
|
|
389
|
+
metadata: l.metadata,
|
|
390
|
+
rules: built.rules,
|
|
391
|
+
untilIds: built.untilIds,
|
|
392
|
+
untilTimes: built.untilTimes,
|
|
393
|
+
salt: l.salt ?? randomSalt(),
|
|
394
|
+
extras: built.extras,
|
|
395
|
+
burnBnb,
|
|
396
|
+
maxBurn: burn.hooks,
|
|
397
|
+
minCreatorBps: t.creatorBps,
|
|
398
|
+
};
|
|
399
|
+
const tx = { to: a.coinsV2Factory, value: (l.devBuy ?? 0n) + deposits, data: encodeFunctionData({ abi: v2FactoryAbi, functionName: "launch", args: [args] }), gasPct: 105, label: "The launch" };
|
|
400
|
+
const sent = await sendTx(ctx, tx, s);
|
|
401
|
+
const ev = parseEventLogs({ abi: v2FactoryAbi, logs: sent.receipt.logs.filter((x) => isAddressEqual(x.address, a.coinsV2Factory)) });
|
|
402
|
+
const token = ev.find((e) => e.eventName === "Launched")?.args.token;
|
|
403
|
+
if (!token)
|
|
404
|
+
throw new PickhooksError("Launched, but the coin was not found in the receipt.", { code: "receipt", hash: sent.hash });
|
|
405
|
+
versions.set(token.toLowerCase(), 2);
|
|
406
|
+
const pots = [];
|
|
407
|
+
const p = launchPots(l.rules);
|
|
408
|
+
if (p.jackpot > 0)
|
|
409
|
+
pots.push(await fundPot(token, "jackpot", BigInt(Math.round(p.jackpot * 1e9)) * 10n ** 9n, s));
|
|
410
|
+
if (p.rewards > 0)
|
|
411
|
+
pots.push(await fundPot(token, "rewards", BigInt(Math.round(p.rewards * 1e9)) * 10n ** 9n, s));
|
|
412
|
+
return { ...sent, token, burned: ev.find((e) => e.eventName === "HooksBurned")?.args.amount ?? 0n, pots };
|
|
413
|
+
}
|
|
414
|
+
// ------------------------------------------------ pots, claims, fees, v2 games
|
|
415
|
+
const tokenTx = async (token, fn, args = [], value = 0n, label) => {
|
|
416
|
+
const v = await need(token);
|
|
417
|
+
return { to: token, value, data: encodeFunctionData({ abi: ABI[v], functionName: fn, args }), gasPct: 150, label };
|
|
418
|
+
};
|
|
419
|
+
const at = (to, abi, fn, args = [], value = 0n, label) => ({
|
|
420
|
+
to,
|
|
421
|
+
value,
|
|
422
|
+
data: encodeFunctionData({ abi, functionName: fn, args }),
|
|
423
|
+
gasPct: 150,
|
|
424
|
+
label,
|
|
425
|
+
});
|
|
426
|
+
/** Funds a coin's jackpot or buyer rewards pot. */
|
|
427
|
+
async function fundPot(token, pot, wei, s = {}) {
|
|
428
|
+
const v = await need(token);
|
|
429
|
+
const fn = pot === "rewards" ? "fundRewards" : v === 2 ? "fundPot" : "fundJackpot";
|
|
430
|
+
return sendTx(ctx, await tokenTx(token, fn, [], wei, `Funding the ${pot}`), s);
|
|
431
|
+
}
|
|
432
|
+
async function lockerOf(token) {
|
|
433
|
+
const v = await need(token);
|
|
434
|
+
const address = v === 2 ? (await terms()).locker : await call(a.coinsV1Factory, FACTORY[1].abi, "locker");
|
|
435
|
+
return { address, abi: (v === 2 ? v2LockerAbi : v1LockerAbi) };
|
|
436
|
+
}
|
|
437
|
+
const build = {
|
|
438
|
+
buy: (b) => buildV3Buy(a, { ...b, gasPct: 150 }),
|
|
439
|
+
sell: (x) => buildV3Sell(a, { ...x, gasPct: 150 }),
|
|
440
|
+
/** jackpot wins and buyer rewards the coin owes the caller */
|
|
441
|
+
claim: (token) => tokenTx(token, "claim", [], 0n, "The claim"),
|
|
442
|
+
settleJackpot: (token) => tokenTx(token, "settleJackpot", [], 0n, "Settling the jackpot"),
|
|
443
|
+
/** the creator takes back rewards it paid in and nobody earned */
|
|
444
|
+
withdrawRewards: (token, wei) => tokenTx(token, "withdrawRewards", [wei], 0n, "Taking rewards back"),
|
|
445
|
+
/** anyone: collects the coin's trading fees from its locked position (creator, pot, platform; coins burned or into the pair) */
|
|
446
|
+
collect: async (token) => {
|
|
447
|
+
const l = await lockerOf(token);
|
|
448
|
+
return at(l.address, l.abi, "collect", [token], 0n, "The collect");
|
|
449
|
+
},
|
|
450
|
+
/** v2: buys a listed rule off (half to the creator, half burns $hooks); the price is the coin's buyoutPrice(kind) */
|
|
451
|
+
buyout: async (token, kind) => tokenTx(token, "buyout", [kind], await call(token, ABI[2], "buyoutPrice", [kind]), "Buying the rule off"),
|
|
452
|
+
/** v2: moves the milestone level and sells at along without a trade (anyone) */
|
|
453
|
+
poke: (token) => tokenTx(token, "poke", [], 0n, "Poke"),
|
|
454
|
+
/** v2 dead man: the creator says it is still here; release: anyone burns the bag of a quiet creator */
|
|
455
|
+
alive: (token) => tokenTx(token, "alive", [], 0n, "Still here"),
|
|
456
|
+
release: (token) => tokenTx(token, "release", [], 0n, "Release"),
|
|
457
|
+
fundPayroll: (token, wei) => tokenTx(token, "fundPayroll", [], wei, "Funding the payroll"),
|
|
458
|
+
payrollClaim: async (token) => at((await terms()).payrollBox, payrollBoxAbi, "claim", [token], 0n, "The payroll claim"),
|
|
459
|
+
payShift: async (token) => at((await terms()).payrollBox, payrollBoxAbi, "payShift", [token], 0n, "Paying the shift"),
|
|
460
|
+
floorDefend: async (token) => at((await terms()).floorVault, floorVaultAbi, "defend", [token], 0n, "Defending the floor"),
|
|
461
|
+
duelClose: async (id) => at((await terms()).arena, arenaAbi, "close", [id], 0n, "Closing the duel"),
|
|
462
|
+
duelSettle: async (id) => at((await terms()).arena, arenaAbi, "settle", [id], 0n, "Settling the duel"),
|
|
463
|
+
duelRefund: async (id) => at((await terms()).arena, arenaAbi, "refund", [id], 0n, "Refunding the duel"),
|
|
464
|
+
/** votes move coins into the vote box: approve the coin to the box first (terms().voteBox) */
|
|
465
|
+
voteStart: async (token, kind, amount) => at((await terms()).voteBox, voteBoxAbi, "start", [token, kind, amount], 0n, "Opening a vote"),
|
|
466
|
+
voteCast: async (token, kind, amount, yes) => at((await terms()).voteBox, voteBoxAbi, "vote", [token, kind, amount, yes], 0n, "The vote"),
|
|
467
|
+
voteFinish: async (token, kind) => at((await terms()).voteBox, voteBoxAbi, "finish", [token, kind], 0n, "Finishing the vote"),
|
|
468
|
+
voteWithdraw: async (token, kind, round) => at((await terms()).voteBox, voteBoxAbi, "withdraw", [token, kind, round, 0n], 0n, "Taking the vote coins back"),
|
|
469
|
+
};
|
|
470
|
+
return {
|
|
471
|
+
v1Factory: a.coinsV1Factory,
|
|
472
|
+
v2Factory: a.coinsV2Factory,
|
|
473
|
+
versionOf,
|
|
474
|
+
count,
|
|
475
|
+
coins,
|
|
476
|
+
coin,
|
|
477
|
+
terms,
|
|
478
|
+
extrasOf,
|
|
479
|
+
check,
|
|
480
|
+
quote,
|
|
481
|
+
estimateBuy,
|
|
482
|
+
burnFor,
|
|
483
|
+
build,
|
|
484
|
+
buy,
|
|
485
|
+
sell,
|
|
486
|
+
launch,
|
|
487
|
+
fundPot,
|
|
488
|
+
/** a wallet's jackpot wins and buyer rewards in a coin, and its counters there */
|
|
489
|
+
wallet: async (token, who) => {
|
|
490
|
+
const v = await need(token);
|
|
491
|
+
const r = await pub.multicall({
|
|
492
|
+
contracts: [
|
|
493
|
+
{ address: token, abi: ABI[v], functionName: "balanceOf", args: [who] },
|
|
494
|
+
{ address: token, abi: ABI[v], functionName: "owed", args: [who] },
|
|
495
|
+
{ address: token, abi: ABI[v], functionName: "wallets", args: [who] },
|
|
496
|
+
],
|
|
497
|
+
allowFailure: false,
|
|
498
|
+
});
|
|
499
|
+
const w = r[2];
|
|
500
|
+
return { balance: r[0], owed: r[1], buys: Number(w[0]), lastBuy: Number(w[1]), lastTrade: Number(w[2]), jailUntil: Number(w[3]) };
|
|
501
|
+
},
|
|
502
|
+
/** what collect() would book now: BNB fees and coins burned (simulated) */
|
|
503
|
+
pendingFees: async (token) => {
|
|
504
|
+
const l = await lockerOf(token);
|
|
505
|
+
const r = await pub.simulateContract({ address: l.address, abi: l.abi, functionName: "collect", args: [token] });
|
|
506
|
+
const [bnb, burned] = r.result;
|
|
507
|
+
return { bnb, burned };
|
|
508
|
+
},
|
|
509
|
+
claim: async (token, s = {}) => sendTx(ctx, await build.claim(token), s),
|
|
510
|
+
settleJackpot: async (token, s = {}) => sendTx(ctx, await build.settleJackpot(token), s),
|
|
511
|
+
withdrawRewards: async (token, wei, s = {}) => sendTx(ctx, await build.withdrawRewards(token, wei), s),
|
|
512
|
+
collect: async (token, s = {}) => sendTx(ctx, await build.collect(token), s),
|
|
513
|
+
buyout: async (token, kind, s = {}) => sendTx(ctx, await build.buyout(token, kind), s),
|
|
514
|
+
poke: async (token, s = {}) => sendTx(ctx, await build.poke(token), s),
|
|
515
|
+
alive: async (token, s = {}) => sendTx(ctx, await build.alive(token), s),
|
|
516
|
+
release: async (token, s = {}) => sendTx(ctx, await build.release(token), s),
|
|
517
|
+
/** the coin's PancakeSwap V2 pair against WBNB */
|
|
518
|
+
pairOf: (token) => call(a.v2Factory, pancakeV2FactoryAbi, "getPair", [token, a.wbnb]),
|
|
519
|
+
};
|
|
520
|
+
}
|
package/dist/dips.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { type Address } from "viem";
|
|
2
|
+
import { type Ctx, type SendOptions, type Sent, type TxRequest } from "./tx.js";
|
|
3
|
+
/**
|
|
4
|
+
* Dip bids (desk 0xc73b…65bf, one desk for every coin, no oracle, no pool touched): a dip buyer locks BNB at their price
|
|
5
|
+
* (the strike) and names a premium; a holder pays that premium for a floor, the right to hand coins in at the strike
|
|
6
|
+
* until it runs out. Pump: the holder keeps the coins. Dump: the coins go to the dip buyer, the holder takes the BNB.
|
|
7
|
+
* Prices: a strike is BNB wei per whole coin (1e18 units).
|
|
8
|
+
*/
|
|
9
|
+
export type DipBid = {
|
|
10
|
+
id: bigint;
|
|
11
|
+
maker: Address;
|
|
12
|
+
token: Address;
|
|
13
|
+
/** BNB wei per whole coin */
|
|
14
|
+
strike: bigint;
|
|
15
|
+
/** how long a floor bought on it runs, seconds */
|
|
16
|
+
duration: number;
|
|
17
|
+
premiumBps: number;
|
|
18
|
+
/** the bid takes new floors until then */
|
|
19
|
+
until: number;
|
|
20
|
+
closed: boolean;
|
|
21
|
+
/** the smallest floor's cover, BNB wei */
|
|
22
|
+
minCover: bigint;
|
|
23
|
+
/** BNB free for new floors, and BNB locked for floors bought */
|
|
24
|
+
free: bigint;
|
|
25
|
+
locked: bigint;
|
|
26
|
+
/** it takes a new floor now */
|
|
27
|
+
live: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type DipFloor = {
|
|
30
|
+
id: bigint;
|
|
31
|
+
bid: bigint;
|
|
32
|
+
owner: Address;
|
|
33
|
+
coins: bigint;
|
|
34
|
+
cover: bigint;
|
|
35
|
+
premium: bigint;
|
|
36
|
+
bought: number;
|
|
37
|
+
expiry: number;
|
|
38
|
+
feeBps: number;
|
|
39
|
+
};
|
|
40
|
+
export type DipPost = {
|
|
41
|
+
token: Address;
|
|
42
|
+
/** BNB per coin (a number), or `strike` in BNB wei per whole coin */
|
|
43
|
+
price?: number;
|
|
44
|
+
strike?: bigint;
|
|
45
|
+
/** seconds a floor runs: 10 minutes to 7 days */
|
|
46
|
+
duration: number;
|
|
47
|
+
/** what a floor costs, basis points of what it covers (up to 50 %) */
|
|
48
|
+
premiumBps: number;
|
|
49
|
+
/** unix time the bid stops taking floors */
|
|
50
|
+
until: number;
|
|
51
|
+
/** the smallest floor, BNB wei of cover (at least the desk's MIN_COVER) */
|
|
52
|
+
minCover: bigint;
|
|
53
|
+
/** BNB the bid locks (at least the desk's MIN_DEPOSIT and its smallest floor) */
|
|
54
|
+
deposit: bigint;
|
|
55
|
+
};
|
|
56
|
+
/** strike = BNB wei per whole coin, from a price in BNB per coin */
|
|
57
|
+
export declare function strikeFromPrice(priceBnb: number): bigint;
|
|
58
|
+
/** BNB wei a floor of `coins` units covers at `strike` (as the desk rounds: down) */
|
|
59
|
+
export declare const coverOf: (coins: bigint, strike: bigint) => bigint;
|
|
60
|
+
/** coin units whose cover is `coverWei` at `strike` (rounded down) */
|
|
61
|
+
export declare const coinsFor: (coverWei: bigint, strike: bigint) => bigint;
|
|
62
|
+
export declare function dipsModule(ctx: Ctx): {
|
|
63
|
+
desk: `0x${string}`;
|
|
64
|
+
terms: () => Promise<{
|
|
65
|
+
premiumFeeBps: number;
|
|
66
|
+
exerciseFeeBps: number;
|
|
67
|
+
minDeposit: bigint;
|
|
68
|
+
minCover: bigint;
|
|
69
|
+
minDuration: number;
|
|
70
|
+
maxDuration: number;
|
|
71
|
+
maxPremiumBps: number;
|
|
72
|
+
}>;
|
|
73
|
+
bids: (token: Address, o?: {
|
|
74
|
+
limit?: number;
|
|
75
|
+
}) => Promise<DipBid[]>;
|
|
76
|
+
bid: (id: bigint) => Promise<DipBid>;
|
|
77
|
+
floorsOf: (holder: Address) => Promise<DipFloor[]>;
|
|
78
|
+
quote: (bidId: bigint, coins: bigint) => Promise<{
|
|
79
|
+
cover: bigint;
|
|
80
|
+
premium: bigint;
|
|
81
|
+
fee: bigint;
|
|
82
|
+
payout: bigint;
|
|
83
|
+
}>;
|
|
84
|
+
dryExercise: (floorId: bigint, coins: bigint, owner: Address) => Promise<{
|
|
85
|
+
ok: true;
|
|
86
|
+
out: bigint;
|
|
87
|
+
} | {
|
|
88
|
+
ok: false;
|
|
89
|
+
why: string;
|
|
90
|
+
}>;
|
|
91
|
+
pending: (who: Address) => Promise<bigint>;
|
|
92
|
+
strikeFromPrice: typeof strikeFromPrice;
|
|
93
|
+
coverOf: (coins: bigint, strike: bigint) => bigint;
|
|
94
|
+
coinsFor: (coverWei: bigint, strike: bigint) => bigint;
|
|
95
|
+
build: {
|
|
96
|
+
post: (p: DipPost) => TxRequest;
|
|
97
|
+
fund: (id: bigint, wei: bigint) => TxRequest;
|
|
98
|
+
withdraw: (id: bigint, wei: bigint) => TxRequest;
|
|
99
|
+
close: (id: bigint) => TxRequest;
|
|
100
|
+
/** value = the quoted premium */
|
|
101
|
+
buyFloor: (bidId: bigint, coins: bigint, premium: bigint) => TxRequest;
|
|
102
|
+
/** needs the coin approved to the desk */
|
|
103
|
+
exercise: (floorId: bigint, coins: bigint, minBnb: bigint) => TxRequest;
|
|
104
|
+
end: (floorId: bigint) => TxRequest;
|
|
105
|
+
give: (floorId: bigint, to: Address) => TxRequest;
|
|
106
|
+
withdrawPending: () => TxRequest;
|
|
107
|
+
};
|
|
108
|
+
/** posts a bid with its deposit; returns the bid id */
|
|
109
|
+
post: (p: DipPost, s?: SendOptions) => Promise<Sent & {
|
|
110
|
+
id: bigint;
|
|
111
|
+
}>;
|
|
112
|
+
fund: (id: bigint, wei: bigint, s?: SendOptions) => Promise<Sent>;
|
|
113
|
+
withdraw: (id: bigint, wei: bigint, s?: SendOptions) => Promise<Sent>;
|
|
114
|
+
close: (id: bigint, s?: SendOptions) => Promise<Sent>;
|
|
115
|
+
/** pays exactly the quoted premium; returns the floor id */
|
|
116
|
+
buyFloor: (bidId: bigint, coins: bigint, s?: SendOptions) => Promise<Sent & {
|
|
117
|
+
id: bigint;
|
|
118
|
+
premium: bigint;
|
|
119
|
+
}>;
|
|
120
|
+
/** approves the coin to the desk if needed, then hands `coins` in for at least the dry run's payout less slippageBps */
|
|
121
|
+
exercise: (o: {
|
|
122
|
+
floorId: bigint;
|
|
123
|
+
coins: bigint;
|
|
124
|
+
token: Address;
|
|
125
|
+
slippageBps?: number;
|
|
126
|
+
}, s?: SendOptions) => Promise<Sent & {
|
|
127
|
+
paid: bigint;
|
|
128
|
+
}>;
|
|
129
|
+
end: (floorId: bigint, s?: SendOptions) => Promise<Sent>;
|
|
130
|
+
give: (floorId: bigint, to: Address, s?: SendOptions) => Promise<Sent>;
|
|
131
|
+
withdrawPending: (s?: SendOptions) => Promise<Sent>;
|
|
132
|
+
};
|
|
133
|
+
export type DipsModule = ReturnType<typeof dipsModule>;
|