@sherwoodagent/cli 0.88.1 → 0.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{abis-WJQ6IVYV.js → abis-2VBXBA7Y.js} +16 -2
- package/dist/{agent-FCGZL4GF.js → agent-KMLNSKBS.js} +11 -11
- package/dist/{chat-AKLYUJZV.js → chat-JW5Z3JRE.js} +13 -13
- package/dist/{chunk-6SAE2VDK.js → chunk-6IC2NRED.js} +2 -2
- package/dist/{chunk-HMFPP2PE.js → chunk-77MBGRE5.js} +52 -24
- package/dist/chunk-77MBGRE5.js.map +1 -0
- package/dist/{chunk-G5MXVAFC.js → chunk-D3GTHAJS.js} +5 -5
- package/dist/chunk-G5FOHSQW.js +338 -0
- package/dist/chunk-G5FOHSQW.js.map +1 -0
- package/dist/chunk-GAQMHH44.js +18 -0
- package/dist/chunk-GAQMHH44.js.map +1 -0
- package/dist/{chunk-FWQESFFM.js → chunk-GYGSKMZE.js} +91 -8
- package/dist/chunk-GYGSKMZE.js.map +1 -0
- package/dist/{chunk-J2IZWAEW.js → chunk-JCL2DEH3.js} +2048 -2
- package/dist/chunk-JCL2DEH3.js.map +1 -0
- package/dist/{chunk-BH3VTJ4F.js → chunk-JKSDCCVL.js} +6 -6
- package/dist/{chunk-EEI6FRTE.js → chunk-JLOROS5V.js} +3 -3
- package/dist/{chunk-6D3FNU75.js → chunk-JRGYI34A.js} +5 -5
- package/dist/chunk-KZGY7NT7.js +209 -0
- package/dist/chunk-KZGY7NT7.js.map +1 -0
- package/dist/{chunk-G3FUEM5N.js → chunk-LIW6K4OJ.js} +2 -2
- package/dist/{chunk-4ITTHKRX.js → chunk-P4LFNKSX.js} +5 -5
- package/dist/{chunk-3TZIOAPH.js → chunk-QUAVAJWE.js} +13 -2
- package/dist/chunk-QUAVAJWE.js.map +1 -0
- package/dist/chunk-QVXKYT2N.js +489 -0
- package/dist/chunk-QVXKYT2N.js.map +1 -0
- package/dist/chunk-UPRFO4YU.js +458 -0
- package/dist/chunk-UPRFO4YU.js.map +1 -0
- package/dist/{chunk-QFGZQYZA.js → chunk-WDPA2BYK.js} +1024 -87
- package/dist/chunk-WDPA2BYK.js.map +1 -0
- package/dist/{chunk-CWGP4VXZ.js → chunk-YIN6R66K.js} +7 -7
- package/dist/{chunk-D4BSBW3Q.js → chunk-Z53JZORT.js} +3 -3
- package/dist/{chunk-SCQAKMMI.js → chunk-ZPOPWFSP.js} +5 -5
- package/dist/{client-PYFE6YQR.js → client-DA434B67.js} +4 -4
- package/dist/{config-ZGFS4I7K.js → config-UZ2GIXAO.js} +6 -2
- package/dist/{eas-B4HR2HXX.js → eas-ZH7BDEOK.js} +7 -7
- package/dist/{governor-QFBN6RYS.js → governor-WHT2V2EU.js} +7 -7
- package/dist/identity-TRPUQEQK.js +19 -0
- package/dist/index.js +8848 -6933
- package/dist/index.js.map +1 -1
- package/dist/launchpad-3N4ZFL6J.js +26 -0
- package/dist/lighter-perp-template-VC4TB7AM.js +92 -0
- package/dist/lighter-preflight-GH3JKLR6.js +149 -0
- package/dist/lighter-preflight-GH3JKLR6.js.map +1 -0
- package/dist/{network-7445KSCF.js → network-EZ5X2BF7.js} +3 -3
- package/dist/network-EZ5X2BF7.js.map +1 -0
- package/dist/{price-EXZDYVDD.js → price-RX5J7GTO.js} +9 -7
- package/dist/{price-EXZDYVDD.js.map → price-RX5J7GTO.js.map} +1 -1
- package/dist/{providers-6UZKDFQD.js → providers-XEEFKITN.js} +5 -5
- package/dist/{research-T7A27OMB.js → research-PYMQ5VKH.js} +9 -9
- package/dist/{session-45NQAHFI.js → session-DE47TUEW.js} +15 -15
- package/dist/{trade-ZOVM433K.js → trade-3VMAQ75D.js} +20 -18
- package/dist/{trade-ZOVM433K.js.map → trade-3VMAQ75D.js.map} +1 -1
- package/dist/{vault-E7XD6DLD.js → vault-PXGG2E3K.js} +9 -9
- package/dist/vault-PXGG2E3K.js.map +1 -0
- package/dist/{xmtp-72IRK7JL.js → xmtp-5PTIIVAI.js} +7 -7
- package/package.json +1 -1
- package/dist/chunk-3TZIOAPH.js.map +0 -1
- package/dist/chunk-FWQESFFM.js.map +0 -1
- package/dist/chunk-HMFPP2PE.js.map +0 -1
- package/dist/chunk-J2IZWAEW.js.map +0 -1
- package/dist/chunk-QFGZQYZA.js.map +0 -1
- package/dist/identity-I2BG4IGT.js +0 -18
- /package/dist/{abis-WJQ6IVYV.js.map → abis-2VBXBA7Y.js.map} +0 -0
- /package/dist/{agent-FCGZL4GF.js.map → agent-KMLNSKBS.js.map} +0 -0
- /package/dist/{chat-AKLYUJZV.js.map → chat-JW5Z3JRE.js.map} +0 -0
- /package/dist/{chunk-6SAE2VDK.js.map → chunk-6IC2NRED.js.map} +0 -0
- /package/dist/{chunk-G5MXVAFC.js.map → chunk-D3GTHAJS.js.map} +0 -0
- /package/dist/{chunk-BH3VTJ4F.js.map → chunk-JKSDCCVL.js.map} +0 -0
- /package/dist/{chunk-EEI6FRTE.js.map → chunk-JLOROS5V.js.map} +0 -0
- /package/dist/{chunk-6D3FNU75.js.map → chunk-JRGYI34A.js.map} +0 -0
- /package/dist/{chunk-G3FUEM5N.js.map → chunk-LIW6K4OJ.js.map} +0 -0
- /package/dist/{chunk-4ITTHKRX.js.map → chunk-P4LFNKSX.js.map} +0 -0
- /package/dist/{chunk-CWGP4VXZ.js.map → chunk-YIN6R66K.js.map} +0 -0
- /package/dist/{chunk-D4BSBW3Q.js.map → chunk-Z53JZORT.js.map} +0 -0
- /package/dist/{chunk-SCQAKMMI.js.map → chunk-ZPOPWFSP.js.map} +0 -0
- /package/dist/{client-PYFE6YQR.js.map → client-DA434B67.js.map} +0 -0
- /package/dist/{config-ZGFS4I7K.js.map → config-UZ2GIXAO.js.map} +0 -0
- /package/dist/{eas-B4HR2HXX.js.map → eas-ZH7BDEOK.js.map} +0 -0
- /package/dist/{governor-QFBN6RYS.js.map → governor-WHT2V2EU.js.map} +0 -0
- /package/dist/{identity-I2BG4IGT.js.map → identity-TRPUQEQK.js.map} +0 -0
- /package/dist/{network-7445KSCF.js.map → launchpad-3N4ZFL6J.js.map} +0 -0
- /package/dist/{vault-E7XD6DLD.js.map → lighter-perp-template-VC4TB7AM.js.map} +0 -0
- /package/dist/{providers-6UZKDFQD.js.map → providers-XEEFKITN.js.map} +0 -0
- /package/dist/{research-T7A27OMB.js.map → research-PYMQ5VKH.js.map} +0 -0
- /package/dist/{session-45NQAHFI.js.map → session-DE47TUEW.js.map} +0 -0
- /package/dist/{xmtp-72IRK7JL.js.map → xmtp-5PTIIVAI.js.map} +0 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BASE_STRATEGY_ABI,
|
|
3
|
+
ERC20_ABI,
|
|
4
|
+
LAUNCHPAD_STRATEGY_ABI,
|
|
5
|
+
LAUNCH_ADAPTER_ABI
|
|
6
|
+
} from "./chunk-JCL2DEH3.js";
|
|
7
|
+
|
|
8
|
+
// src/strategies/launchpad-template.ts
|
|
9
|
+
import { decodeAbiParameters, encodeAbiParameters, encodeFunctionData, parseUnits } from "viem";
|
|
10
|
+
var MAX_RESERVE_BPS = 2000n;
|
|
11
|
+
var MAX_CLAIM_WINDOW_SECONDS = 14n * 86400n;
|
|
12
|
+
var MAX_SLIPPAGE_BPS = 1000n;
|
|
13
|
+
var CLAIM_SETTLE_BUFFER_SECONDS = 300n;
|
|
14
|
+
var MAX_STAMP_DRAWDOWN_BPS = 9e3;
|
|
15
|
+
var SUSHI_LAUNCH_SUPPLY = 10n ** 27n;
|
|
16
|
+
var LAUNCH_TOKEN_DECIMALS = 18;
|
|
17
|
+
var DEFAULT_SETTLE_SLIPPAGE_BPS = 500n;
|
|
18
|
+
var MAX_FEE_HEADROOM_BPS = 2500n;
|
|
19
|
+
var DRAWDOWN_HEADROOM_BPS = 200;
|
|
20
|
+
var DEFAULT_DEADLINE_MARGIN_SECONDS = 86400n;
|
|
21
|
+
var LaunchpadInputError = class extends Error {
|
|
22
|
+
constructor(message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "LaunchpadInputError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var SUSHI_LIQUIDITY_MODES = { standard: 0, moon: 1 };
|
|
28
|
+
var SUSHI_FEE_MODES = { direct: 0, burn: 1, buyback: 2 };
|
|
29
|
+
function encodeSushiVenueData(v) {
|
|
30
|
+
if (v.liquidityMode === "standard" && v.feeMode === "direct") return "0x";
|
|
31
|
+
return encodeAbiParameters(
|
|
32
|
+
[{ type: "uint8" }, { type: "uint8" }],
|
|
33
|
+
[SUSHI_LIQUIDITY_MODES[v.liquidityMode], SUSHI_FEE_MODES[v.feeMode]]
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function decodeSushiVenueData(data) {
|
|
37
|
+
if (data === "0x") return { liquidityMode: "standard", feeMode: "direct" };
|
|
38
|
+
if ((data.length - 2) / 2 !== 64) return null;
|
|
39
|
+
const [liq, fee] = decodeAbiParameters([{ type: "uint8" }, { type: "uint8" }], data);
|
|
40
|
+
const liquidityMode = Object.keys(SUSHI_LIQUIDITY_MODES).find((k) => SUSHI_LIQUIDITY_MODES[k] === liq);
|
|
41
|
+
const feeMode = Object.keys(SUSHI_FEE_MODES).find((k) => SUSHI_FEE_MODES[k] === fee);
|
|
42
|
+
return liquidityMode && feeMode ? { liquidityMode, feeMode } : null;
|
|
43
|
+
}
|
|
44
|
+
var STONK_BOUNDS = {
|
|
45
|
+
minStartMcapUsd8: 10n ** 11n,
|
|
46
|
+
// $1,000
|
|
47
|
+
maxStartMcapUsd8: 10n ** 14n,
|
|
48
|
+
// $1,000,000
|
|
49
|
+
minGradMcapUsd8: 5n * 10n ** 12n,
|
|
50
|
+
// $50,000
|
|
51
|
+
maxGradMcapUsd8: 10n ** 15n,
|
|
52
|
+
// $10,000,000
|
|
53
|
+
maxStartTaxBps: 9900,
|
|
54
|
+
minWindowSecs: 600,
|
|
55
|
+
maxWindowSecs: 5940,
|
|
56
|
+
maxBuyPpm: 42069
|
|
57
|
+
};
|
|
58
|
+
var STONK_DEFAULTS = {
|
|
59
|
+
supply: 10n ** 27n,
|
|
60
|
+
startMcapUsd8: 10n ** 12n,
|
|
61
|
+
gradMcapUsd8: 10n ** 13n,
|
|
62
|
+
startTaxBps: 1e3,
|
|
63
|
+
taxDecayPerMinuteBps: 100,
|
|
64
|
+
sellsEnabled: true,
|
|
65
|
+
bufferSecs: 60,
|
|
66
|
+
unsoldMode: 0,
|
|
67
|
+
openEnded: false,
|
|
68
|
+
postTaxBps: 0,
|
|
69
|
+
bondVenue: 0,
|
|
70
|
+
maxBuyPpm: 0
|
|
71
|
+
};
|
|
72
|
+
var STONK_VENUE_DATA_TUPLE = {
|
|
73
|
+
type: "tuple",
|
|
74
|
+
components: [
|
|
75
|
+
{ name: "supply", type: "uint256" },
|
|
76
|
+
{ name: "startMcapUsd8", type: "uint64" },
|
|
77
|
+
{ name: "gradMcapUsd8", type: "uint64" },
|
|
78
|
+
{ name: "startTaxBps", type: "uint16" },
|
|
79
|
+
{ name: "taxDecayPerMinuteBps", type: "uint16" },
|
|
80
|
+
{ name: "sellsEnabled", type: "bool" },
|
|
81
|
+
{ name: "bufferSecs", type: "uint32" },
|
|
82
|
+
{ name: "unsoldMode", type: "uint8" },
|
|
83
|
+
{ name: "openEnded", type: "bool" },
|
|
84
|
+
{ name: "postTaxBps", type: "uint16" },
|
|
85
|
+
{ name: "bondVenue", type: "uint8" },
|
|
86
|
+
{ name: "maxBuyPpm", type: "uint32" }
|
|
87
|
+
]
|
|
88
|
+
};
|
|
89
|
+
function encodeStonkVenueData(v) {
|
|
90
|
+
return encodeAbiParameters([STONK_VENUE_DATA_TUPLE], [v]);
|
|
91
|
+
}
|
|
92
|
+
function decodeStonkVenueData(data) {
|
|
93
|
+
try {
|
|
94
|
+
const [v] = decodeAbiParameters([STONK_VENUE_DATA_TUPLE], data);
|
|
95
|
+
return { ...v };
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
var INIT_PARAMS_TUPLE = {
|
|
101
|
+
type: "tuple",
|
|
102
|
+
components: [
|
|
103
|
+
{ name: "launchAdapter", type: "address" },
|
|
104
|
+
{ name: "swapAdapter", type: "address" },
|
|
105
|
+
{ name: "assetIn", type: "uint256" },
|
|
106
|
+
{ name: "quoteToken", type: "address" },
|
|
107
|
+
{ name: "minQuoteOut", type: "uint256" },
|
|
108
|
+
{ name: "quoteSwapData", type: "bytes" },
|
|
109
|
+
{ name: "feeSwapData", type: "bytes" },
|
|
110
|
+
{ name: "launchSupply", type: "uint256" },
|
|
111
|
+
{ name: "reserveAmount", type: "uint256" },
|
|
112
|
+
{ name: "minTokensOut", type: "uint256" },
|
|
113
|
+
{ name: "claimWindow", type: "uint256" },
|
|
114
|
+
{ name: "deadline", type: "uint64" },
|
|
115
|
+
{ name: "settleSlippageBps", type: "uint256" },
|
|
116
|
+
{ name: "maxFeeIn", type: "uint256" },
|
|
117
|
+
{ name: "name", type: "string" },
|
|
118
|
+
{ name: "symbol", type: "string" },
|
|
119
|
+
{ name: "venueData", type: "bytes" }
|
|
120
|
+
]
|
|
121
|
+
};
|
|
122
|
+
function buildInitData(p) {
|
|
123
|
+
return encodeAbiParameters([INIT_PARAMS_TUPLE], [p]);
|
|
124
|
+
}
|
|
125
|
+
function buildExecuteCalls(clone, asset, assetIn) {
|
|
126
|
+
return [
|
|
127
|
+
{
|
|
128
|
+
target: asset,
|
|
129
|
+
data: encodeFunctionData({ abi: ERC20_ABI, functionName: "approve", args: [clone, assetIn] }),
|
|
130
|
+
value: 0n
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
target: clone,
|
|
134
|
+
data: encodeFunctionData({ abi: BASE_STRATEGY_ABI, functionName: "execute" }),
|
|
135
|
+
value: 0n
|
|
136
|
+
}
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
function buildSettleCalls(clone) {
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
target: clone,
|
|
143
|
+
data: encodeFunctionData({ abi: BASE_STRATEGY_ABI, functionName: "settle" }),
|
|
144
|
+
value: 0n
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
function encodeClaimCalldata() {
|
|
149
|
+
return encodeFunctionData({ abi: LAUNCHPAD_STRATEGY_ABI, functionName: "claim" });
|
|
150
|
+
}
|
|
151
|
+
function encodeClaimForCalldata(holder) {
|
|
152
|
+
return encodeFunctionData({ abi: LAUNCHPAD_STRATEGY_ABI, functionName: "claimFor", args: [holder] });
|
|
153
|
+
}
|
|
154
|
+
function encodeCollectFeesCalldata(launchRef) {
|
|
155
|
+
return encodeFunctionData({ abi: LAUNCH_ADAPTER_ABI, functionName: "collectFees", args: [launchRef] });
|
|
156
|
+
}
|
|
157
|
+
function encodeFinalizeCalldata(launchRef) {
|
|
158
|
+
return encodeFunctionData({ abi: LAUNCH_ADAPTER_ABI, functionName: "finalize", args: [launchRef] });
|
|
159
|
+
}
|
|
160
|
+
function requiredDrawdownBps(assetIn, totalAssets) {
|
|
161
|
+
if (totalAssets === 0n) return null;
|
|
162
|
+
const lossBps = (assetIn * 10000n + totalAssets - 1n) / totalAssets;
|
|
163
|
+
return Number(lossBps) + DRAWDOWN_HEADROOM_BPS;
|
|
164
|
+
}
|
|
165
|
+
function decideLaunchDrawdown(userValue, assetIn, totalAssets, fmtAssetIn, fmtTotalAssets) {
|
|
166
|
+
const required = requiredDrawdownBps(assetIn, totalAssets);
|
|
167
|
+
if (required === null) {
|
|
168
|
+
throw new LaunchpadInputError(
|
|
169
|
+
"The vault's totalAssets() is 0 \u2014 there is no capital to launch with and no share price to size a drawdown against."
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
const why = `A launch settles as a vault-asset LOSS of about --asset-in (the reserve is a dividend in kind; v1 books no value for the launch token). ${fmtAssetIn} of ${fmtTotalAssets} is ${required - DRAWDOWN_HEADROOM_BPS} bps of the vault, plus ${DRAWDOWN_HEADROOM_BPS} bps headroom = ${required} bps.`;
|
|
173
|
+
if (required > MAX_STAMP_DRAWDOWN_BPS) {
|
|
174
|
+
throw new LaunchpadInputError(
|
|
175
|
+
`${why} The governor caps the settle-price floor at ${MAX_STAMP_DRAWDOWN_BPS} bps (MAX_STAMP_DRAWDOWN_BPS), so this proposal could never settle through settleProposal. Lower --asset-in.`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
if (userValue === void 0) return { maxDrawdownBps: required, derived: true };
|
|
179
|
+
const user = Number(userValue);
|
|
180
|
+
if (!Number.isInteger(user) || user < 0 || user > 1e4) {
|
|
181
|
+
throw new LaunchpadInputError(`--max-drawdown-bps must be an integer 0-10000 (got ${userValue})`);
|
|
182
|
+
}
|
|
183
|
+
if (user < required) {
|
|
184
|
+
throw new LaunchpadInputError(
|
|
185
|
+
`--max-drawdown-bps ${user} is below the ${required} bps this launch needs to settle. ${why} Below that, settleProposal reverts SettlePriceBelowFloor and only the vault owner's unstick/emergency path can finish the proposal. Omit --max-drawdown-bps to use ${required}, or pass a value >= ${required}.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return { maxDrawdownBps: user, derived: false };
|
|
189
|
+
}
|
|
190
|
+
function mulDivCeil(a, b, c) {
|
|
191
|
+
return (a * b + c - 1n) / c;
|
|
192
|
+
}
|
|
193
|
+
function liveFeeCost(args) {
|
|
194
|
+
const { feeToken, feeAmount, asset, quoteToken, settleSlippageBps } = args;
|
|
195
|
+
if (feeAmount === 0n) return { cost: 0n, payIn: "none", lane: "none" };
|
|
196
|
+
if (sameAddr(feeToken, asset)) return { cost: feeAmount, payIn: "asset", lane: "asset" };
|
|
197
|
+
if (sameAddr(feeToken, quoteToken)) return { cost: feeAmount, payIn: "quote", lane: "quote" };
|
|
198
|
+
const probe = args.probe ?? 0n;
|
|
199
|
+
const out = args.probeOut ?? 0n;
|
|
200
|
+
if (probe === 0n || out === 0n) {
|
|
201
|
+
throw new LaunchpadInputError(
|
|
202
|
+
`The launch fee is charged in ${feeToken}, which is neither the vault asset nor the quote, and the swap adapter returned no quote for it \u2014 execute would revert FeeAcquisitionFailed. Pass --fee-swap-route.`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const amountIn = mulDivCeil(feeAmount, probe, out);
|
|
206
|
+
const cost = amountIn * (10000n + settleSlippageBps) / 10000n;
|
|
207
|
+
return { cost, payIn: "quote", lane: "swap" };
|
|
208
|
+
}
|
|
209
|
+
function defaultMaxFeeIn(liveCost) {
|
|
210
|
+
if (liveCost === 0n) return 0n;
|
|
211
|
+
return mulDivCeil(liveCost, 10000n + MAX_FEE_HEADROOM_BPS, 10000n);
|
|
212
|
+
}
|
|
213
|
+
function sameAddr(a, b) {
|
|
214
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
215
|
+
}
|
|
216
|
+
function parseVenue(raw) {
|
|
217
|
+
const v = (typeof raw === "string" ? raw : "sushi").toLowerCase();
|
|
218
|
+
if (v !== "sushi" && v !== "stonk") {
|
|
219
|
+
throw new LaunchpadInputError(`--venue must be sushi or stonk (got ${raw})`);
|
|
220
|
+
}
|
|
221
|
+
return v;
|
|
222
|
+
}
|
|
223
|
+
function parseDurationSeconds(input) {
|
|
224
|
+
const match = input.trim().match(/^(\d+)(d|h|m|s)?$/);
|
|
225
|
+
if (!match) throw new LaunchpadInputError(`Invalid duration: ${input} (use e.g. 7d, 12h, 30m)`);
|
|
226
|
+
const value = BigInt(match[1]);
|
|
227
|
+
switch (match[2]) {
|
|
228
|
+
case "d":
|
|
229
|
+
return value * 86400n;
|
|
230
|
+
case "h":
|
|
231
|
+
return value * 3600n;
|
|
232
|
+
case "m":
|
|
233
|
+
return value * 60n;
|
|
234
|
+
default:
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function parseSettleSlippageBps(raw) {
|
|
239
|
+
if (raw === void 0) return DEFAULT_SETTLE_SLIPPAGE_BPS;
|
|
240
|
+
const n = Number(raw);
|
|
241
|
+
if (!Number.isInteger(n) || n <= 0 || BigInt(n) > MAX_SLIPPAGE_BPS) {
|
|
242
|
+
throw new LaunchpadInputError(
|
|
243
|
+
`--settle-slippage-bps must be an integer in [1, ${MAX_SLIPPAGE_BPS}] (got ${raw}). Zero is refused on chain (InvalidSlippage): it would turn every settle-time conversion into a raw in-kind delivery.`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return BigInt(n);
|
|
247
|
+
}
|
|
248
|
+
function parseAmountFlag(raw, decimals, flag) {
|
|
249
|
+
if (typeof raw !== "string" || raw.trim() === "") throw new LaunchpadInputError(`${flag} is required`);
|
|
250
|
+
try {
|
|
251
|
+
const v = parseUnits(raw.trim(), decimals);
|
|
252
|
+
if (v < 0n) throw new Error("negative");
|
|
253
|
+
return v;
|
|
254
|
+
} catch {
|
|
255
|
+
throw new LaunchpadInputError(`${flag} must be a non-negative decimal amount (got ${raw})`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function parseDeadline(raw, now, fallback) {
|
|
259
|
+
if (raw === void 0) return fallback;
|
|
260
|
+
if (typeof raw !== "string") throw new LaunchpadInputError("--deadline needs a value");
|
|
261
|
+
const s = raw.trim();
|
|
262
|
+
if (s === "0") return 0n;
|
|
263
|
+
if (/^\d+$/.test(s) && BigInt(s) >= 1000000000n) return BigInt(s);
|
|
264
|
+
return now + parseDurationSeconds(s);
|
|
265
|
+
}
|
|
266
|
+
function parseIntFlag(raw, flag, fallback) {
|
|
267
|
+
if (raw === void 0) return fallback;
|
|
268
|
+
const n = Number(raw);
|
|
269
|
+
if (!Number.isInteger(n) || n < 0) throw new LaunchpadInputError(`${flag} must be a non-negative integer (got ${raw})`);
|
|
270
|
+
return n;
|
|
271
|
+
}
|
|
272
|
+
function parseSushiVenueData(opts) {
|
|
273
|
+
const liq = (typeof opts.liquidity === "string" ? opts.liquidity : "standard").toLowerCase();
|
|
274
|
+
if (!(liq in SUSHI_LIQUIDITY_MODES)) {
|
|
275
|
+
throw new LaunchpadInputError(`--liquidity must be standard or moon (got ${opts.liquidity})`);
|
|
276
|
+
}
|
|
277
|
+
const fee = (typeof opts.feeMode === "string" ? opts.feeMode : "direct").toLowerCase();
|
|
278
|
+
if (fee === "holders" || fee === "distribute" || fee === "distribute_to_holders") {
|
|
279
|
+
throw new LaunchpadInputError(
|
|
280
|
+
"--fee-mode holders (DISTRIBUTE_TO_HOLDERS) is refused by SushiLaunchAdapter (DispositionUnsupported): its fee share accrues on a per-token HolderRewards contract that nothing in this stack collects for the vault. Use direct, burn or buyback."
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (!(fee in SUSHI_FEE_MODES)) {
|
|
284
|
+
throw new LaunchpadInputError(`--fee-mode must be direct, burn or buyback (got ${opts.feeMode})`);
|
|
285
|
+
}
|
|
286
|
+
return { liquidityMode: liq, feeMode: fee };
|
|
287
|
+
}
|
|
288
|
+
function parseStonkVenueData(opts) {
|
|
289
|
+
const d = STONK_DEFAULTS;
|
|
290
|
+
const supply = opts.stonkSupply === void 0 ? d.supply : parseAmountFlag(opts.stonkSupply, LAUNCH_TOKEN_DECIMALS, "--stonk-supply");
|
|
291
|
+
const startMcapUsd8 = opts.startMcap === void 0 ? d.startMcapUsd8 : parseAmountFlag(opts.startMcap, 8, "--start-mcap");
|
|
292
|
+
const gradMcapUsd8 = opts.gradMcap === void 0 ? d.gradMcapUsd8 : parseAmountFlag(opts.gradMcap, 8, "--grad-mcap");
|
|
293
|
+
const openEnded = opts.openEnded === true;
|
|
294
|
+
let unsoldMode = d.unsoldMode;
|
|
295
|
+
if (opts.unsoldMode !== void 0) {
|
|
296
|
+
const u = String(opts.unsoldMode).toLowerCase();
|
|
297
|
+
if (u === "burn" || u === "0") unsoldMode = 0;
|
|
298
|
+
else if (u === "lp" || u === "1") unsoldMode = 1;
|
|
299
|
+
else throw new LaunchpadInputError(`--unsold-mode must be burn or lp (got ${opts.unsoldMode})`);
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
supply,
|
|
303
|
+
startMcapUsd8,
|
|
304
|
+
gradMcapUsd8,
|
|
305
|
+
startTaxBps: parseIntFlag(opts.startTaxBps, "--start-tax-bps", d.startTaxBps),
|
|
306
|
+
taxDecayPerMinuteBps: parseIntFlag(opts.taxDecayBps, "--tax-decay-bps", d.taxDecayPerMinuteBps),
|
|
307
|
+
sellsEnabled: opts.sellsDisabled !== true,
|
|
308
|
+
bufferSecs: parseIntFlag(opts.bufferSecs, "--buffer-secs", d.bufferSecs),
|
|
309
|
+
unsoldMode,
|
|
310
|
+
openEnded,
|
|
311
|
+
// An open-ended curve needs a post-tax in [100, 500]; default to the floor.
|
|
312
|
+
postTaxBps: parseIntFlag(opts.postTaxBps, "--post-tax-bps", openEnded ? 100 : d.postTaxBps),
|
|
313
|
+
bondVenue: parseIntFlag(opts.bondVenue, "--bond-venue", d.bondVenue),
|
|
314
|
+
maxBuyPpm: parseIntFlag(opts.maxBuyPpm, "--max-buy-ppm", d.maxBuyPpm)
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function launchSupplyFor(venue, stonk) {
|
|
318
|
+
return venue === "sushi" ? SUSHI_LAUNCH_SUPPLY : (stonk ?? STONK_DEFAULTS).supply;
|
|
319
|
+
}
|
|
320
|
+
function parseReserve(raw, launchSupply) {
|
|
321
|
+
if (typeof raw !== "string" || raw.trim() === "") throw new LaunchpadInputError("--reserve is required");
|
|
322
|
+
const s = raw.trim();
|
|
323
|
+
if (s.endsWith("%")) {
|
|
324
|
+
const pct = s.slice(0, -1);
|
|
325
|
+
if (!/^\d+(\.\d{1,2})?$/.test(pct)) throw new LaunchpadInputError(`--reserve percent must look like 10% or 12.5% (got ${raw})`);
|
|
326
|
+
const bps = parseUnits(pct, 2);
|
|
327
|
+
return launchSupply * bps / 10000n;
|
|
328
|
+
}
|
|
329
|
+
return parseAmountFlag(s, LAUNCH_TOKEN_DECIMALS, "--reserve");
|
|
330
|
+
}
|
|
331
|
+
function validateLaunchpadParams(p, venue, stonk) {
|
|
332
|
+
const errors = [];
|
|
333
|
+
const ZERO_ADDR = "0x0000000000000000000000000000000000000000";
|
|
334
|
+
if (sameAddr(p.launchAdapter, ZERO_ADDR)) errors.push(`no ${venue} launch adapter on this network (ZeroAddress)`);
|
|
335
|
+
if (sameAddr(p.swapAdapter, ZERO_ADDR)) errors.push("no swap adapter on this network \u2014 pass --swap-adapter (ZeroAddress)");
|
|
336
|
+
if (sameAddr(p.quoteToken, ZERO_ADDR)) errors.push("--quote resolved to the zero address (ZeroAddress)");
|
|
337
|
+
if (p.assetIn === 0n) errors.push("--asset-in must be nonzero \u2014 there is no budget to deploy (InvalidAmount)");
|
|
338
|
+
if (p.reserveAmount === 0n) {
|
|
339
|
+
errors.push("--reserve must be nonzero (ZeroReserve): the holders' claim is the reason this template may spend the float");
|
|
340
|
+
} else {
|
|
341
|
+
const maxReserve = p.launchSupply * MAX_RESERVE_BPS / 10000n;
|
|
342
|
+
if (p.reserveAmount > maxReserve) {
|
|
343
|
+
errors.push(
|
|
344
|
+
`--reserve ${p.reserveAmount} wei exceeds 20% of the ${p.launchSupply} wei launch supply (max ${maxReserve}; MAX_RESERVE_BPS) (ReserveTooLarge)`
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (p.claimWindow === 0n || p.claimWindow > MAX_CLAIM_WINDOW_SECONDS) {
|
|
349
|
+
errors.push(`--claim-window must be > 0 and <= 14d (MAX_CLAIM_WINDOW) (InvalidClaimWindow)`);
|
|
350
|
+
}
|
|
351
|
+
if (p.settleSlippageBps === 0n || p.settleSlippageBps > MAX_SLIPPAGE_BPS) {
|
|
352
|
+
errors.push(`--settle-slippage-bps must be in [1, ${MAX_SLIPPAGE_BPS}] (InvalidSlippage)`);
|
|
353
|
+
}
|
|
354
|
+
if (!p.name) errors.push("--token-name is required");
|
|
355
|
+
if (!p.symbol) errors.push("--token-symbol is required");
|
|
356
|
+
if (venue === "sushi") {
|
|
357
|
+
if (Buffer.byteLength(p.name, "utf8") > 64) errors.push("--token-name must be at most 64 bytes on Sushi");
|
|
358
|
+
if (Buffer.byteLength(p.symbol, "utf8") > 16) errors.push("--token-symbol must be at most 16 bytes on Sushi");
|
|
359
|
+
if (p.launchSupply !== SUSHI_LAUNCH_SUPPLY) {
|
|
360
|
+
errors.push(`Sushi mints a fixed ${SUSHI_LAUNCH_SUPPLY} wei supply; ${p.launchSupply} would revert SupplyMismatch`);
|
|
361
|
+
}
|
|
362
|
+
if (p.minTokensOut < p.reserveAmount) {
|
|
363
|
+
errors.push(
|
|
364
|
+
`--min-tokens-out must be >= --reserve on Sushi: the reserve is the dev buy, and a lower floor lets a sandwiched buy deliver less than the holders were promised (ReserveFloorBelowReserve)`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (venue === "stonk") {
|
|
369
|
+
if (!stonk) {
|
|
370
|
+
errors.push("StonkBrokers needs its venue economics");
|
|
371
|
+
return errors;
|
|
372
|
+
}
|
|
373
|
+
if (stonk.supply !== p.launchSupply) errors.push("--stonk-supply must equal the declared launch supply");
|
|
374
|
+
if (p.reserveAmount >= stonk.supply) {
|
|
375
|
+
errors.push("--reserve must be strictly below --stonk-supply (ReserveExceedsSupply): nothing would be left to arm");
|
|
376
|
+
}
|
|
377
|
+
if (p.minTokensOut === 0n) {
|
|
378
|
+
errors.push(
|
|
379
|
+
"--min-tokens-out must be nonzero: it is the slippage floor on the curve dev buy (quote it through the StonkBrokers lens off-chain)"
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
errors.push(...validateStonkEconomics(stonk));
|
|
383
|
+
}
|
|
384
|
+
return errors;
|
|
385
|
+
}
|
|
386
|
+
function validateStonkEconomics(v) {
|
|
387
|
+
const errors = [];
|
|
388
|
+
const b = STONK_BOUNDS;
|
|
389
|
+
if (v.supply === 0n) errors.push("--stonk-supply must be nonzero");
|
|
390
|
+
if (v.startMcapUsd8 < b.minStartMcapUsd8 || v.startMcapUsd8 > b.maxStartMcapUsd8) {
|
|
391
|
+
errors.push("--start-mcap must be within $1,000\u2013$1,000,000 (pad bounds())");
|
|
392
|
+
}
|
|
393
|
+
if (v.gradMcapUsd8 < b.minGradMcapUsd8 || v.gradMcapUsd8 > b.maxGradMcapUsd8) {
|
|
394
|
+
errors.push("--grad-mcap must be within $50,000\u2013$10,000,000 (pad bounds())");
|
|
395
|
+
}
|
|
396
|
+
if (v.gradMcapUsd8 <= v.startMcapUsd8) errors.push("--grad-mcap must be above --start-mcap");
|
|
397
|
+
if (v.startTaxBps > b.maxStartTaxBps) errors.push(`--start-tax-bps must be <= ${b.maxStartTaxBps}`);
|
|
398
|
+
if (v.startTaxBps === 0) {
|
|
399
|
+
if (v.taxDecayPerMinuteBps !== 0) errors.push("--start-tax-bps 0 requires --tax-decay-bps 0");
|
|
400
|
+
if (!v.openEnded) errors.push("--start-tax-bps 0 requires --open-ended (there is no decay window to derive)");
|
|
401
|
+
} else if (v.taxDecayPerMinuteBps === 0) {
|
|
402
|
+
errors.push("a nonzero --start-tax-bps requires a nonzero --tax-decay-bps");
|
|
403
|
+
} else if (v.startTaxBps % v.taxDecayPerMinuteBps !== 0) {
|
|
404
|
+
errors.push("--start-tax-bps must be divisible by --tax-decay-bps");
|
|
405
|
+
} else {
|
|
406
|
+
const windowSecs = v.startTaxBps / v.taxDecayPerMinuteBps * 60;
|
|
407
|
+
if (windowSecs < b.minWindowSecs || windowSecs > b.maxWindowSecs) {
|
|
408
|
+
errors.push(
|
|
409
|
+
`the derived curve window (start-tax / decay \xD7 60 = ${windowSecs}s) must be within [${b.minWindowSecs}, ${b.maxWindowSecs}]s`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (v.openEnded) {
|
|
414
|
+
if (!v.sellsEnabled) errors.push("--open-ended requires sells (drop --sells-disabled)");
|
|
415
|
+
if (v.postTaxBps < 100 || v.postTaxBps > 500) errors.push("--open-ended requires --post-tax-bps within [100, 500]");
|
|
416
|
+
} else if (v.postTaxBps !== 0) {
|
|
417
|
+
errors.push("--post-tax-bps must be 0 unless --open-ended");
|
|
418
|
+
}
|
|
419
|
+
if (v.unsoldMode > 1) errors.push("--unsold-mode must be burn or lp");
|
|
420
|
+
if (v.maxBuyPpm > b.maxBuyPpm) errors.push(`--max-buy-ppm must be <= ${b.maxBuyPpm}`);
|
|
421
|
+
if (v.bondVenue > 255) errors.push("--bond-venue must fit a uint8");
|
|
422
|
+
if (v.bufferSecs > 4294967295) errors.push("--buffer-secs must fit a uint32");
|
|
423
|
+
return errors;
|
|
424
|
+
}
|
|
425
|
+
function buildLaunchpadInitParams(opts, r) {
|
|
426
|
+
let venueData;
|
|
427
|
+
let stonk;
|
|
428
|
+
let sushi;
|
|
429
|
+
if (r.venue === "sushi") {
|
|
430
|
+
sushi = parseSushiVenueData(opts);
|
|
431
|
+
venueData = encodeSushiVenueData(sushi);
|
|
432
|
+
} else {
|
|
433
|
+
stonk = parseStonkVenueData(opts);
|
|
434
|
+
venueData = encodeStonkVenueData(stonk);
|
|
435
|
+
}
|
|
436
|
+
const launchSupply = launchSupplyFor(r.venue, stonk);
|
|
437
|
+
if (typeof opts.claimWindow !== "string") throw new LaunchpadInputError("--claim-window is required (e.g. 7d, at most 14d)");
|
|
438
|
+
const params = {
|
|
439
|
+
launchAdapter: r.launchAdapter,
|
|
440
|
+
swapAdapter: r.swapAdapter,
|
|
441
|
+
assetIn: r.assetIn,
|
|
442
|
+
quoteToken: r.quoteToken,
|
|
443
|
+
minQuoteOut: r.minQuoteOut,
|
|
444
|
+
quoteSwapData: r.quoteSwapData,
|
|
445
|
+
feeSwapData: r.feeSwapData,
|
|
446
|
+
launchSupply,
|
|
447
|
+
reserveAmount: parseReserve(opts.reserve, launchSupply),
|
|
448
|
+
minTokensOut: parseAmountFlag(opts.minTokensOut, LAUNCH_TOKEN_DECIMALS, "--min-tokens-out"),
|
|
449
|
+
claimWindow: parseDurationSeconds(opts.claimWindow),
|
|
450
|
+
deadline: r.deadline,
|
|
451
|
+
settleSlippageBps: r.settleSlippageBps,
|
|
452
|
+
maxFeeIn: r.maxFeeIn,
|
|
453
|
+
name: typeof opts.tokenName === "string" ? opts.tokenName : "",
|
|
454
|
+
symbol: typeof opts.tokenSymbol === "string" ? opts.tokenSymbol : "",
|
|
455
|
+
venueData
|
|
456
|
+
};
|
|
457
|
+
const errors = validateLaunchpadParams(params, r.venue, stonk);
|
|
458
|
+
if (errors.length > 0) {
|
|
459
|
+
throw new LaunchpadInputError(`Launchpad parameters rejected:
|
|
460
|
+
- ${errors.join("\n - ")}`);
|
|
461
|
+
}
|
|
462
|
+
return { params, stonk, sushi };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export {
|
|
466
|
+
CLAIM_SETTLE_BUFFER_SECONDS,
|
|
467
|
+
DEFAULT_DEADLINE_MARGIN_SECONDS,
|
|
468
|
+
LaunchpadInputError,
|
|
469
|
+
decodeSushiVenueData,
|
|
470
|
+
decodeStonkVenueData,
|
|
471
|
+
buildInitData,
|
|
472
|
+
buildExecuteCalls,
|
|
473
|
+
buildSettleCalls,
|
|
474
|
+
encodeClaimCalldata,
|
|
475
|
+
encodeClaimForCalldata,
|
|
476
|
+
encodeCollectFeesCalldata,
|
|
477
|
+
encodeFinalizeCalldata,
|
|
478
|
+
decideLaunchDrawdown,
|
|
479
|
+
liveFeeCost,
|
|
480
|
+
defaultMaxFeeIn,
|
|
481
|
+
sameAddr,
|
|
482
|
+
parseVenue,
|
|
483
|
+
parseDurationSeconds,
|
|
484
|
+
parseSettleSlippageBps,
|
|
485
|
+
parseAmountFlag,
|
|
486
|
+
parseDeadline,
|
|
487
|
+
buildLaunchpadInitParams
|
|
488
|
+
};
|
|
489
|
+
//# sourceMappingURL=chunk-QVXKYT2N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/strategies/launchpad-template.ts"],"sourcesContent":["/**\n * LaunchpadStrategy call builder (sherwood-strategies `src/launchpad/`).\n *\n * \"IPOs\" a fund: launches a fund token on an allowlisted launch venue with\n * vault capital, holds a reserve back, and lets the fund's share holders claim\n * that reserve pro-rata by a snapshot taken at execute. Two venues ship\n * `ILaunchAdapter` implementations: Sushi Launchpad V2 (`SushiLaunchAdapter`)\n * and StonkBrokers Smart Launch V2 (`StonkLaunchAdapter`).\n *\n * PURITY RULE (same as lib/template-encoder.ts): nothing here performs chain\n * I/O. Every chain-derived input (vault asset + decimals, quote decimals, the\n * live launch fee, swap quotes, the drawdown the vault needs) is resolved by\n * the command layer (commands/launchpad-preflight.ts) and handed in as a\n * `LaunchpadResolved`.\n *\n * InitParams — ONE struct, `abi.encode`d whole, so the payload is a single\n * dynamic tuple with a leading 0x20 offset, NOT a flat parameter list. Field\n * order is `LaunchpadStrategy.InitParams` verbatim; note `maxFeeIn` sits\n * between `settleSlippageBps` and `name`.\n *\n * THERE IS NO `feeRecipient` MEMBER ON PURPOSE. `_execute` names the fund's\n * VAULT as the venue's fee recipient, so creator fees are the vault's from the\n * launch block and never enter the clone. They are not part of the claim pot.\n */\n\nimport type { Address, Hex } from \"viem\";\nimport { decodeAbiParameters, encodeAbiParameters, encodeFunctionData, parseUnits } from \"viem\";\nimport { ERC20_ABI, BASE_STRATEGY_ABI, LAUNCHPAD_STRATEGY_ABI, LAUNCH_ADAPTER_ABI } from \"../lib/abis.js\";\nimport type { BatchCall } from \"../lib/batch.js\";\n\n// ── Template constants (mirrors of LaunchpadStrategy's own) ──\n\n/** `LaunchpadStrategy.MAX_RESERVE_BPS` — the reserve may be at most 20% of supply. */\nexport const MAX_RESERVE_BPS = 2_000n;\n/** `LaunchpadStrategy.MAX_CLAIM_WINDOW` — 14 days. */\nexport const MAX_CLAIM_WINDOW_SECONDS = 14n * 86_400n;\n/** `LaunchpadStrategy.MAX_SLIPPAGE_BPS` — ceiling on the settle-time conversion tolerance. */\nexport const MAX_SLIPPAGE_BPS = 1_000n;\n/** `LaunchpadStrategy.CLAIM_SETTLE_BUFFER` — `strategyDuration` must exceed it or execute reverts. */\nexport const CLAIM_SETTLE_BUFFER_SECONDS = 300n;\n/** `SyndicateGovernor.MAX_STAMP_DRAWDOWN_BPS` — the settle-price floor never widens past 90%. */\nexport const MAX_STAMP_DRAWDOWN_BPS = 9_000;\n\n/**\n * Sushi Launchpad V2 mints a FIXED 1e9 × 1e18 supply. `launchSupply` is not a\n * choice on this venue: `_execute` compares the token's real `totalSupply()`\n * with the declared figure and reverts `SupplyMismatch` on any other number.\n */\nexport const SUSHI_LAUNCH_SUPPLY = 10n ** 27n;\n/** Launch tokens are 18-decimal on both venues. */\nexport const LAUNCH_TOKEN_DECIMALS = 18;\n\n// ── CLI defaults (each documented where it is consumed) ──\n\n/** Settle-time quote conversion tolerance: 5%, matching the other templates. */\nexport const DEFAULT_SETTLE_SLIPPAGE_BPS = 500n;\n/**\n * Headroom added on top of the live launch-fee cost for the default\n * `maxFeeIn`: 25%. Enough to absorb ordinary price drift on the fee swap and a\n * modest venue reprice between the vote and execute; a fee that rises by more than\n * this still refuses at execute (`FeeAboveCap`), which is the point of the cap.\n */\nexport const MAX_FEE_HEADROOM_BPS = 2_500n;\n/**\n * Headroom added on top of `assetIn / totalAssets` for the default drawdown:\n * 2 percentage points, for management-fee accrual, swap rounding and share\n * price drift between propose and execute.\n */\nexport const DRAWDOWN_HEADROOM_BPS = 200;\n/** Seconds after the governor's collaboration + voting + execution windows the default deadline allows. */\nexport const DEFAULT_DEADLINE_MARGIN_SECONDS = 86_400n;\n\nexport type LaunchVenue = \"sushi\" | \"stonk\";\n\n/** A flag problem the user can fix. Thrown by the parsers; message is user-facing. */\nexport class LaunchpadInputError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LaunchpadInputError\";\n }\n}\n\n// ── Venue data: Sushi ──\n\n/** `ISushiLaunchpadV2.LiquidityMode` ordinals. */\nexport const SUSHI_LIQUIDITY_MODES = { standard: 0, moon: 1 } as const;\n/**\n * `ISushiLaunchpadV2.FeeDisposition` ordinals. `DISTRIBUTE_TO_HOLDERS` (3) is\n * deliberately absent: `SushiLaunchAdapter` refuses it (`DispositionUnsupported`),\n * because its fee share would accrue on a per-token HolderRewards contract that\n * nothing in this stack collects for the vault.\n */\nexport const SUSHI_FEE_MODES = { direct: 0, burn: 1, buyback: 2 } as const;\nexport type SushiLiquidityMode = keyof typeof SUSHI_LIQUIDITY_MODES;\nexport type SushiFeeMode = keyof typeof SUSHI_FEE_MODES;\n\nexport interface SushiVenueData {\n liquidityMode: SushiLiquidityMode;\n feeMode: SushiFeeMode;\n}\n\n/**\n * `abi.encode(SushiLaunchAdapter.VenueData)`, or `0x` for the adapter's\n * defaults (STANDARD + DIRECT_PAYOUT). The adapter accepts exactly 0 or 64\n * bytes, so the defaults are sent as EMPTY bytes — byte-identical to what the\n * adapter decodes them to, and the shape every existing Sushi launch used.\n */\nexport function encodeSushiVenueData(v: SushiVenueData): Hex {\n if (v.liquidityMode === \"standard\" && v.feeMode === \"direct\") return \"0x\";\n return encodeAbiParameters(\n [{ type: \"uint8\" }, { type: \"uint8\" }],\n [SUSHI_LIQUIDITY_MODES[v.liquidityMode], SUSHI_FEE_MODES[v.feeMode]],\n );\n}\n\n/** Inverse of `encodeSushiVenueData`, for status views. `null` for a payload the adapter would refuse. */\nexport function decodeSushiVenueData(data: Hex): SushiVenueData | null {\n if (data === \"0x\") return { liquidityMode: \"standard\", feeMode: \"direct\" };\n if ((data.length - 2) / 2 !== 64) return null;\n const [liq, fee] = decodeAbiParameters([{ type: \"uint8\" }, { type: \"uint8\" }], data);\n const liquidityMode = (Object.keys(SUSHI_LIQUIDITY_MODES) as SushiLiquidityMode[]).find((k) => SUSHI_LIQUIDITY_MODES[k] === liq);\n const feeMode = (Object.keys(SUSHI_FEE_MODES) as SushiFeeMode[]).find((k) => SUSHI_FEE_MODES[k] === fee);\n return liquidityMode && feeMode ? { liquidityMode, feeMode } : null;\n}\n\n// ── Venue data: StonkBrokers ──\n\n/**\n * `StonkLaunchAdapter.VenueData` — passed through to the pad's `CreateParams`.\n * The pad, not the adapter, validates these (`BadEconomics`); see\n * `validateStonkEconomics` for the mirrored rules.\n */\nexport interface StonkVenueData {\n /** Total supply to mint, in wei. Becomes `launchSupply`. */\n supply: bigint;\n /** Starting market cap, 8-decimal USD. */\n startMcapUsd8: bigint;\n /** Graduation market cap, 8-decimal USD. */\n gradMcapUsd8: bigint;\n /** Opening trade tax, bps. */\n startTaxBps: number;\n /** Per-minute linear tax decay, bps. Derives the curve window with `startTaxBps`. */\n taxDecayPerMinuteBps: number;\n /** Whether the curve accepts sells. */\n sellsEnabled: boolean;\n /** Opening anti-snipe shield (99.99% tax), seconds, folded into the deadline. */\n bufferSecs: number;\n /** Unsold supply at graduation: 0 = burn, 1 = single-sided LP. */\n unsoldMode: number;\n /** No closing deadline. Requires sells and a post-tax in [100, 500]. */\n openEnded: boolean;\n /** Tax after the decay schedule ends, bps. 0 unless `openEnded`. */\n postTaxBps: number;\n /** AMM `bond()` mints the locked LP on. */\n bondVenue: number;\n /** Per-wallet buy cap, ppm of supply. 0 = uncapped; at most 42069. */\n maxBuyPpm: number;\n}\n\n/** StonkBrokers pad `bounds()` and `createLaunch` limits, read live on every pad 2026-08-28. */\nexport const STONK_BOUNDS = {\n minStartMcapUsd8: 10n ** 11n, // $1,000\n maxStartMcapUsd8: 10n ** 14n, // $1,000,000\n minGradMcapUsd8: 5n * 10n ** 12n, // $50,000\n maxGradMcapUsd8: 10n ** 15n, // $10,000,000\n maxStartTaxBps: 9_900,\n minWindowSecs: 600,\n maxWindowSecs: 5_940,\n maxBuyPpm: 42_069,\n} as const;\n\n/**\n * Default StonkBrokers economics. Every value is inside the pad's live bounds,\n * and the tax schedule / buffer / modes are the ones the fork suite\n * (`StonkLaunchRobinhoodFork.t.sol`) launched with on 4663:\n * - 1,000,000,000 tokens — the same float as a Sushi launch, so the 20%\n * reserve cap means the same number on both venues.\n * - $10,000 start / $100,000 graduation market cap.\n * - 1000 bps opening tax decaying 100 bps/minute ⇒ a derived 600 s window\n * (the pad's minimum), plus a 60 s opening anti-snipe buffer.\n * - sells enabled, unsold supply BURNED, not open-ended (so post-tax 0),\n * bond venue 0, no per-wallet cap (a cap would also cap the fund's own\n * dev buy — `BuyCapExceeded`).\n */\nexport const STONK_DEFAULTS: StonkVenueData = {\n supply: 10n ** 27n,\n startMcapUsd8: 10n ** 12n,\n gradMcapUsd8: 10n ** 13n,\n startTaxBps: 1_000,\n taxDecayPerMinuteBps: 100,\n sellsEnabled: true,\n bufferSecs: 60,\n unsoldMode: 0,\n openEnded: false,\n postTaxBps: 0,\n bondVenue: 0,\n maxBuyPpm: 0,\n};\n\nconst STONK_VENUE_DATA_TUPLE = {\n type: \"tuple\",\n components: [\n { name: \"supply\", type: \"uint256\" },\n { name: \"startMcapUsd8\", type: \"uint64\" },\n { name: \"gradMcapUsd8\", type: \"uint64\" },\n { name: \"startTaxBps\", type: \"uint16\" },\n { name: \"taxDecayPerMinuteBps\", type: \"uint16\" },\n { name: \"sellsEnabled\", type: \"bool\" },\n { name: \"bufferSecs\", type: \"uint32\" },\n { name: \"unsoldMode\", type: \"uint8\" },\n { name: \"openEnded\", type: \"bool\" },\n { name: \"postTaxBps\", type: \"uint16\" },\n { name: \"bondVenue\", type: \"uint8\" },\n { name: \"maxBuyPpm\", type: \"uint32\" },\n ],\n} as const;\n\n/** `abi.encode(StonkLaunchAdapter.VenueData)` — a static struct, so twelve bare words. */\nexport function encodeStonkVenueData(v: StonkVenueData): Hex {\n return encodeAbiParameters([STONK_VENUE_DATA_TUPLE], [v]);\n}\n\n/** Inverse of `encodeStonkVenueData`, for status views. */\nexport function decodeStonkVenueData(data: Hex): StonkVenueData | null {\n try {\n const [v] = decodeAbiParameters([STONK_VENUE_DATA_TUPLE], data);\n return { ...v };\n } catch {\n return null;\n }\n}\n\n// ── Init ──\n\nexport interface LaunchpadInitParams {\n launchAdapter: Address;\n swapAdapter: Address;\n assetIn: bigint;\n quoteToken: Address;\n minQuoteOut: bigint;\n quoteSwapData: Hex;\n feeSwapData: Hex;\n launchSupply: bigint;\n reserveAmount: bigint;\n minTokensOut: bigint;\n claimWindow: bigint;\n deadline: bigint;\n settleSlippageBps: bigint;\n maxFeeIn: bigint;\n name: string;\n symbol: string;\n venueData: Hex;\n}\n\n/** `LaunchpadStrategy.InitParams`, field for field. */\nexport const INIT_PARAMS_TUPLE = {\n type: \"tuple\",\n components: [\n { name: \"launchAdapter\", type: \"address\" },\n { name: \"swapAdapter\", type: \"address\" },\n { name: \"assetIn\", type: \"uint256\" },\n { name: \"quoteToken\", type: \"address\" },\n { name: \"minQuoteOut\", type: \"uint256\" },\n { name: \"quoteSwapData\", type: \"bytes\" },\n { name: \"feeSwapData\", type: \"bytes\" },\n { name: \"launchSupply\", type: \"uint256\" },\n { name: \"reserveAmount\", type: \"uint256\" },\n { name: \"minTokensOut\", type: \"uint256\" },\n { name: \"claimWindow\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint64\" },\n { name: \"settleSlippageBps\", type: \"uint256\" },\n { name: \"maxFeeIn\", type: \"uint256\" },\n { name: \"name\", type: \"string\" },\n { name: \"symbol\", type: \"string\" },\n { name: \"venueData\", type: \"bytes\" },\n ],\n} as const;\n\n/** `abi.encode(InitParams)` — one dynamic tuple, matching `abi.decode(data, (InitParams))`. */\nexport function buildInitData(p: LaunchpadInitParams): Hex {\n return encodeAbiParameters([INIT_PARAMS_TUPLE], [p]);\n}\n\n// ── Batch calls ──\n\n/**\n * Execute batch: `asset.approve(clone, assetIn)` then `clone.execute()`.\n * `_execute` pulls exactly `assetIn` from the vault and pushes the unspent\n * remainder back in the same call.\n */\nexport function buildExecuteCalls(clone: Address, asset: Address, assetIn: bigint): BatchCall[] {\n return [\n {\n target: asset,\n data: encodeFunctionData({ abi: ERC20_ABI, functionName: \"approve\", args: [clone, assetIn] }),\n value: 0n,\n },\n {\n target: clone,\n data: encodeFunctionData({ abi: BASE_STRATEGY_ABI, functionName: \"execute\" }),\n value: 0n,\n },\n ];\n}\n\n/**\n * Settle batch: `clone.settle()`. Reverts `ClaimWindowStillOpen` until the\n * claim window closes or the proposal's own duration has passed.\n */\nexport function buildSettleCalls(clone: Address): BatchCall[] {\n return [\n {\n target: clone,\n data: encodeFunctionData({ abi: BASE_STRATEGY_ABI, functionName: \"settle\" }),\n value: 0n,\n },\n ];\n}\n\n// ── Direct (non-batch) calls ──\n\n/** `claim()` — the caller takes their own slice of the reserve. */\nexport function encodeClaimCalldata(): Hex {\n return encodeFunctionData({ abi: LAUNCHPAD_STRATEGY_ABI, functionName: \"claim\" });\n}\n\n/** `claimFor(holder)` — permissionless; pays the HOLDER, never the caller. */\nexport function encodeClaimForCalldata(holder: Address): Hex {\n return encodeFunctionData({ abi: LAUNCHPAD_STRATEGY_ABI, functionName: \"claimFor\", args: [holder] });\n}\n\n/** `adapter.collectFees(launchRef)` — permissionless; pays the fund's vault. */\nexport function encodeCollectFeesCalldata(launchRef: Hex): Hex {\n return encodeFunctionData({ abi: LAUNCH_ADAPTER_ABI, functionName: \"collectFees\", args: [launchRef] });\n}\n\n/** `adapter.finalize(launchRef)` — graduate + bond on Stonk; a no-op on Sushi. */\nexport function encodeFinalizeCalldata(launchRef: Hex): Hex {\n return encodeFunctionData({ abi: LAUNCH_ADAPTER_ABI, functionName: \"finalize\", args: [launchRef] });\n}\n\n// ── Pure economics the preflight and the drawdown guard share ──\n\n/**\n * The drawdown a launch proposal must declare to be settleable.\n *\n * v1 books settle P&L as the vault-asset balance delta and credits nothing for\n * the launch reserve (design.md, \"P&L: a launch settles as a loss\"), so the\n * share price at settle is roughly `(totalAssets - assetIn) / supply`. The\n * governor refuses `pps < ppsAtExecute × (1 − maxDrawdownBps)`, hence\n *\n * required = ceil(assetIn × 10_000 / totalAssets) + DRAWDOWN_HEADROOM_BPS\n *\n * Returns `null` when the vault holds nothing (no share price to protect).\n */\nexport function requiredDrawdownBps(assetIn: bigint, totalAssets: bigint): number | null {\n if (totalAssets === 0n) return null;\n const lossBps = (assetIn * 10_000n + totalAssets - 1n) / totalAssets;\n return Number(lossBps) + DRAWDOWN_HEADROOM_BPS;\n}\n\nexport interface DrawdownDecision {\n /** The value to declare on the proposal's risk envelope. */\n maxDrawdownBps: number;\n /** True when the user did not pass --max-drawdown-bps and the default was derived. */\n derived: boolean;\n}\n\n/**\n * Pick the proposal's `maxDrawdownBps` for a launch, or refuse.\n *\n * - no user value → the required figure;\n * - a user value below the required figure → REFUSE: the proposal could be\n * voted and executed but `settleProposal` would revert\n * `SettlePriceBelowFloor`, leaving only the owner's `unstick` / emergency\n * path to finish it;\n * - a required figure above `MAX_STAMP_DRAWDOWN_BPS` → REFUSE: the governor\n * caps the floor at 90%, so no declaration makes it settleable.\n */\nexport function decideLaunchDrawdown(\n userValue: string | undefined,\n assetIn: bigint,\n totalAssets: bigint,\n fmtAssetIn: string,\n fmtTotalAssets: string,\n): DrawdownDecision {\n const required = requiredDrawdownBps(assetIn, totalAssets);\n if (required === null) {\n throw new LaunchpadInputError(\n \"The vault's totalAssets() is 0 — there is no capital to launch with and no share price to size a drawdown against.\",\n );\n }\n const why =\n `A launch settles as a vault-asset LOSS of about --asset-in (the reserve is a dividend in kind; v1 books no ` +\n `value for the launch token). ${fmtAssetIn} of ${fmtTotalAssets} is ${required - DRAWDOWN_HEADROOM_BPS} bps of ` +\n `the vault, plus ${DRAWDOWN_HEADROOM_BPS} bps headroom = ${required} bps.`;\n if (required > MAX_STAMP_DRAWDOWN_BPS) {\n throw new LaunchpadInputError(\n `${why} The governor caps the settle-price floor at ${MAX_STAMP_DRAWDOWN_BPS} bps (MAX_STAMP_DRAWDOWN_BPS), ` +\n `so this proposal could never settle through settleProposal. Lower --asset-in.`,\n );\n }\n if (userValue === undefined) return { maxDrawdownBps: required, derived: true };\n const user = Number(userValue);\n if (!Number.isInteger(user) || user < 0 || user > 10_000) {\n throw new LaunchpadInputError(`--max-drawdown-bps must be an integer 0-10000 (got ${userValue})`);\n }\n if (user < required) {\n throw new LaunchpadInputError(\n `--max-drawdown-bps ${user} is below the ${required} bps this launch needs to settle. ${why} ` +\n `Below that, settleProposal reverts SettlePriceBelowFloor and only the vault owner's unstick/emergency path ` +\n `can finish the proposal. Omit --max-drawdown-bps to use ${required}, or pass a value >= ${required}.`,\n );\n }\n return { maxDrawdownBps: user, derived: false };\n}\n\n/** `ceil(a × b / c)`. */\nfunction mulDivCeil(a: bigint, b: bigint, c: bigint): bigint {\n return (a * b + c - 1n) / c;\n}\n\n/**\n * The cost of the venue's native launch fee in the token that PAYS for it,\n * mirroring `LaunchpadStrategy._execute` / `_acquireFeeToken`:\n * - fee token == vault asset → held back from the budget; cost = fee, in ASSET;\n * - fee token == quote → paid out of the quote; cost = fee, in QUOTE;\n * - otherwise → bought by swapping quote, sized from the swap\n * adapter's forward quote `out` for `probe` quote, plus `settleSlippageBps`\n * of headroom; cost = ceil(fee × probe / out) × (1 + slip), in QUOTE.\n * `fee == 0` costs nothing.\n */\nexport function liveFeeCost(args: {\n feeToken: Address;\n feeAmount: bigint;\n asset: Address;\n quoteToken: Address;\n settleSlippageBps: bigint;\n /** Third-token lane only: the quote balance the strategy will probe with. */\n probe?: bigint;\n /** Third-token lane only: `swapAdapter.quote(quote, feeToken, probe, feeSwapData)`. */\n probeOut?: bigint;\n}): { cost: bigint; payIn: \"asset\" | \"quote\" | \"none\"; lane: \"none\" | \"asset\" | \"quote\" | \"swap\" } {\n const { feeToken, feeAmount, asset, quoteToken, settleSlippageBps } = args;\n if (feeAmount === 0n) return { cost: 0n, payIn: \"none\", lane: \"none\" };\n if (sameAddr(feeToken, asset)) return { cost: feeAmount, payIn: \"asset\", lane: \"asset\" };\n if (sameAddr(feeToken, quoteToken)) return { cost: feeAmount, payIn: \"quote\", lane: \"quote\" };\n const probe = args.probe ?? 0n;\n const out = args.probeOut ?? 0n;\n if (probe === 0n || out === 0n) {\n throw new LaunchpadInputError(\n `The launch fee is charged in ${feeToken}, which is neither the vault asset nor the quote, and the swap ` +\n `adapter returned no quote for it — execute would revert FeeAcquisitionFailed. Pass --fee-swap-route.`,\n );\n }\n const amountIn = mulDivCeil(feeAmount, probe, out);\n const cost = (amountIn * (10_000n + settleSlippageBps)) / 10_000n;\n return { cost, payIn: \"quote\", lane: \"swap\" };\n}\n\n/** Default `maxFeeIn`: the live cost plus `MAX_FEE_HEADROOM_BPS`, rounded up. */\nexport function defaultMaxFeeIn(liveCost: bigint): bigint {\n if (liveCost === 0n) return 0n;\n return mulDivCeil(liveCost, 10_000n + MAX_FEE_HEADROOM_BPS, 10_000n);\n}\n\n// ── Flag parsing (pure) ──\n\nexport type LaunchpadOpts = Record<string, string | boolean | undefined>;\n\n/** Chain-derived inputs resolved by commands/launchpad-preflight.ts. */\nexport interface LaunchpadResolved {\n venue: LaunchVenue;\n launchAdapter: Address;\n swapAdapter: Address;\n /** `IERC4626(vault).asset()`. */\n asset: Address;\n assetDecimals: number;\n quoteToken: Address;\n quoteDecimals: number;\n assetIn: bigint;\n minQuoteOut: bigint;\n quoteSwapData: Hex;\n feeSwapData: Hex;\n maxFeeIn: bigint;\n deadline: bigint;\n settleSlippageBps: bigint;\n /** The risk-envelope drawdown the proposal must declare (see `decideLaunchDrawdown`). */\n maxDrawdownBps: number;\n}\n\nexport function sameAddr(a: string, b: string): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n\nexport function parseVenue(raw: string | boolean | undefined): LaunchVenue {\n const v = (typeof raw === \"string\" ? raw : \"sushi\").toLowerCase();\n if (v !== \"sushi\" && v !== \"stonk\") {\n throw new LaunchpadInputError(`--venue must be sushi or stonk (got ${raw})`);\n }\n return v;\n}\n\n/** Duration string (`7d`, `12h`, `30m`, `90s`, bare seconds) → seconds. */\nexport function parseDurationSeconds(input: string): bigint {\n const match = input.trim().match(/^(\\d+)(d|h|m|s)?$/);\n if (!match) throw new LaunchpadInputError(`Invalid duration: ${input} (use e.g. 7d, 12h, 30m)`);\n const value = BigInt(match[1]);\n switch (match[2]) {\n case \"d\":\n return value * 86_400n;\n case \"h\":\n return value * 3_600n;\n case \"m\":\n return value * 60n;\n default:\n return value;\n }\n}\n\nexport function parseSettleSlippageBps(raw: string | boolean | undefined): bigint {\n if (raw === undefined) return DEFAULT_SETTLE_SLIPPAGE_BPS;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0 || BigInt(n) > MAX_SLIPPAGE_BPS) {\n throw new LaunchpadInputError(\n `--settle-slippage-bps must be an integer in [1, ${MAX_SLIPPAGE_BPS}] (got ${raw}). Zero is refused on chain ` +\n `(InvalidSlippage): it would turn every settle-time conversion into a raw in-kind delivery.`,\n );\n }\n return BigInt(n);\n}\n\n/** Human amount → base units, with a flag-named error. */\nexport function parseAmountFlag(raw: string | boolean | undefined, decimals: number, flag: string): bigint {\n if (typeof raw !== \"string\" || raw.trim() === \"\") throw new LaunchpadInputError(`${flag} is required`);\n try {\n const v = parseUnits(raw.trim(), decimals);\n if (v < 0n) throw new Error(\"negative\");\n return v;\n } catch {\n throw new LaunchpadInputError(`${flag} must be a non-negative decimal amount (got ${raw})`);\n }\n}\n\n/**\n * `--deadline`: `0` = none (the adapters skip the check), a unix timestamp\n * (>= 1e9), or a duration from `now` (`3d`). Undefined → `fallback`.\n */\nexport function parseDeadline(raw: string | boolean | undefined, now: bigint, fallback: bigint): bigint {\n if (raw === undefined) return fallback;\n if (typeof raw !== \"string\") throw new LaunchpadInputError(\"--deadline needs a value\");\n const s = raw.trim();\n if (s === \"0\") return 0n;\n if (/^\\d+$/.test(s) && BigInt(s) >= 1_000_000_000n) return BigInt(s);\n return now + parseDurationSeconds(s);\n}\n\nfunction parseIntFlag(raw: string | boolean | undefined, flag: string, fallback: number): number {\n if (raw === undefined) return fallback;\n const n = Number(raw);\n if (!Number.isInteger(n) || n < 0) throw new LaunchpadInputError(`${flag} must be a non-negative integer (got ${raw})`);\n return n;\n}\n\n/** Sushi venue flags → VenueData. `holders` is refused with the adapter's reason. */\nexport function parseSushiVenueData(opts: LaunchpadOpts): SushiVenueData {\n const liq = (typeof opts.liquidity === \"string\" ? opts.liquidity : \"standard\").toLowerCase();\n if (!(liq in SUSHI_LIQUIDITY_MODES)) {\n throw new LaunchpadInputError(`--liquidity must be standard or moon (got ${opts.liquidity})`);\n }\n const fee = (typeof opts.feeMode === \"string\" ? opts.feeMode : \"direct\").toLowerCase();\n if (fee === \"holders\" || fee === \"distribute\" || fee === \"distribute_to_holders\") {\n throw new LaunchpadInputError(\n \"--fee-mode holders (DISTRIBUTE_TO_HOLDERS) is refused by SushiLaunchAdapter (DispositionUnsupported): its fee \" +\n \"share accrues on a per-token HolderRewards contract that nothing in this stack collects for the vault. \" +\n \"Use direct, burn or buyback.\",\n );\n }\n if (!(fee in SUSHI_FEE_MODES)) {\n throw new LaunchpadInputError(`--fee-mode must be direct, burn or buyback (got ${opts.feeMode})`);\n }\n return { liquidityMode: liq as SushiLiquidityMode, feeMode: fee as SushiFeeMode };\n}\n\n/** Stonk venue flags → VenueData, defaults from `STONK_DEFAULTS`. */\nexport function parseStonkVenueData(opts: LaunchpadOpts): StonkVenueData {\n const d = STONK_DEFAULTS;\n const supply =\n opts.stonkSupply === undefined ? d.supply : parseAmountFlag(opts.stonkSupply, LAUNCH_TOKEN_DECIMALS, \"--stonk-supply\");\n const startMcapUsd8 =\n opts.startMcap === undefined ? d.startMcapUsd8 : parseAmountFlag(opts.startMcap, 8, \"--start-mcap\");\n const gradMcapUsd8 = opts.gradMcap === undefined ? d.gradMcapUsd8 : parseAmountFlag(opts.gradMcap, 8, \"--grad-mcap\");\n const openEnded = opts.openEnded === true;\n let unsoldMode = d.unsoldMode;\n if (opts.unsoldMode !== undefined) {\n const u = String(opts.unsoldMode).toLowerCase();\n if (u === \"burn\" || u === \"0\") unsoldMode = 0;\n else if (u === \"lp\" || u === \"1\") unsoldMode = 1;\n else throw new LaunchpadInputError(`--unsold-mode must be burn or lp (got ${opts.unsoldMode})`);\n }\n return {\n supply,\n startMcapUsd8,\n gradMcapUsd8,\n startTaxBps: parseIntFlag(opts.startTaxBps, \"--start-tax-bps\", d.startTaxBps),\n taxDecayPerMinuteBps: parseIntFlag(opts.taxDecayBps, \"--tax-decay-bps\", d.taxDecayPerMinuteBps),\n sellsEnabled: opts.sellsDisabled !== true,\n bufferSecs: parseIntFlag(opts.bufferSecs, \"--buffer-secs\", d.bufferSecs),\n unsoldMode,\n openEnded,\n // An open-ended curve needs a post-tax in [100, 500]; default to the floor.\n postTaxBps: parseIntFlag(opts.postTaxBps, \"--post-tax-bps\", openEnded ? 100 : d.postTaxBps),\n bondVenue: parseIntFlag(opts.bondVenue, \"--bond-venue\", d.bondVenue),\n maxBuyPpm: parseIntFlag(opts.maxBuyPpm, \"--max-buy-ppm\", d.maxBuyPpm),\n };\n}\n\n/** The venue's supply, as the strategy will verify it at execute. */\nexport function launchSupplyFor(venue: LaunchVenue, stonk?: StonkVenueData): bigint {\n return venue === \"sushi\" ? SUSHI_LAUNCH_SUPPLY : (stonk ?? STONK_DEFAULTS).supply;\n}\n\n/**\n * `--reserve`: whole launch tokens (`100000000`) or a percent of the launch\n * supply (`10%`, up to two decimals).\n */\nexport function parseReserve(raw: string | boolean | undefined, launchSupply: bigint): bigint {\n if (typeof raw !== \"string\" || raw.trim() === \"\") throw new LaunchpadInputError(\"--reserve is required\");\n const s = raw.trim();\n if (s.endsWith(\"%\")) {\n const pct = s.slice(0, -1);\n if (!/^\\d+(\\.\\d{1,2})?$/.test(pct)) throw new LaunchpadInputError(`--reserve percent must look like 10% or 12.5% (got ${raw})`);\n const bps = parseUnits(pct, 2); // percent × 100 = bps\n return (launchSupply * bps) / 10_000n;\n }\n return parseAmountFlag(s, LAUNCH_TOKEN_DECIMALS, \"--reserve\");\n}\n\n/** Every launch-token amount/params check that needs no chain. */\nexport function validateLaunchpadParams(p: LaunchpadInitParams, venue: LaunchVenue, stonk?: StonkVenueData): string[] {\n const errors: string[] = [];\n const ZERO_ADDR = \"0x0000000000000000000000000000000000000000\";\n if (sameAddr(p.launchAdapter, ZERO_ADDR)) errors.push(`no ${venue} launch adapter on this network (ZeroAddress)`);\n if (sameAddr(p.swapAdapter, ZERO_ADDR)) errors.push(\"no swap adapter on this network — pass --swap-adapter (ZeroAddress)\");\n if (sameAddr(p.quoteToken, ZERO_ADDR)) errors.push(\"--quote resolved to the zero address (ZeroAddress)\");\n if (p.assetIn === 0n) errors.push(\"--asset-in must be nonzero — there is no budget to deploy (InvalidAmount)\");\n\n if (p.reserveAmount === 0n) {\n errors.push(\"--reserve must be nonzero (ZeroReserve): the holders' claim is the reason this template may spend the float\");\n } else {\n const maxReserve = (p.launchSupply * MAX_RESERVE_BPS) / 10_000n;\n if (p.reserveAmount > maxReserve) {\n errors.push(\n `--reserve ${p.reserveAmount} wei exceeds 20% of the ${p.launchSupply} wei launch supply ` +\n `(max ${maxReserve}; MAX_RESERVE_BPS) (ReserveTooLarge)`,\n );\n }\n }\n\n if (p.claimWindow === 0n || p.claimWindow > MAX_CLAIM_WINDOW_SECONDS) {\n errors.push(`--claim-window must be > 0 and <= 14d (MAX_CLAIM_WINDOW) (InvalidClaimWindow)`);\n }\n if (p.settleSlippageBps === 0n || p.settleSlippageBps > MAX_SLIPPAGE_BPS) {\n errors.push(`--settle-slippage-bps must be in [1, ${MAX_SLIPPAGE_BPS}] (InvalidSlippage)`);\n }\n if (!p.name) errors.push(\"--token-name is required\");\n if (!p.symbol) errors.push(\"--token-symbol is required\");\n\n if (venue === \"sushi\") {\n // TokenConfig limits enforced by the venue.\n if (Buffer.byteLength(p.name, \"utf8\") > 64) errors.push(\"--token-name must be at most 64 bytes on Sushi\");\n if (Buffer.byteLength(p.symbol, \"utf8\") > 16) errors.push(\"--token-symbol must be at most 16 bytes on Sushi\");\n if (p.launchSupply !== SUSHI_LAUNCH_SUPPLY) {\n errors.push(`Sushi mints a fixed ${SUSHI_LAUNCH_SUPPLY} wei supply; ${p.launchSupply} would revert SupplyMismatch`);\n }\n // The venue mints the launcher nothing, so the dev buy IS the reserve and\n // its venue-enforced floor must cover it.\n if (p.minTokensOut < p.reserveAmount) {\n errors.push(\n `--min-tokens-out must be >= --reserve on Sushi: the reserve is the dev buy, and a lower floor lets a ` +\n `sandwiched buy deliver less than the holders were promised (ReserveFloorBelowReserve)`,\n );\n }\n }\n\n if (venue === \"stonk\") {\n if (!stonk) {\n errors.push(\"StonkBrokers needs its venue economics\");\n return errors;\n }\n if (stonk.supply !== p.launchSupply) errors.push(\"--stonk-supply must equal the declared launch supply\");\n if (p.reserveAmount >= stonk.supply) {\n errors.push(\"--reserve must be strictly below --stonk-supply (ReserveExceedsSupply): nothing would be left to arm\");\n }\n // Stonk mints the whole supply to the fund and the reserve is a free\n // allocation; `minTokensOut` floors only the dev buy on the curve. A zero\n // floor would make that buy a sandwich budget.\n if (p.minTokensOut === 0n) {\n errors.push(\n \"--min-tokens-out must be nonzero: it is the slippage floor on the curve dev buy (quote it through the \" +\n \"StonkBrokers lens off-chain)\",\n );\n }\n errors.push(...validateStonkEconomics(stonk));\n }\n return errors;\n}\n\n/**\n * The pad's documented `createLaunch` rules, mirrored for an early, readable\n * failure. The PAD stays authoritative (`BadEconomics`) — the adapter passes\n * these through un-checked on purpose.\n */\nexport function validateStonkEconomics(v: StonkVenueData): string[] {\n const errors: string[] = [];\n const b = STONK_BOUNDS;\n if (v.supply === 0n) errors.push(\"--stonk-supply must be nonzero\");\n if (v.startMcapUsd8 < b.minStartMcapUsd8 || v.startMcapUsd8 > b.maxStartMcapUsd8) {\n errors.push(\"--start-mcap must be within $1,000–$1,000,000 (pad bounds())\");\n }\n if (v.gradMcapUsd8 < b.minGradMcapUsd8 || v.gradMcapUsd8 > b.maxGradMcapUsd8) {\n errors.push(\"--grad-mcap must be within $50,000–$10,000,000 (pad bounds())\");\n }\n if (v.gradMcapUsd8 <= v.startMcapUsd8) errors.push(\"--grad-mcap must be above --start-mcap\");\n if (v.startTaxBps > b.maxStartTaxBps) errors.push(`--start-tax-bps must be <= ${b.maxStartTaxBps}`);\n if (v.startTaxBps === 0) {\n if (v.taxDecayPerMinuteBps !== 0) errors.push(\"--start-tax-bps 0 requires --tax-decay-bps 0\");\n if (!v.openEnded) errors.push(\"--start-tax-bps 0 requires --open-ended (there is no decay window to derive)\");\n } else if (v.taxDecayPerMinuteBps === 0) {\n errors.push(\"a nonzero --start-tax-bps requires a nonzero --tax-decay-bps\");\n } else if (v.startTaxBps % v.taxDecayPerMinuteBps !== 0) {\n errors.push(\"--start-tax-bps must be divisible by --tax-decay-bps\");\n } else {\n const windowSecs = (v.startTaxBps / v.taxDecayPerMinuteBps) * 60;\n if (windowSecs < b.minWindowSecs || windowSecs > b.maxWindowSecs) {\n errors.push(\n `the derived curve window (start-tax / decay × 60 = ${windowSecs}s) must be within ` +\n `[${b.minWindowSecs}, ${b.maxWindowSecs}]s`,\n );\n }\n }\n if (v.openEnded) {\n if (!v.sellsEnabled) errors.push(\"--open-ended requires sells (drop --sells-disabled)\");\n if (v.postTaxBps < 100 || v.postTaxBps > 500) errors.push(\"--open-ended requires --post-tax-bps within [100, 500]\");\n } else if (v.postTaxBps !== 0) {\n errors.push(\"--post-tax-bps must be 0 unless --open-ended\");\n }\n if (v.unsoldMode > 1) errors.push(\"--unsold-mode must be burn or lp\");\n if (v.maxBuyPpm > b.maxBuyPpm) errors.push(`--max-buy-ppm must be <= ${b.maxBuyPpm}`);\n if (v.bondVenue > 255) errors.push(\"--bond-venue must fit a uint8\");\n if (v.bufferSecs > 0xffff_ffff) errors.push(\"--buffer-secs must fit a uint32\");\n return errors;\n}\n\n/**\n * Flags + resolved chain inputs → InitParams. Pure; throws\n * `LaunchpadInputError` listing EVERY problem at once.\n */\nexport function buildLaunchpadInitParams(\n opts: LaunchpadOpts,\n r: LaunchpadResolved,\n): { params: LaunchpadInitParams; stonk?: StonkVenueData; sushi?: SushiVenueData } {\n let venueData: Hex;\n let stonk: StonkVenueData | undefined;\n let sushi: SushiVenueData | undefined;\n if (r.venue === \"sushi\") {\n sushi = parseSushiVenueData(opts);\n venueData = encodeSushiVenueData(sushi);\n } else {\n stonk = parseStonkVenueData(opts);\n venueData = encodeStonkVenueData(stonk);\n }\n const launchSupply = launchSupplyFor(r.venue, stonk);\n\n if (typeof opts.claimWindow !== \"string\") throw new LaunchpadInputError(\"--claim-window is required (e.g. 7d, at most 14d)\");\n const params: LaunchpadInitParams = {\n launchAdapter: r.launchAdapter,\n swapAdapter: r.swapAdapter,\n assetIn: r.assetIn,\n quoteToken: r.quoteToken,\n minQuoteOut: r.minQuoteOut,\n quoteSwapData: r.quoteSwapData,\n feeSwapData: r.feeSwapData,\n launchSupply,\n reserveAmount: parseReserve(opts.reserve, launchSupply),\n minTokensOut: parseAmountFlag(opts.minTokensOut, LAUNCH_TOKEN_DECIMALS, \"--min-tokens-out\"),\n claimWindow: parseDurationSeconds(opts.claimWindow),\n deadline: r.deadline,\n settleSlippageBps: r.settleSlippageBps,\n maxFeeIn: r.maxFeeIn,\n name: typeof opts.tokenName === \"string\" ? opts.tokenName : \"\",\n symbol: typeof opts.tokenSymbol === \"string\" ? opts.tokenSymbol : \"\",\n venueData,\n };\n const errors = validateLaunchpadParams(params, r.venue, stonk);\n if (errors.length > 0) {\n throw new LaunchpadInputError(`Launchpad parameters rejected:\\n - ${errors.join(\"\\n - \")}`);\n }\n return { params, stonk, sushi };\n}\n"],"mappings":"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","names":[]}
|