@mania-labs/mania-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +397 -0
- package/dist/index.d.mts +1116 -0
- package/dist/index.d.ts +1116 -0
- package/dist/index.js +1220 -0
- package/dist/index.mjs +1165 -0
- package/package.json +56 -0
- package/src/.claude/settings.local.json +9 -0
- package/src/abi/ManiaFactoryUpgradeable.json +2183 -0
- package/src/abi.ts +268 -0
- package/src/bondingCurve.ts +319 -0
- package/src/constants.ts +72 -0
- package/src/index.ts +71 -0
- package/src/mania.ts +652 -0
- package/src/types.ts +238 -0
- package/src/utils.ts +154 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Main SDK class
|
|
2
|
+
export { ManiaSDK } from "./mania.js";
|
|
3
|
+
|
|
4
|
+
// Bonding curve utilities
|
|
5
|
+
export {
|
|
6
|
+
BondingCurve,
|
|
7
|
+
calculateBuyAmount,
|
|
8
|
+
calculateSellAmount,
|
|
9
|
+
} from "./bondingCurve.js";
|
|
10
|
+
|
|
11
|
+
// Types
|
|
12
|
+
export type {
|
|
13
|
+
BondingCurveState,
|
|
14
|
+
GlobalState,
|
|
15
|
+
SetParamsInput,
|
|
16
|
+
CreateTokenParams,
|
|
17
|
+
CreateAndBuyParams,
|
|
18
|
+
BuyParams,
|
|
19
|
+
SellParams,
|
|
20
|
+
MigrateParams,
|
|
21
|
+
BuyQuote,
|
|
22
|
+
SellQuote,
|
|
23
|
+
TokenInfo,
|
|
24
|
+
TransactionResult,
|
|
25
|
+
CreateEvent,
|
|
26
|
+
TradeEvent,
|
|
27
|
+
CompleteEvent,
|
|
28
|
+
MigrationEvent,
|
|
29
|
+
ManiaSDKConfig,
|
|
30
|
+
SlippageConfig,
|
|
31
|
+
} from "./types.js";
|
|
32
|
+
|
|
33
|
+
// Constants
|
|
34
|
+
export {
|
|
35
|
+
PROTOCOL_FEE_BASIS_POINTS,
|
|
36
|
+
CREATOR_FEE_BASIS_POINTS,
|
|
37
|
+
TOTAL_FEE_BASIS_POINTS,
|
|
38
|
+
MAX_FEE_BASIS_POINTS,
|
|
39
|
+
MIGRATION_THRESHOLD,
|
|
40
|
+
TOKENS_FOR_LP,
|
|
41
|
+
MAX_MIGRATE_FEES,
|
|
42
|
+
UNISWAP_FEE_TIER,
|
|
43
|
+
TICK_LOWER,
|
|
44
|
+
TICK_UPPER,
|
|
45
|
+
DEFAULT_SLIPPAGE_BPS,
|
|
46
|
+
BPS_DENOMINATOR,
|
|
47
|
+
CHAIN_CONFIGS,
|
|
48
|
+
getChainConfig,
|
|
49
|
+
type ChainConfig,
|
|
50
|
+
} from "./constants.js";
|
|
51
|
+
|
|
52
|
+
// Utility functions
|
|
53
|
+
export {
|
|
54
|
+
formatEthValue,
|
|
55
|
+
formatTokenAmount,
|
|
56
|
+
parseEthValue,
|
|
57
|
+
calculateWithSlippage,
|
|
58
|
+
calculateMigrationProgress,
|
|
59
|
+
formatPrice,
|
|
60
|
+
formatMarketCap,
|
|
61
|
+
isValidAddress,
|
|
62
|
+
truncateAddress,
|
|
63
|
+
bpsToPercent,
|
|
64
|
+
percentToBps,
|
|
65
|
+
calculatePriceImpact,
|
|
66
|
+
sleep,
|
|
67
|
+
withRetry,
|
|
68
|
+
} from "./utils.js";
|
|
69
|
+
|
|
70
|
+
// ABIs
|
|
71
|
+
export { MANIA_FACTORY_ABI, ERC20_ABI } from "./abi.js";
|
package/src/mania.ts
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPublicClient,
|
|
3
|
+
createWalletClient,
|
|
4
|
+
http,
|
|
5
|
+
type Address,
|
|
6
|
+
type PublicClient,
|
|
7
|
+
type WalletClient,
|
|
8
|
+
type Chain,
|
|
9
|
+
parseEventLogs,
|
|
10
|
+
} from "viem";
|
|
11
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
12
|
+
import type {
|
|
13
|
+
BondingCurveState,
|
|
14
|
+
GlobalState,
|
|
15
|
+
CreateTokenParams,
|
|
16
|
+
CreateAndBuyParams,
|
|
17
|
+
BuyParams,
|
|
18
|
+
SellParams,
|
|
19
|
+
MigrateParams,
|
|
20
|
+
TokenInfo,
|
|
21
|
+
TransactionResult,
|
|
22
|
+
CreateEvent,
|
|
23
|
+
TradeEvent,
|
|
24
|
+
CompleteEvent,
|
|
25
|
+
MigrationEvent,
|
|
26
|
+
ManiaSDKConfig,
|
|
27
|
+
} from "./types.js";
|
|
28
|
+
import { BondingCurve } from "./bondingCurve.js";
|
|
29
|
+
import {
|
|
30
|
+
DEFAULT_SLIPPAGE_BPS,
|
|
31
|
+
getChainConfig,
|
|
32
|
+
} from "./constants.js";
|
|
33
|
+
import { MANIA_FACTORY_ABI } from "./abi.js";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* ManiaSDK - Official SDK for interacting with the Mania Protocol
|
|
37
|
+
*
|
|
38
|
+
* Mania is an EVM-based token launchpad using bonding curves. Tokens are created
|
|
39
|
+
* with an initial bonding curve that allows trading until a migration threshold
|
|
40
|
+
* (4 ETH) is reached. Once complete, liquidity is migrated to Uniswap V3.
|
|
41
|
+
*/
|
|
42
|
+
export class ManiaSDK {
|
|
43
|
+
public publicClient: PublicClient;
|
|
44
|
+
public walletClient: WalletClient | null = null;
|
|
45
|
+
public readonly factoryAddress: Address;
|
|
46
|
+
public readonly chainId: number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a new ManiaSDK instance
|
|
50
|
+
*
|
|
51
|
+
* @param config - SDK configuration
|
|
52
|
+
* @param config.factoryAddress - ManiaFactory contract address
|
|
53
|
+
* @param config.rpcUrl - RPC URL for the chain
|
|
54
|
+
* @param config.chainId - Chain ID (optional, will be fetched if not provided)
|
|
55
|
+
*/
|
|
56
|
+
constructor(config: ManiaSDKConfig) {
|
|
57
|
+
this.factoryAddress = config.factoryAddress;
|
|
58
|
+
this.chainId = config.chainId ?? 1;
|
|
59
|
+
|
|
60
|
+
this.publicClient = createPublicClient({
|
|
61
|
+
transport: http(config.rpcUrl),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create SDK instance from chain ID using default configuration
|
|
67
|
+
*
|
|
68
|
+
* @param chainId - Chain ID
|
|
69
|
+
* @param rpcUrl - RPC URL (optional, uses public RPC if not provided)
|
|
70
|
+
* @returns ManiaSDK instance
|
|
71
|
+
*/
|
|
72
|
+
static fromChainId(chainId: number, rpcUrl?: string): ManiaSDK {
|
|
73
|
+
const chainConfig = getChainConfig(chainId);
|
|
74
|
+
if (!chainConfig) {
|
|
75
|
+
throw new Error(`Unsupported chain ID: ${chainId}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return new ManiaSDK({
|
|
79
|
+
factoryAddress: chainConfig.factoryAddress,
|
|
80
|
+
rpcUrl,
|
|
81
|
+
chainId,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Connect a wallet for signing transactions
|
|
87
|
+
*
|
|
88
|
+
* @param privateKey - Private key (with or without 0x prefix)
|
|
89
|
+
* @param chain - Viem chain object
|
|
90
|
+
*/
|
|
91
|
+
connectWallet(privateKey: string, chain: Chain): void {
|
|
92
|
+
const account = privateKeyToAccount(
|
|
93
|
+
privateKey.startsWith("0x") ? (privateKey as `0x${string}`) : (`0x${privateKey}` as `0x${string}`)
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
this.walletClient = createWalletClient({
|
|
97
|
+
account,
|
|
98
|
+
chain,
|
|
99
|
+
transport: http(),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Connect an existing wallet client
|
|
105
|
+
*
|
|
106
|
+
* @param walletClient - Viem wallet client
|
|
107
|
+
*/
|
|
108
|
+
setWalletClient(walletClient: WalletClient): void {
|
|
109
|
+
this.walletClient = walletClient;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Set a custom public client
|
|
114
|
+
*
|
|
115
|
+
* @param publicClient - Viem public client
|
|
116
|
+
*/
|
|
117
|
+
setPublicClient(publicClient: PublicClient): void {
|
|
118
|
+
this.publicClient = publicClient;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get the connected wallet address
|
|
123
|
+
*/
|
|
124
|
+
getWalletAddress(): Address | undefined {
|
|
125
|
+
return this.walletClient?.account?.address;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ========== READ METHODS ==========
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get global protocol configuration
|
|
132
|
+
*/
|
|
133
|
+
async getGlobalState(): Promise<GlobalState> {
|
|
134
|
+
const result = await this.publicClient.readContract({
|
|
135
|
+
address: this.factoryAddress,
|
|
136
|
+
abi: MANIA_FACTORY_ABI,
|
|
137
|
+
functionName: "global",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const [
|
|
141
|
+
initialized,
|
|
142
|
+
authority,
|
|
143
|
+
feeRecipient,
|
|
144
|
+
initialVirtualTokenReserves,
|
|
145
|
+
initialVirtualEthReserves,
|
|
146
|
+
initialRealTokenReserves,
|
|
147
|
+
tokenTotalSupply,
|
|
148
|
+
feeBasisPoints,
|
|
149
|
+
withdrawAuthority,
|
|
150
|
+
enableMigrate,
|
|
151
|
+
poolMigrationFee,
|
|
152
|
+
] = result as [boolean, Address, Address, bigint, bigint, bigint, bigint, bigint, Address, boolean, bigint];
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
initialized,
|
|
156
|
+
authority,
|
|
157
|
+
feeRecipient,
|
|
158
|
+
initialVirtualTokenReserves,
|
|
159
|
+
initialVirtualEthReserves,
|
|
160
|
+
initialRealTokenReserves,
|
|
161
|
+
tokenTotalSupply,
|
|
162
|
+
feeBasisPoints,
|
|
163
|
+
withdrawAuthority,
|
|
164
|
+
enableMigrate,
|
|
165
|
+
poolMigrationFee,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get bonding curve state for a token
|
|
171
|
+
*
|
|
172
|
+
* @param token - Token address
|
|
173
|
+
*/
|
|
174
|
+
async getBondingCurve(token: Address): Promise<BondingCurveState> {
|
|
175
|
+
const result = await this.publicClient.readContract({
|
|
176
|
+
address: this.factoryAddress,
|
|
177
|
+
abi: MANIA_FACTORY_ABI,
|
|
178
|
+
functionName: "getBondingCurve",
|
|
179
|
+
args: [token],
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const curve = result as {
|
|
183
|
+
virtualTokenReserves: bigint;
|
|
184
|
+
virtualEthReserves: bigint;
|
|
185
|
+
realTokenReserves: bigint;
|
|
186
|
+
realEthReserves: bigint;
|
|
187
|
+
tokenTotalSupply: bigint;
|
|
188
|
+
complete: boolean;
|
|
189
|
+
trackVolume: boolean;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
virtualTokenReserves: curve.virtualTokenReserves,
|
|
194
|
+
virtualEthReserves: curve.virtualEthReserves,
|
|
195
|
+
realTokenReserves: curve.realTokenReserves,
|
|
196
|
+
realEthReserves: curve.realEthReserves,
|
|
197
|
+
tokenTotalSupply: curve.tokenTotalSupply,
|
|
198
|
+
complete: curve.complete,
|
|
199
|
+
trackVolume: curve.trackVolume,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get a BondingCurve instance for calculations
|
|
205
|
+
*
|
|
206
|
+
* @param token - Token address
|
|
207
|
+
*/
|
|
208
|
+
async getBondingCurveInstance(token: Address): Promise<BondingCurve> {
|
|
209
|
+
const [curveState, globalState] = await Promise.all([
|
|
210
|
+
this.getBondingCurve(token),
|
|
211
|
+
this.getGlobalState(),
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
return new BondingCurve(curveState, globalState.feeBasisPoints);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get comprehensive token information
|
|
219
|
+
*
|
|
220
|
+
* @param token - Token address
|
|
221
|
+
*/
|
|
222
|
+
async getTokenInfo(token: Address): Promise<TokenInfo> {
|
|
223
|
+
const curve = await this.getBondingCurveInstance(token);
|
|
224
|
+
const state = curve.getState();
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
address: token,
|
|
228
|
+
bondingCurve: state,
|
|
229
|
+
currentPrice: curve.getCurrentPrice(),
|
|
230
|
+
marketCapEth: curve.getMarketCapEth(),
|
|
231
|
+
migrationProgress: curve.getMigrationProgress(),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get buy quote from contract
|
|
237
|
+
*
|
|
238
|
+
* @param token - Token address
|
|
239
|
+
* @param ethAmount - ETH amount in wei
|
|
240
|
+
*/
|
|
241
|
+
async getBuyQuote(token: Address, ethAmount: bigint): Promise<bigint> {
|
|
242
|
+
const result = await this.publicClient.readContract({
|
|
243
|
+
address: this.factoryAddress,
|
|
244
|
+
abi: MANIA_FACTORY_ABI,
|
|
245
|
+
functionName: "getBuyQuote",
|
|
246
|
+
args: [token, ethAmount],
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return result as bigint;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get sell quote from contract
|
|
254
|
+
*
|
|
255
|
+
* @param token - Token address
|
|
256
|
+
* @param tokenAmount - Token amount
|
|
257
|
+
*/
|
|
258
|
+
async getSellQuote(token: Address, tokenAmount: bigint): Promise<bigint> {
|
|
259
|
+
const result = await this.publicClient.readContract({
|
|
260
|
+
address: this.factoryAddress,
|
|
261
|
+
abi: MANIA_FACTORY_ABI,
|
|
262
|
+
functionName: "getSellQuote",
|
|
263
|
+
args: [token, tokenAmount],
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return result as bigint;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get fee for a given amount
|
|
271
|
+
*
|
|
272
|
+
* @param amount - Amount in wei
|
|
273
|
+
*/
|
|
274
|
+
async getFee(amount: bigint): Promise<bigint> {
|
|
275
|
+
const result = await this.publicClient.readContract({
|
|
276
|
+
address: this.factoryAddress,
|
|
277
|
+
abi: MANIA_FACTORY_ABI,
|
|
278
|
+
functionName: "getFee",
|
|
279
|
+
args: [amount],
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return result as bigint;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get user volume
|
|
287
|
+
*
|
|
288
|
+
* @param user - User address
|
|
289
|
+
*/
|
|
290
|
+
async getUserVolume(user: Address): Promise<bigint> {
|
|
291
|
+
const result = await this.publicClient.readContract({
|
|
292
|
+
address: this.factoryAddress,
|
|
293
|
+
abi: MANIA_FACTORY_ABI,
|
|
294
|
+
functionName: "userVolume",
|
|
295
|
+
args: [user],
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return result as bigint;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ========== WRITE METHODS ==========
|
|
302
|
+
|
|
303
|
+
private getConnectedWallet(): WalletClient {
|
|
304
|
+
if (!this.walletClient?.account) {
|
|
305
|
+
throw new Error("Wallet not connected. Call connectWallet() first.");
|
|
306
|
+
}
|
|
307
|
+
return this.walletClient;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Create a new token with bonding curve
|
|
312
|
+
*
|
|
313
|
+
* @param params - Token creation parameters
|
|
314
|
+
* @returns Transaction result with token address
|
|
315
|
+
*/
|
|
316
|
+
async create(params: CreateTokenParams): Promise<TransactionResult & { tokenAddress?: Address }> {
|
|
317
|
+
const wallet = this.getConnectedWallet();
|
|
318
|
+
|
|
319
|
+
const hash = await wallet.writeContract({
|
|
320
|
+
chain: null,
|
|
321
|
+
account: null,
|
|
322
|
+
address: this.factoryAddress,
|
|
323
|
+
abi: MANIA_FACTORY_ABI,
|
|
324
|
+
functionName: "create",
|
|
325
|
+
args: [params.name, params.symbol, params.uri, params.creator],
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
329
|
+
|
|
330
|
+
// Parse CreateEvent to get token address
|
|
331
|
+
const logs = parseEventLogs({
|
|
332
|
+
abi: MANIA_FACTORY_ABI,
|
|
333
|
+
logs: receipt.logs,
|
|
334
|
+
eventName: "CreateEvent",
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const tokenAddress = logs[0]?.args?.mint as Address | undefined;
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
hash,
|
|
341
|
+
success: receipt.status === "success",
|
|
342
|
+
tokenAddress,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Create a new token and buy in a single transaction
|
|
348
|
+
*
|
|
349
|
+
* @param params - Creation and buy parameters
|
|
350
|
+
* @returns Transaction result with token address
|
|
351
|
+
*/
|
|
352
|
+
async createAndBuy(params: CreateAndBuyParams): Promise<TransactionResult & { tokenAddress?: Address }> {
|
|
353
|
+
const wallet = this.getConnectedWallet();
|
|
354
|
+
|
|
355
|
+
const hash = await wallet.writeContract({
|
|
356
|
+
chain: null,
|
|
357
|
+
account: null,
|
|
358
|
+
address: this.factoryAddress,
|
|
359
|
+
abi: MANIA_FACTORY_ABI,
|
|
360
|
+
functionName: "createAndBuy",
|
|
361
|
+
args: [params.name, params.symbol, params.uri, params.creator, params.minTokensOut],
|
|
362
|
+
value: params.buyAmountEth,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
366
|
+
|
|
367
|
+
// Parse CreateEvent to get token address
|
|
368
|
+
const logs = parseEventLogs({
|
|
369
|
+
abi: MANIA_FACTORY_ABI,
|
|
370
|
+
logs: receipt.logs,
|
|
371
|
+
eventName: "CreateEvent",
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const tokenAddress = logs[0]?.args?.mint as Address | undefined;
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
hash,
|
|
378
|
+
success: receipt.status === "success",
|
|
379
|
+
tokenAddress,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Buy tokens from bonding curve
|
|
385
|
+
*
|
|
386
|
+
* @param params - Buy parameters
|
|
387
|
+
* @returns Transaction result
|
|
388
|
+
*/
|
|
389
|
+
async buy(params: BuyParams): Promise<TransactionResult> {
|
|
390
|
+
const wallet = this.getConnectedWallet();
|
|
391
|
+
|
|
392
|
+
const recipient = params.recipient ?? wallet.account!.address;
|
|
393
|
+
|
|
394
|
+
const hash = await wallet.writeContract({
|
|
395
|
+
chain: null,
|
|
396
|
+
account: null,
|
|
397
|
+
address: this.factoryAddress,
|
|
398
|
+
abi: MANIA_FACTORY_ABI,
|
|
399
|
+
functionName: "buy",
|
|
400
|
+
args: [params.token, params.minTokensOut, recipient],
|
|
401
|
+
value: params.amountEth,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
405
|
+
|
|
406
|
+
return {
|
|
407
|
+
hash,
|
|
408
|
+
success: receipt.status === "success",
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Buy tokens with automatic slippage calculation
|
|
414
|
+
*
|
|
415
|
+
* @param token - Token address
|
|
416
|
+
* @param ethAmount - ETH amount to spend
|
|
417
|
+
* @param slippageBps - Slippage tolerance in basis points (default: 100 = 1%)
|
|
418
|
+
*/
|
|
419
|
+
async buyWithSlippage(
|
|
420
|
+
token: Address,
|
|
421
|
+
ethAmount: bigint,
|
|
422
|
+
slippageBps: number = DEFAULT_SLIPPAGE_BPS
|
|
423
|
+
): Promise<TransactionResult> {
|
|
424
|
+
const curve = await this.getBondingCurveInstance(token);
|
|
425
|
+
const minTokensOut = curve.calculateMinTokensOut(ethAmount, slippageBps);
|
|
426
|
+
|
|
427
|
+
return this.buy({
|
|
428
|
+
token,
|
|
429
|
+
amountEth: ethAmount,
|
|
430
|
+
minTokensOut,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Sell tokens to bonding curve
|
|
436
|
+
*
|
|
437
|
+
* @param params - Sell parameters
|
|
438
|
+
* @returns Transaction result
|
|
439
|
+
*/
|
|
440
|
+
async sell(params: SellParams): Promise<TransactionResult> {
|
|
441
|
+
const wallet = this.getConnectedWallet();
|
|
442
|
+
|
|
443
|
+
const hash = await wallet.writeContract({
|
|
444
|
+
chain: null,
|
|
445
|
+
account: null,
|
|
446
|
+
address: this.factoryAddress,
|
|
447
|
+
abi: MANIA_FACTORY_ABI,
|
|
448
|
+
functionName: "sell",
|
|
449
|
+
args: [params.token, params.amountTokens, params.minEthOut],
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
hash,
|
|
456
|
+
success: receipt.status === "success",
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Sell tokens with automatic slippage calculation
|
|
462
|
+
*
|
|
463
|
+
* @param token - Token address
|
|
464
|
+
* @param tokenAmount - Token amount to sell
|
|
465
|
+
* @param slippageBps - Slippage tolerance in basis points (default: 100 = 1%)
|
|
466
|
+
*/
|
|
467
|
+
async sellWithSlippage(
|
|
468
|
+
token: Address,
|
|
469
|
+
tokenAmount: bigint,
|
|
470
|
+
slippageBps: number = DEFAULT_SLIPPAGE_BPS
|
|
471
|
+
): Promise<TransactionResult> {
|
|
472
|
+
const curve = await this.getBondingCurveInstance(token);
|
|
473
|
+
const minEthOut = curve.calculateMinEthOut(tokenAmount, slippageBps);
|
|
474
|
+
|
|
475
|
+
return this.sell({
|
|
476
|
+
token,
|
|
477
|
+
amountTokens: tokenAmount,
|
|
478
|
+
minEthOut,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Migrate liquidity to Uniswap V3
|
|
484
|
+
*
|
|
485
|
+
* @param params - Migration parameters
|
|
486
|
+
* @returns Transaction result with pool address
|
|
487
|
+
*/
|
|
488
|
+
async migrate(params: MigrateParams): Promise<TransactionResult & { poolAddress?: Address }> {
|
|
489
|
+
const wallet = this.getConnectedWallet();
|
|
490
|
+
|
|
491
|
+
const globalState = await this.getGlobalState();
|
|
492
|
+
|
|
493
|
+
const hash = await wallet.writeContract({
|
|
494
|
+
chain: null,
|
|
495
|
+
account: null,
|
|
496
|
+
address: this.factoryAddress,
|
|
497
|
+
abi: MANIA_FACTORY_ABI,
|
|
498
|
+
functionName: "migrate",
|
|
499
|
+
args: [params.token],
|
|
500
|
+
value: globalState.poolMigrationFee,
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
504
|
+
|
|
505
|
+
// Parse migration event to get pool address
|
|
506
|
+
const logs = parseEventLogs({
|
|
507
|
+
abi: MANIA_FACTORY_ABI,
|
|
508
|
+
logs: receipt.logs,
|
|
509
|
+
eventName: "CompleteManiaAmmMigrationEvent",
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
const poolAddress = logs[0]?.args?.pool as Address | undefined;
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
hash,
|
|
516
|
+
success: receipt.status === "success",
|
|
517
|
+
poolAddress,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// ========== EVENT WATCHING ==========
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Watch for new token creation events
|
|
525
|
+
*
|
|
526
|
+
* @param callback - Callback function for each event
|
|
527
|
+
* @returns Unwatch function
|
|
528
|
+
*/
|
|
529
|
+
watchCreateEvents(callback: (event: CreateEvent) => void): () => void {
|
|
530
|
+
return this.publicClient.watchContractEvent({
|
|
531
|
+
address: this.factoryAddress,
|
|
532
|
+
abi: MANIA_FACTORY_ABI,
|
|
533
|
+
eventName: "CreateEvent",
|
|
534
|
+
onLogs: (logs) => {
|
|
535
|
+
for (const log of logs) {
|
|
536
|
+
const args = log.args as unknown as CreateEvent;
|
|
537
|
+
callback(args);
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Watch for trade events on a specific token
|
|
545
|
+
*
|
|
546
|
+
* @param token - Token address (optional, watches all if not provided)
|
|
547
|
+
* @param callback - Callback function for each event
|
|
548
|
+
* @returns Unwatch function
|
|
549
|
+
*/
|
|
550
|
+
watchTradeEvents(token: Address | undefined, callback: (event: TradeEvent) => void): () => void {
|
|
551
|
+
return this.publicClient.watchContractEvent({
|
|
552
|
+
address: this.factoryAddress,
|
|
553
|
+
abi: MANIA_FACTORY_ABI,
|
|
554
|
+
eventName: "TradeEvent",
|
|
555
|
+
args: token ? { mint: token } : undefined,
|
|
556
|
+
onLogs: (logs) => {
|
|
557
|
+
for (const log of logs) {
|
|
558
|
+
const args = log.args as unknown as TradeEvent;
|
|
559
|
+
callback(args);
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Watch for bonding curve completion events
|
|
567
|
+
*
|
|
568
|
+
* @param callback - Callback function for each event
|
|
569
|
+
* @returns Unwatch function
|
|
570
|
+
*/
|
|
571
|
+
watchCompleteEvents(callback: (event: CompleteEvent) => void): () => void {
|
|
572
|
+
return this.publicClient.watchContractEvent({
|
|
573
|
+
address: this.factoryAddress,
|
|
574
|
+
abi: MANIA_FACTORY_ABI,
|
|
575
|
+
eventName: "CompleteEvent",
|
|
576
|
+
onLogs: (logs) => {
|
|
577
|
+
for (const log of logs) {
|
|
578
|
+
const args = log.args as unknown as CompleteEvent;
|
|
579
|
+
callback(args);
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Watch for migration events
|
|
587
|
+
*
|
|
588
|
+
* @param callback - Callback function for each event
|
|
589
|
+
* @returns Unwatch function
|
|
590
|
+
*/
|
|
591
|
+
watchMigrationEvents(callback: (event: MigrationEvent) => void): () => void {
|
|
592
|
+
return this.publicClient.watchContractEvent({
|
|
593
|
+
address: this.factoryAddress,
|
|
594
|
+
abi: MANIA_FACTORY_ABI,
|
|
595
|
+
eventName: "CompleteManiaAmmMigrationEvent",
|
|
596
|
+
onLogs: (logs) => {
|
|
597
|
+
for (const log of logs) {
|
|
598
|
+
const args = log.args as unknown as MigrationEvent;
|
|
599
|
+
callback(args);
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// ========== UTILITY METHODS ==========
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Check if a token's bonding curve is complete
|
|
609
|
+
*
|
|
610
|
+
* @param token - Token address
|
|
611
|
+
*/
|
|
612
|
+
async isComplete(token: Address): Promise<boolean> {
|
|
613
|
+
const curve = await this.getBondingCurve(token);
|
|
614
|
+
return curve.complete;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Check if a token has been migrated
|
|
619
|
+
*
|
|
620
|
+
* @param token - Token address
|
|
621
|
+
*/
|
|
622
|
+
async isMigrated(token: Address): Promise<boolean> {
|
|
623
|
+
const curve = await this.getBondingCurve(token);
|
|
624
|
+
return (
|
|
625
|
+
curve.realEthReserves === 0n &&
|
|
626
|
+
curve.virtualEthReserves === 0n &&
|
|
627
|
+
curve.realTokenReserves === 0n &&
|
|
628
|
+
curve.virtualTokenReserves === 0n
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Get the factory contract address
|
|
634
|
+
*/
|
|
635
|
+
getFactoryAddress(): Address {
|
|
636
|
+
return this.factoryAddress;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Get the public client
|
|
641
|
+
*/
|
|
642
|
+
getPublicClient(): PublicClient {
|
|
643
|
+
return this.publicClient;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Get the wallet client
|
|
648
|
+
*/
|
|
649
|
+
getWalletClient(): WalletClient | null {
|
|
650
|
+
return this.walletClient;
|
|
651
|
+
}
|
|
652
|
+
}
|