@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/dist/index.js
ADDED
|
@@ -0,0 +1,1220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BPS_DENOMINATOR: () => BPS_DENOMINATOR,
|
|
24
|
+
BondingCurve: () => BondingCurve,
|
|
25
|
+
CHAIN_CONFIGS: () => CHAIN_CONFIGS,
|
|
26
|
+
CREATOR_FEE_BASIS_POINTS: () => CREATOR_FEE_BASIS_POINTS,
|
|
27
|
+
DEFAULT_SLIPPAGE_BPS: () => DEFAULT_SLIPPAGE_BPS,
|
|
28
|
+
ERC20_ABI: () => ERC20_ABI,
|
|
29
|
+
MANIA_FACTORY_ABI: () => MANIA_FACTORY_ABI,
|
|
30
|
+
MAX_FEE_BASIS_POINTS: () => MAX_FEE_BASIS_POINTS,
|
|
31
|
+
MAX_MIGRATE_FEES: () => MAX_MIGRATE_FEES,
|
|
32
|
+
MIGRATION_THRESHOLD: () => MIGRATION_THRESHOLD,
|
|
33
|
+
ManiaSDK: () => ManiaSDK,
|
|
34
|
+
PROTOCOL_FEE_BASIS_POINTS: () => PROTOCOL_FEE_BASIS_POINTS,
|
|
35
|
+
TICK_LOWER: () => TICK_LOWER,
|
|
36
|
+
TICK_UPPER: () => TICK_UPPER,
|
|
37
|
+
TOKENS_FOR_LP: () => TOKENS_FOR_LP,
|
|
38
|
+
TOTAL_FEE_BASIS_POINTS: () => TOTAL_FEE_BASIS_POINTS,
|
|
39
|
+
UNISWAP_FEE_TIER: () => UNISWAP_FEE_TIER,
|
|
40
|
+
bpsToPercent: () => bpsToPercent,
|
|
41
|
+
calculateBuyAmount: () => calculateBuyAmount,
|
|
42
|
+
calculateMigrationProgress: () => calculateMigrationProgress,
|
|
43
|
+
calculatePriceImpact: () => calculatePriceImpact,
|
|
44
|
+
calculateSellAmount: () => calculateSellAmount,
|
|
45
|
+
calculateWithSlippage: () => calculateWithSlippage,
|
|
46
|
+
formatEthValue: () => formatEthValue,
|
|
47
|
+
formatMarketCap: () => formatMarketCap,
|
|
48
|
+
formatPrice: () => formatPrice,
|
|
49
|
+
formatTokenAmount: () => formatTokenAmount,
|
|
50
|
+
getChainConfig: () => getChainConfig,
|
|
51
|
+
isValidAddress: () => isValidAddress,
|
|
52
|
+
parseEthValue: () => parseEthValue,
|
|
53
|
+
percentToBps: () => percentToBps,
|
|
54
|
+
sleep: () => sleep,
|
|
55
|
+
truncateAddress: () => truncateAddress,
|
|
56
|
+
withRetry: () => withRetry
|
|
57
|
+
});
|
|
58
|
+
module.exports = __toCommonJS(index_exports);
|
|
59
|
+
|
|
60
|
+
// src/mania.ts
|
|
61
|
+
var import_viem = require("viem");
|
|
62
|
+
var import_accounts = require("viem/accounts");
|
|
63
|
+
|
|
64
|
+
// src/constants.ts
|
|
65
|
+
var PROTOCOL_FEE_BASIS_POINTS = 70n;
|
|
66
|
+
var CREATOR_FEE_BASIS_POINTS = 30n;
|
|
67
|
+
var TOTAL_FEE_BASIS_POINTS = PROTOCOL_FEE_BASIS_POINTS + CREATOR_FEE_BASIS_POINTS;
|
|
68
|
+
var MAX_FEE_BASIS_POINTS = 1000n;
|
|
69
|
+
var MIGRATION_THRESHOLD = 4000000000000000000n;
|
|
70
|
+
var TOKENS_FOR_LP = 206900000000000000000000000n;
|
|
71
|
+
var MAX_MIGRATE_FEES = 300000000000000n;
|
|
72
|
+
var UNISWAP_FEE_TIER = 3e3;
|
|
73
|
+
var TICK_LOWER = -887220;
|
|
74
|
+
var TICK_UPPER = 887220;
|
|
75
|
+
var DEFAULT_SLIPPAGE_BPS = 100;
|
|
76
|
+
var BPS_DENOMINATOR = 10000n;
|
|
77
|
+
var CHAIN_CONFIGS = {
|
|
78
|
+
// Mega Eth Testnet
|
|
79
|
+
6543: {
|
|
80
|
+
chainId: 6543,
|
|
81
|
+
name: "Mega Eth Testnet",
|
|
82
|
+
factoryAddress: "0x0d593cE47EBA2d15a77ddbAc41BdE6d03CC9241b",
|
|
83
|
+
wethAddress: "0x4200000000000000000000000000000000000006",
|
|
84
|
+
nonfungiblePositionManager: "0xa204A97EF8Bd2E3198f19EB5a804680467BD85f5",
|
|
85
|
+
uniswapV3Factory: "0x619fb6C12c36b57a8bAb05e98F42C43745DCf69f",
|
|
86
|
+
blockExplorer: "https://megaeth-testnet-v2.blockscout.com"
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function getChainConfig(chainId) {
|
|
90
|
+
return CHAIN_CONFIGS[chainId];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/bondingCurve.ts
|
|
94
|
+
var BondingCurve = class _BondingCurve {
|
|
95
|
+
state;
|
|
96
|
+
feeBasisPoints;
|
|
97
|
+
constructor(state, feeBasisPoints) {
|
|
98
|
+
this.state = state;
|
|
99
|
+
this.feeBasisPoints = feeBasisPoints;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get the current bonding curve state
|
|
103
|
+
*/
|
|
104
|
+
getState() {
|
|
105
|
+
return { ...this.state };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Check if the bonding curve is complete (reached migration threshold)
|
|
109
|
+
*/
|
|
110
|
+
isComplete() {
|
|
111
|
+
return this.state.complete;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if the bonding curve has been migrated (all reserves are 0)
|
|
115
|
+
*/
|
|
116
|
+
isMigrated() {
|
|
117
|
+
return this.state.realEthReserves === 0n && this.state.virtualEthReserves === 0n && this.state.realTokenReserves === 0n && this.state.virtualTokenReserves === 0n;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get the current token price in ETH (wei per token)
|
|
121
|
+
*
|
|
122
|
+
* Price is calculated as: virtualEthReserves / virtualTokenReserves
|
|
123
|
+
*/
|
|
124
|
+
getCurrentPrice() {
|
|
125
|
+
if (this.state.virtualTokenReserves === 0n) {
|
|
126
|
+
return 0n;
|
|
127
|
+
}
|
|
128
|
+
return this.state.virtualEthReserves * 10n ** 18n / this.state.virtualTokenReserves;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the market cap in ETH
|
|
132
|
+
*
|
|
133
|
+
* Market cap = current price * total supply
|
|
134
|
+
*/
|
|
135
|
+
getMarketCapEth() {
|
|
136
|
+
const price = this.getCurrentPrice();
|
|
137
|
+
return price * this.state.tokenTotalSupply / 10n ** 18n;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get the progress towards migration threshold (0-100)
|
|
141
|
+
*/
|
|
142
|
+
getMigrationProgress() {
|
|
143
|
+
if (this.state.complete) {
|
|
144
|
+
return 100;
|
|
145
|
+
}
|
|
146
|
+
const progress = this.state.realEthReserves * 100n / MIGRATION_THRESHOLD;
|
|
147
|
+
return Number(progress);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Calculate ETH remaining until migration
|
|
151
|
+
*/
|
|
152
|
+
getEthUntilMigration() {
|
|
153
|
+
if (this.state.realEthReserves >= MIGRATION_THRESHOLD) {
|
|
154
|
+
return 0n;
|
|
155
|
+
}
|
|
156
|
+
return MIGRATION_THRESHOLD - this.state.realEthReserves;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get a quote for buying tokens with a specific ETH amount
|
|
160
|
+
*
|
|
161
|
+
* @param ethAmount - Amount of ETH to spend (in wei)
|
|
162
|
+
* @returns Quote with tokens out, fees, and price impact
|
|
163
|
+
*/
|
|
164
|
+
getBuyQuote(ethAmount) {
|
|
165
|
+
const fee = ethAmount * this.feeBasisPoints / BPS_DENOMINATOR;
|
|
166
|
+
const netEth = ethAmount - fee;
|
|
167
|
+
const tokensOut = this.state.virtualTokenReserves * netEth / (this.state.virtualEthReserves + netEth);
|
|
168
|
+
const cappedTokensOut = tokensOut > this.state.realTokenReserves ? this.state.realTokenReserves : tokensOut;
|
|
169
|
+
const pricePerToken = cappedTokensOut > 0n ? netEth * 10n ** 18n / cappedTokensOut : 0n;
|
|
170
|
+
return {
|
|
171
|
+
tokensOut: cappedTokensOut,
|
|
172
|
+
fee,
|
|
173
|
+
netEth,
|
|
174
|
+
pricePerToken
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get a quote for selling tokens
|
|
179
|
+
*
|
|
180
|
+
* @param tokenAmount - Amount of tokens to sell
|
|
181
|
+
* @returns Quote with ETH out, fees, and price impact
|
|
182
|
+
*/
|
|
183
|
+
getSellQuote(tokenAmount) {
|
|
184
|
+
const ethOutGross = tokenAmount * this.state.virtualEthReserves / (this.state.virtualTokenReserves + tokenAmount);
|
|
185
|
+
const fee = ethOutGross * this.feeBasisPoints / BPS_DENOMINATOR;
|
|
186
|
+
const ethOutNet = ethOutGross - fee;
|
|
187
|
+
const pricePerToken = tokenAmount > 0n ? ethOutNet * 10n ** 18n / tokenAmount : 0n;
|
|
188
|
+
return {
|
|
189
|
+
ethOutGross,
|
|
190
|
+
fee,
|
|
191
|
+
ethOutNet,
|
|
192
|
+
pricePerToken
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Calculate minimum tokens out with slippage tolerance
|
|
197
|
+
*
|
|
198
|
+
* @param ethAmount - Amount of ETH to spend
|
|
199
|
+
* @param slippageBps - Slippage tolerance in basis points (e.g., 100 = 1%)
|
|
200
|
+
* @returns Minimum tokens to receive
|
|
201
|
+
*/
|
|
202
|
+
calculateMinTokensOut(ethAmount, slippageBps) {
|
|
203
|
+
const quote = this.getBuyQuote(ethAmount);
|
|
204
|
+
const slippageMultiplier = BPS_DENOMINATOR - BigInt(slippageBps);
|
|
205
|
+
return quote.tokensOut * slippageMultiplier / BPS_DENOMINATOR;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Calculate minimum ETH out with slippage tolerance
|
|
209
|
+
*
|
|
210
|
+
* @param tokenAmount - Amount of tokens to sell
|
|
211
|
+
* @param slippageBps - Slippage tolerance in basis points (e.g., 100 = 1%)
|
|
212
|
+
* @returns Minimum ETH to receive
|
|
213
|
+
*/
|
|
214
|
+
calculateMinEthOut(tokenAmount, slippageBps) {
|
|
215
|
+
const quote = this.getSellQuote(tokenAmount);
|
|
216
|
+
const slippageMultiplier = BPS_DENOMINATOR - BigInt(slippageBps);
|
|
217
|
+
return quote.ethOutNet * slippageMultiplier / BPS_DENOMINATOR;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Calculate price impact for a buy order
|
|
221
|
+
*
|
|
222
|
+
* @param ethAmount - Amount of ETH to spend
|
|
223
|
+
* @returns Price impact as a percentage (e.g., 2.5 = 2.5%)
|
|
224
|
+
*/
|
|
225
|
+
calculateBuyPriceImpact(ethAmount) {
|
|
226
|
+
const currentPrice = this.getCurrentPrice();
|
|
227
|
+
if (currentPrice === 0n) return 0;
|
|
228
|
+
const quote = this.getBuyQuote(ethAmount);
|
|
229
|
+
if (quote.tokensOut === 0n) return 0;
|
|
230
|
+
const newVirtualEth = this.state.virtualEthReserves + quote.netEth;
|
|
231
|
+
const newVirtualTokens = this.state.virtualTokenReserves - quote.tokensOut;
|
|
232
|
+
const newPrice = newVirtualEth * 10n ** 18n / newVirtualTokens;
|
|
233
|
+
const impact = Number((newPrice - currentPrice) * 10000n / currentPrice) / 100;
|
|
234
|
+
return impact;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Calculate price impact for a sell order
|
|
238
|
+
*
|
|
239
|
+
* @param tokenAmount - Amount of tokens to sell
|
|
240
|
+
* @returns Price impact as a percentage (negative for sells)
|
|
241
|
+
*/
|
|
242
|
+
calculateSellPriceImpact(tokenAmount) {
|
|
243
|
+
const currentPrice = this.getCurrentPrice();
|
|
244
|
+
if (currentPrice === 0n) return 0;
|
|
245
|
+
const quote = this.getSellQuote(tokenAmount);
|
|
246
|
+
const newVirtualEth = this.state.virtualEthReserves - quote.ethOutGross;
|
|
247
|
+
const newVirtualTokens = this.state.virtualTokenReserves + tokenAmount;
|
|
248
|
+
if (newVirtualTokens === 0n) return -100;
|
|
249
|
+
const newPrice = newVirtualEth * 10n ** 18n / newVirtualTokens;
|
|
250
|
+
const impact = Number((newPrice - currentPrice) * 10000n / currentPrice) / 100;
|
|
251
|
+
return impact;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Check if a buy would exceed the migration threshold
|
|
255
|
+
*
|
|
256
|
+
* @param ethAmount - Amount of ETH to spend
|
|
257
|
+
* @returns True if the buy would exceed threshold
|
|
258
|
+
*/
|
|
259
|
+
wouldExceedMigrationThreshold(ethAmount) {
|
|
260
|
+
const fee = ethAmount * this.feeBasisPoints / BPS_DENOMINATOR;
|
|
261
|
+
const netEth = ethAmount - fee;
|
|
262
|
+
return this.state.realEthReserves + netEth > MIGRATION_THRESHOLD;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Calculate maximum ETH that can be spent before hitting migration threshold
|
|
266
|
+
*
|
|
267
|
+
* @returns Maximum ETH amount (in wei)
|
|
268
|
+
*/
|
|
269
|
+
getMaxBuyAmount() {
|
|
270
|
+
if (this.state.complete) {
|
|
271
|
+
return 0n;
|
|
272
|
+
}
|
|
273
|
+
const remainingEth = MIGRATION_THRESHOLD - this.state.realEthReserves;
|
|
274
|
+
const maxEthWithFees = remainingEth * BPS_DENOMINATOR / (BPS_DENOMINATOR - this.feeBasisPoints);
|
|
275
|
+
return maxEthWithFees;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Update the internal state (used after trades)
|
|
279
|
+
*/
|
|
280
|
+
updateState(newState) {
|
|
281
|
+
this.state = { ...newState };
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Create a BondingCurve instance from raw contract data
|
|
285
|
+
*/
|
|
286
|
+
static fromContractData(data, feeBasisPoints) {
|
|
287
|
+
const state = {
|
|
288
|
+
virtualTokenReserves: data[0],
|
|
289
|
+
virtualEthReserves: data[1],
|
|
290
|
+
realTokenReserves: data[2],
|
|
291
|
+
realEthReserves: data[3],
|
|
292
|
+
tokenTotalSupply: data[4],
|
|
293
|
+
complete: data[5],
|
|
294
|
+
trackVolume: data[6]
|
|
295
|
+
};
|
|
296
|
+
return new _BondingCurve(state, feeBasisPoints);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
function calculateBuyAmount(virtualTokenReserves, virtualEthReserves, ethAmount, feeBasisPoints) {
|
|
300
|
+
const fee = ethAmount * feeBasisPoints / BPS_DENOMINATOR;
|
|
301
|
+
const netEth = ethAmount - fee;
|
|
302
|
+
return virtualTokenReserves * netEth / (virtualEthReserves + netEth);
|
|
303
|
+
}
|
|
304
|
+
function calculateSellAmount(virtualTokenReserves, virtualEthReserves, tokenAmount, feeBasisPoints) {
|
|
305
|
+
const ethOutGross = tokenAmount * virtualEthReserves / (virtualTokenReserves + tokenAmount);
|
|
306
|
+
const fee = ethOutGross * feeBasisPoints / BPS_DENOMINATOR;
|
|
307
|
+
return ethOutGross - fee;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/abi.ts
|
|
311
|
+
var MANIA_FACTORY_ABI = [
|
|
312
|
+
{
|
|
313
|
+
type: "function",
|
|
314
|
+
name: "bondingCurves",
|
|
315
|
+
inputs: [{ name: "mint", type: "address" }],
|
|
316
|
+
outputs: [
|
|
317
|
+
{ name: "virtualTokenReserves", type: "uint256" },
|
|
318
|
+
{ name: "virtualEthReserves", type: "uint64" },
|
|
319
|
+
{ name: "realTokenReserves", type: "uint256" },
|
|
320
|
+
{ name: "realEthReserves", type: "uint64" },
|
|
321
|
+
{ name: "tokenTotalSupply", type: "uint256" },
|
|
322
|
+
{ name: "complete", type: "bool" },
|
|
323
|
+
{ name: "trackVolume", type: "bool" }
|
|
324
|
+
],
|
|
325
|
+
stateMutability: "view"
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
type: "function",
|
|
329
|
+
name: "buy",
|
|
330
|
+
inputs: [
|
|
331
|
+
{ name: "token", type: "address" },
|
|
332
|
+
{ name: "minTokensOut", type: "uint256" },
|
|
333
|
+
{ name: "recipient", type: "address" }
|
|
334
|
+
],
|
|
335
|
+
outputs: [],
|
|
336
|
+
stateMutability: "payable"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
type: "function",
|
|
340
|
+
name: "create",
|
|
341
|
+
inputs: [
|
|
342
|
+
{ name: "name", type: "string" },
|
|
343
|
+
{ name: "symbol", type: "string" },
|
|
344
|
+
{ name: "uri", type: "string" },
|
|
345
|
+
{ name: "creator", type: "address" }
|
|
346
|
+
],
|
|
347
|
+
outputs: [{ name: "token", type: "address" }],
|
|
348
|
+
stateMutability: "nonpayable"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
type: "function",
|
|
352
|
+
name: "createAndBuy",
|
|
353
|
+
inputs: [
|
|
354
|
+
{ name: "name", type: "string" },
|
|
355
|
+
{ name: "symbol", type: "string" },
|
|
356
|
+
{ name: "uri", type: "string" },
|
|
357
|
+
{ name: "creator", type: "address" },
|
|
358
|
+
{ name: "minTokensOut", type: "uint256" }
|
|
359
|
+
],
|
|
360
|
+
outputs: [{ name: "token", type: "address" }],
|
|
361
|
+
stateMutability: "payable"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
type: "function",
|
|
365
|
+
name: "getBondingCurve",
|
|
366
|
+
inputs: [{ name: "token", type: "address" }],
|
|
367
|
+
outputs: [
|
|
368
|
+
{
|
|
369
|
+
name: "curve",
|
|
370
|
+
type: "tuple",
|
|
371
|
+
components: [
|
|
372
|
+
{ name: "virtualTokenReserves", type: "uint256" },
|
|
373
|
+
{ name: "virtualEthReserves", type: "uint64" },
|
|
374
|
+
{ name: "realTokenReserves", type: "uint256" },
|
|
375
|
+
{ name: "realEthReserves", type: "uint64" },
|
|
376
|
+
{ name: "tokenTotalSupply", type: "uint256" },
|
|
377
|
+
{ name: "complete", type: "bool" },
|
|
378
|
+
{ name: "trackVolume", type: "bool" }
|
|
379
|
+
]
|
|
380
|
+
}
|
|
381
|
+
],
|
|
382
|
+
stateMutability: "view"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
type: "function",
|
|
386
|
+
name: "getBuyQuote",
|
|
387
|
+
inputs: [
|
|
388
|
+
{ name: "token", type: "address" },
|
|
389
|
+
{ name: "ethAmount", type: "uint64" }
|
|
390
|
+
],
|
|
391
|
+
outputs: [{ name: "tokenAmount", type: "uint256" }],
|
|
392
|
+
stateMutability: "view"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
type: "function",
|
|
396
|
+
name: "getFee",
|
|
397
|
+
inputs: [{ name: "amount", type: "uint64" }],
|
|
398
|
+
outputs: [{ name: "fee", type: "uint128" }],
|
|
399
|
+
stateMutability: "view"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
type: "function",
|
|
403
|
+
name: "getSellQuote",
|
|
404
|
+
inputs: [
|
|
405
|
+
{ name: "token", type: "address" },
|
|
406
|
+
{ name: "amount", type: "uint256" }
|
|
407
|
+
],
|
|
408
|
+
outputs: [{ name: "ethOutput", type: "uint128" }],
|
|
409
|
+
stateMutability: "view"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
type: "function",
|
|
413
|
+
name: "global",
|
|
414
|
+
inputs: [],
|
|
415
|
+
outputs: [
|
|
416
|
+
{ name: "initialized", type: "bool" },
|
|
417
|
+
{ name: "authority", type: "address" },
|
|
418
|
+
{ name: "feeRecipient", type: "address" },
|
|
419
|
+
{ name: "initialVirtualTokenReserves", type: "uint256" },
|
|
420
|
+
{ name: "initialVirtualEthReserves", type: "uint64" },
|
|
421
|
+
{ name: "initialRealTokenReserves", type: "uint256" },
|
|
422
|
+
{ name: "tokenTotalSupply", type: "uint256" },
|
|
423
|
+
{ name: "feeBasisPoints", type: "uint64" },
|
|
424
|
+
{ name: "withdrawAuthority", type: "address" },
|
|
425
|
+
{ name: "enableMigrate", type: "bool" },
|
|
426
|
+
{ name: "poolMigrationFee", type: "uint64" }
|
|
427
|
+
],
|
|
428
|
+
stateMutability: "view"
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
type: "function",
|
|
432
|
+
name: "migrate",
|
|
433
|
+
inputs: [{ name: "token", type: "address" }],
|
|
434
|
+
outputs: [],
|
|
435
|
+
stateMutability: "payable"
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
type: "function",
|
|
439
|
+
name: "sell",
|
|
440
|
+
inputs: [
|
|
441
|
+
{ name: "token", type: "address" },
|
|
442
|
+
{ name: "amount", type: "uint256" },
|
|
443
|
+
{ name: "minEthOutput", type: "uint64" }
|
|
444
|
+
],
|
|
445
|
+
outputs: [],
|
|
446
|
+
stateMutability: "nonpayable"
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
type: "function",
|
|
450
|
+
name: "userVolume",
|
|
451
|
+
inputs: [{ name: "user", type: "address" }],
|
|
452
|
+
outputs: [{ name: "volume", type: "uint256" }],
|
|
453
|
+
stateMutability: "view"
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
type: "event",
|
|
457
|
+
name: "CreateEvent",
|
|
458
|
+
inputs: [
|
|
459
|
+
{ name: "name", type: "string", indexed: false },
|
|
460
|
+
{ name: "symbol", type: "string", indexed: false },
|
|
461
|
+
{ name: "uri", type: "string", indexed: false },
|
|
462
|
+
{ name: "mint", type: "address", indexed: true },
|
|
463
|
+
{ name: "user", type: "address", indexed: true },
|
|
464
|
+
{ name: "creator", type: "address", indexed: true },
|
|
465
|
+
{ name: "timestamp", type: "uint256", indexed: false }
|
|
466
|
+
],
|
|
467
|
+
anonymous: false
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
type: "event",
|
|
471
|
+
name: "TradeEvent",
|
|
472
|
+
inputs: [
|
|
473
|
+
{ name: "mint", type: "address", indexed: true },
|
|
474
|
+
{ name: "ethAmount", type: "uint64", indexed: false },
|
|
475
|
+
{ name: "tokenAmount", type: "uint256", indexed: false },
|
|
476
|
+
{ name: "isBuy", type: "bool", indexed: false },
|
|
477
|
+
{ name: "user", type: "address", indexed: true },
|
|
478
|
+
{ name: "timestamp", type: "uint256", indexed: false },
|
|
479
|
+
{ name: "virtualEthReserves", type: "uint64", indexed: false },
|
|
480
|
+
{ name: "virtualTokenReserves", type: "uint256", indexed: false },
|
|
481
|
+
{ name: "realEthReserves", type: "uint64", indexed: false },
|
|
482
|
+
{ name: "realTokenReserves", type: "uint256", indexed: false }
|
|
483
|
+
],
|
|
484
|
+
anonymous: false
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
type: "event",
|
|
488
|
+
name: "CompleteEvent",
|
|
489
|
+
inputs: [
|
|
490
|
+
{ name: "user", type: "address", indexed: true },
|
|
491
|
+
{ name: "mint", type: "address", indexed: true },
|
|
492
|
+
{ name: "timestamp", type: "uint256", indexed: false }
|
|
493
|
+
],
|
|
494
|
+
anonymous: false
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
type: "event",
|
|
498
|
+
name: "CompleteManiaAmmMigrationEvent",
|
|
499
|
+
inputs: [
|
|
500
|
+
{ name: "user", type: "address", indexed: true },
|
|
501
|
+
{ name: "mint", type: "address", indexed: true },
|
|
502
|
+
{ name: "mintAmount", type: "uint256", indexed: false },
|
|
503
|
+
{ name: "ethAmount", type: "uint64", indexed: false },
|
|
504
|
+
{ name: "poolMigrationFee", type: "uint64", indexed: false },
|
|
505
|
+
{ name: "timestamp", type: "uint256", indexed: false },
|
|
506
|
+
{ name: "pool", type: "address", indexed: true }
|
|
507
|
+
],
|
|
508
|
+
anonymous: false
|
|
509
|
+
}
|
|
510
|
+
];
|
|
511
|
+
var ERC20_ABI = [
|
|
512
|
+
{
|
|
513
|
+
type: "function",
|
|
514
|
+
name: "name",
|
|
515
|
+
inputs: [],
|
|
516
|
+
outputs: [{ type: "string" }],
|
|
517
|
+
stateMutability: "view"
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
type: "function",
|
|
521
|
+
name: "symbol",
|
|
522
|
+
inputs: [],
|
|
523
|
+
outputs: [{ type: "string" }],
|
|
524
|
+
stateMutability: "view"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
type: "function",
|
|
528
|
+
name: "decimals",
|
|
529
|
+
inputs: [],
|
|
530
|
+
outputs: [{ type: "uint8" }],
|
|
531
|
+
stateMutability: "view"
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
type: "function",
|
|
535
|
+
name: "totalSupply",
|
|
536
|
+
inputs: [],
|
|
537
|
+
outputs: [{ type: "uint256" }],
|
|
538
|
+
stateMutability: "view"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
type: "function",
|
|
542
|
+
name: "balanceOf",
|
|
543
|
+
inputs: [{ name: "account", type: "address" }],
|
|
544
|
+
outputs: [{ type: "uint256" }],
|
|
545
|
+
stateMutability: "view"
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
type: "function",
|
|
549
|
+
name: "allowance",
|
|
550
|
+
inputs: [
|
|
551
|
+
{ name: "owner", type: "address" },
|
|
552
|
+
{ name: "spender", type: "address" }
|
|
553
|
+
],
|
|
554
|
+
outputs: [{ type: "uint256" }],
|
|
555
|
+
stateMutability: "view"
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
type: "function",
|
|
559
|
+
name: "approve",
|
|
560
|
+
inputs: [
|
|
561
|
+
{ name: "spender", type: "address" },
|
|
562
|
+
{ name: "amount", type: "uint256" }
|
|
563
|
+
],
|
|
564
|
+
outputs: [{ type: "bool" }],
|
|
565
|
+
stateMutability: "nonpayable"
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
type: "function",
|
|
569
|
+
name: "transfer",
|
|
570
|
+
inputs: [
|
|
571
|
+
{ name: "to", type: "address" },
|
|
572
|
+
{ name: "amount", type: "uint256" }
|
|
573
|
+
],
|
|
574
|
+
outputs: [{ type: "bool" }],
|
|
575
|
+
stateMutability: "nonpayable"
|
|
576
|
+
}
|
|
577
|
+
];
|
|
578
|
+
|
|
579
|
+
// src/mania.ts
|
|
580
|
+
var ManiaSDK = class _ManiaSDK {
|
|
581
|
+
publicClient;
|
|
582
|
+
walletClient = null;
|
|
583
|
+
factoryAddress;
|
|
584
|
+
chainId;
|
|
585
|
+
/**
|
|
586
|
+
* Create a new ManiaSDK instance
|
|
587
|
+
*
|
|
588
|
+
* @param config - SDK configuration
|
|
589
|
+
* @param config.factoryAddress - ManiaFactory contract address
|
|
590
|
+
* @param config.rpcUrl - RPC URL for the chain
|
|
591
|
+
* @param config.chainId - Chain ID (optional, will be fetched if not provided)
|
|
592
|
+
*/
|
|
593
|
+
constructor(config) {
|
|
594
|
+
this.factoryAddress = config.factoryAddress;
|
|
595
|
+
this.chainId = config.chainId ?? 1;
|
|
596
|
+
this.publicClient = (0, import_viem.createPublicClient)({
|
|
597
|
+
transport: (0, import_viem.http)(config.rpcUrl)
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Create SDK instance from chain ID using default configuration
|
|
602
|
+
*
|
|
603
|
+
* @param chainId - Chain ID
|
|
604
|
+
* @param rpcUrl - RPC URL (optional, uses public RPC if not provided)
|
|
605
|
+
* @returns ManiaSDK instance
|
|
606
|
+
*/
|
|
607
|
+
static fromChainId(chainId, rpcUrl) {
|
|
608
|
+
const chainConfig = getChainConfig(chainId);
|
|
609
|
+
if (!chainConfig) {
|
|
610
|
+
throw new Error(`Unsupported chain ID: ${chainId}`);
|
|
611
|
+
}
|
|
612
|
+
return new _ManiaSDK({
|
|
613
|
+
factoryAddress: chainConfig.factoryAddress,
|
|
614
|
+
rpcUrl,
|
|
615
|
+
chainId
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Connect a wallet for signing transactions
|
|
620
|
+
*
|
|
621
|
+
* @param privateKey - Private key (with or without 0x prefix)
|
|
622
|
+
* @param chain - Viem chain object
|
|
623
|
+
*/
|
|
624
|
+
connectWallet(privateKey, chain) {
|
|
625
|
+
const account = (0, import_accounts.privateKeyToAccount)(
|
|
626
|
+
privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`
|
|
627
|
+
);
|
|
628
|
+
this.walletClient = (0, import_viem.createWalletClient)({
|
|
629
|
+
account,
|
|
630
|
+
chain,
|
|
631
|
+
transport: (0, import_viem.http)()
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Connect an existing wallet client
|
|
636
|
+
*
|
|
637
|
+
* @param walletClient - Viem wallet client
|
|
638
|
+
*/
|
|
639
|
+
setWalletClient(walletClient) {
|
|
640
|
+
this.walletClient = walletClient;
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Set a custom public client
|
|
644
|
+
*
|
|
645
|
+
* @param publicClient - Viem public client
|
|
646
|
+
*/
|
|
647
|
+
setPublicClient(publicClient) {
|
|
648
|
+
this.publicClient = publicClient;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Get the connected wallet address
|
|
652
|
+
*/
|
|
653
|
+
getWalletAddress() {
|
|
654
|
+
return this.walletClient?.account?.address;
|
|
655
|
+
}
|
|
656
|
+
// ========== READ METHODS ==========
|
|
657
|
+
/**
|
|
658
|
+
* Get global protocol configuration
|
|
659
|
+
*/
|
|
660
|
+
async getGlobalState() {
|
|
661
|
+
const result = await this.publicClient.readContract({
|
|
662
|
+
address: this.factoryAddress,
|
|
663
|
+
abi: MANIA_FACTORY_ABI,
|
|
664
|
+
functionName: "global"
|
|
665
|
+
});
|
|
666
|
+
const [
|
|
667
|
+
initialized,
|
|
668
|
+
authority,
|
|
669
|
+
feeRecipient,
|
|
670
|
+
initialVirtualTokenReserves,
|
|
671
|
+
initialVirtualEthReserves,
|
|
672
|
+
initialRealTokenReserves,
|
|
673
|
+
tokenTotalSupply,
|
|
674
|
+
feeBasisPoints,
|
|
675
|
+
withdrawAuthority,
|
|
676
|
+
enableMigrate,
|
|
677
|
+
poolMigrationFee
|
|
678
|
+
] = result;
|
|
679
|
+
return {
|
|
680
|
+
initialized,
|
|
681
|
+
authority,
|
|
682
|
+
feeRecipient,
|
|
683
|
+
initialVirtualTokenReserves,
|
|
684
|
+
initialVirtualEthReserves,
|
|
685
|
+
initialRealTokenReserves,
|
|
686
|
+
tokenTotalSupply,
|
|
687
|
+
feeBasisPoints,
|
|
688
|
+
withdrawAuthority,
|
|
689
|
+
enableMigrate,
|
|
690
|
+
poolMigrationFee
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Get bonding curve state for a token
|
|
695
|
+
*
|
|
696
|
+
* @param token - Token address
|
|
697
|
+
*/
|
|
698
|
+
async getBondingCurve(token) {
|
|
699
|
+
const result = await this.publicClient.readContract({
|
|
700
|
+
address: this.factoryAddress,
|
|
701
|
+
abi: MANIA_FACTORY_ABI,
|
|
702
|
+
functionName: "getBondingCurve",
|
|
703
|
+
args: [token]
|
|
704
|
+
});
|
|
705
|
+
const curve = result;
|
|
706
|
+
return {
|
|
707
|
+
virtualTokenReserves: curve.virtualTokenReserves,
|
|
708
|
+
virtualEthReserves: curve.virtualEthReserves,
|
|
709
|
+
realTokenReserves: curve.realTokenReserves,
|
|
710
|
+
realEthReserves: curve.realEthReserves,
|
|
711
|
+
tokenTotalSupply: curve.tokenTotalSupply,
|
|
712
|
+
complete: curve.complete,
|
|
713
|
+
trackVolume: curve.trackVolume
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Get a BondingCurve instance for calculations
|
|
718
|
+
*
|
|
719
|
+
* @param token - Token address
|
|
720
|
+
*/
|
|
721
|
+
async getBondingCurveInstance(token) {
|
|
722
|
+
const [curveState, globalState] = await Promise.all([
|
|
723
|
+
this.getBondingCurve(token),
|
|
724
|
+
this.getGlobalState()
|
|
725
|
+
]);
|
|
726
|
+
return new BondingCurve(curveState, globalState.feeBasisPoints);
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Get comprehensive token information
|
|
730
|
+
*
|
|
731
|
+
* @param token - Token address
|
|
732
|
+
*/
|
|
733
|
+
async getTokenInfo(token) {
|
|
734
|
+
const curve = await this.getBondingCurveInstance(token);
|
|
735
|
+
const state = curve.getState();
|
|
736
|
+
return {
|
|
737
|
+
address: token,
|
|
738
|
+
bondingCurve: state,
|
|
739
|
+
currentPrice: curve.getCurrentPrice(),
|
|
740
|
+
marketCapEth: curve.getMarketCapEth(),
|
|
741
|
+
migrationProgress: curve.getMigrationProgress()
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get buy quote from contract
|
|
746
|
+
*
|
|
747
|
+
* @param token - Token address
|
|
748
|
+
* @param ethAmount - ETH amount in wei
|
|
749
|
+
*/
|
|
750
|
+
async getBuyQuote(token, ethAmount) {
|
|
751
|
+
const result = await this.publicClient.readContract({
|
|
752
|
+
address: this.factoryAddress,
|
|
753
|
+
abi: MANIA_FACTORY_ABI,
|
|
754
|
+
functionName: "getBuyQuote",
|
|
755
|
+
args: [token, ethAmount]
|
|
756
|
+
});
|
|
757
|
+
return result;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Get sell quote from contract
|
|
761
|
+
*
|
|
762
|
+
* @param token - Token address
|
|
763
|
+
* @param tokenAmount - Token amount
|
|
764
|
+
*/
|
|
765
|
+
async getSellQuote(token, tokenAmount) {
|
|
766
|
+
const result = await this.publicClient.readContract({
|
|
767
|
+
address: this.factoryAddress,
|
|
768
|
+
abi: MANIA_FACTORY_ABI,
|
|
769
|
+
functionName: "getSellQuote",
|
|
770
|
+
args: [token, tokenAmount]
|
|
771
|
+
});
|
|
772
|
+
return result;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Get fee for a given amount
|
|
776
|
+
*
|
|
777
|
+
* @param amount - Amount in wei
|
|
778
|
+
*/
|
|
779
|
+
async getFee(amount) {
|
|
780
|
+
const result = await this.publicClient.readContract({
|
|
781
|
+
address: this.factoryAddress,
|
|
782
|
+
abi: MANIA_FACTORY_ABI,
|
|
783
|
+
functionName: "getFee",
|
|
784
|
+
args: [amount]
|
|
785
|
+
});
|
|
786
|
+
return result;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Get user volume
|
|
790
|
+
*
|
|
791
|
+
* @param user - User address
|
|
792
|
+
*/
|
|
793
|
+
async getUserVolume(user) {
|
|
794
|
+
const result = await this.publicClient.readContract({
|
|
795
|
+
address: this.factoryAddress,
|
|
796
|
+
abi: MANIA_FACTORY_ABI,
|
|
797
|
+
functionName: "userVolume",
|
|
798
|
+
args: [user]
|
|
799
|
+
});
|
|
800
|
+
return result;
|
|
801
|
+
}
|
|
802
|
+
// ========== WRITE METHODS ==========
|
|
803
|
+
getConnectedWallet() {
|
|
804
|
+
if (!this.walletClient?.account) {
|
|
805
|
+
throw new Error("Wallet not connected. Call connectWallet() first.");
|
|
806
|
+
}
|
|
807
|
+
return this.walletClient;
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Create a new token with bonding curve
|
|
811
|
+
*
|
|
812
|
+
* @param params - Token creation parameters
|
|
813
|
+
* @returns Transaction result with token address
|
|
814
|
+
*/
|
|
815
|
+
async create(params) {
|
|
816
|
+
const wallet = this.getConnectedWallet();
|
|
817
|
+
const hash = await wallet.writeContract({
|
|
818
|
+
chain: null,
|
|
819
|
+
account: null,
|
|
820
|
+
address: this.factoryAddress,
|
|
821
|
+
abi: MANIA_FACTORY_ABI,
|
|
822
|
+
functionName: "create",
|
|
823
|
+
args: [params.name, params.symbol, params.uri, params.creator]
|
|
824
|
+
});
|
|
825
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
826
|
+
const logs = (0, import_viem.parseEventLogs)({
|
|
827
|
+
abi: MANIA_FACTORY_ABI,
|
|
828
|
+
logs: receipt.logs,
|
|
829
|
+
eventName: "CreateEvent"
|
|
830
|
+
});
|
|
831
|
+
const tokenAddress = logs[0]?.args?.mint;
|
|
832
|
+
return {
|
|
833
|
+
hash,
|
|
834
|
+
success: receipt.status === "success",
|
|
835
|
+
tokenAddress
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Create a new token and buy in a single transaction
|
|
840
|
+
*
|
|
841
|
+
* @param params - Creation and buy parameters
|
|
842
|
+
* @returns Transaction result with token address
|
|
843
|
+
*/
|
|
844
|
+
async createAndBuy(params) {
|
|
845
|
+
const wallet = this.getConnectedWallet();
|
|
846
|
+
const hash = await wallet.writeContract({
|
|
847
|
+
chain: null,
|
|
848
|
+
account: null,
|
|
849
|
+
address: this.factoryAddress,
|
|
850
|
+
abi: MANIA_FACTORY_ABI,
|
|
851
|
+
functionName: "createAndBuy",
|
|
852
|
+
args: [params.name, params.symbol, params.uri, params.creator, params.minTokensOut],
|
|
853
|
+
value: params.buyAmountEth
|
|
854
|
+
});
|
|
855
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
856
|
+
const logs = (0, import_viem.parseEventLogs)({
|
|
857
|
+
abi: MANIA_FACTORY_ABI,
|
|
858
|
+
logs: receipt.logs,
|
|
859
|
+
eventName: "CreateEvent"
|
|
860
|
+
});
|
|
861
|
+
const tokenAddress = logs[0]?.args?.mint;
|
|
862
|
+
return {
|
|
863
|
+
hash,
|
|
864
|
+
success: receipt.status === "success",
|
|
865
|
+
tokenAddress
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Buy tokens from bonding curve
|
|
870
|
+
*
|
|
871
|
+
* @param params - Buy parameters
|
|
872
|
+
* @returns Transaction result
|
|
873
|
+
*/
|
|
874
|
+
async buy(params) {
|
|
875
|
+
const wallet = this.getConnectedWallet();
|
|
876
|
+
const recipient = params.recipient ?? wallet.account.address;
|
|
877
|
+
const hash = await wallet.writeContract({
|
|
878
|
+
chain: null,
|
|
879
|
+
account: null,
|
|
880
|
+
address: this.factoryAddress,
|
|
881
|
+
abi: MANIA_FACTORY_ABI,
|
|
882
|
+
functionName: "buy",
|
|
883
|
+
args: [params.token, params.minTokensOut, recipient],
|
|
884
|
+
value: params.amountEth
|
|
885
|
+
});
|
|
886
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
887
|
+
return {
|
|
888
|
+
hash,
|
|
889
|
+
success: receipt.status === "success"
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Buy tokens with automatic slippage calculation
|
|
894
|
+
*
|
|
895
|
+
* @param token - Token address
|
|
896
|
+
* @param ethAmount - ETH amount to spend
|
|
897
|
+
* @param slippageBps - Slippage tolerance in basis points (default: 100 = 1%)
|
|
898
|
+
*/
|
|
899
|
+
async buyWithSlippage(token, ethAmount, slippageBps = DEFAULT_SLIPPAGE_BPS) {
|
|
900
|
+
const curve = await this.getBondingCurveInstance(token);
|
|
901
|
+
const minTokensOut = curve.calculateMinTokensOut(ethAmount, slippageBps);
|
|
902
|
+
return this.buy({
|
|
903
|
+
token,
|
|
904
|
+
amountEth: ethAmount,
|
|
905
|
+
minTokensOut
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Sell tokens to bonding curve
|
|
910
|
+
*
|
|
911
|
+
* @param params - Sell parameters
|
|
912
|
+
* @returns Transaction result
|
|
913
|
+
*/
|
|
914
|
+
async sell(params) {
|
|
915
|
+
const wallet = this.getConnectedWallet();
|
|
916
|
+
const hash = await wallet.writeContract({
|
|
917
|
+
chain: null,
|
|
918
|
+
account: null,
|
|
919
|
+
address: this.factoryAddress,
|
|
920
|
+
abi: MANIA_FACTORY_ABI,
|
|
921
|
+
functionName: "sell",
|
|
922
|
+
args: [params.token, params.amountTokens, params.minEthOut]
|
|
923
|
+
});
|
|
924
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
925
|
+
return {
|
|
926
|
+
hash,
|
|
927
|
+
success: receipt.status === "success"
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Sell tokens with automatic slippage calculation
|
|
932
|
+
*
|
|
933
|
+
* @param token - Token address
|
|
934
|
+
* @param tokenAmount - Token amount to sell
|
|
935
|
+
* @param slippageBps - Slippage tolerance in basis points (default: 100 = 1%)
|
|
936
|
+
*/
|
|
937
|
+
async sellWithSlippage(token, tokenAmount, slippageBps = DEFAULT_SLIPPAGE_BPS) {
|
|
938
|
+
const curve = await this.getBondingCurveInstance(token);
|
|
939
|
+
const minEthOut = curve.calculateMinEthOut(tokenAmount, slippageBps);
|
|
940
|
+
return this.sell({
|
|
941
|
+
token,
|
|
942
|
+
amountTokens: tokenAmount,
|
|
943
|
+
minEthOut
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Migrate liquidity to Uniswap V3
|
|
948
|
+
*
|
|
949
|
+
* @param params - Migration parameters
|
|
950
|
+
* @returns Transaction result with pool address
|
|
951
|
+
*/
|
|
952
|
+
async migrate(params) {
|
|
953
|
+
const wallet = this.getConnectedWallet();
|
|
954
|
+
const globalState = await this.getGlobalState();
|
|
955
|
+
const hash = await wallet.writeContract({
|
|
956
|
+
chain: null,
|
|
957
|
+
account: null,
|
|
958
|
+
address: this.factoryAddress,
|
|
959
|
+
abi: MANIA_FACTORY_ABI,
|
|
960
|
+
functionName: "migrate",
|
|
961
|
+
args: [params.token],
|
|
962
|
+
value: globalState.poolMigrationFee
|
|
963
|
+
});
|
|
964
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
965
|
+
const logs = (0, import_viem.parseEventLogs)({
|
|
966
|
+
abi: MANIA_FACTORY_ABI,
|
|
967
|
+
logs: receipt.logs,
|
|
968
|
+
eventName: "CompleteManiaAmmMigrationEvent"
|
|
969
|
+
});
|
|
970
|
+
const poolAddress = logs[0]?.args?.pool;
|
|
971
|
+
return {
|
|
972
|
+
hash,
|
|
973
|
+
success: receipt.status === "success",
|
|
974
|
+
poolAddress
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
// ========== EVENT WATCHING ==========
|
|
978
|
+
/**
|
|
979
|
+
* Watch for new token creation events
|
|
980
|
+
*
|
|
981
|
+
* @param callback - Callback function for each event
|
|
982
|
+
* @returns Unwatch function
|
|
983
|
+
*/
|
|
984
|
+
watchCreateEvents(callback) {
|
|
985
|
+
return this.publicClient.watchContractEvent({
|
|
986
|
+
address: this.factoryAddress,
|
|
987
|
+
abi: MANIA_FACTORY_ABI,
|
|
988
|
+
eventName: "CreateEvent",
|
|
989
|
+
onLogs: (logs) => {
|
|
990
|
+
for (const log of logs) {
|
|
991
|
+
const args = log.args;
|
|
992
|
+
callback(args);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Watch for trade events on a specific token
|
|
999
|
+
*
|
|
1000
|
+
* @param token - Token address (optional, watches all if not provided)
|
|
1001
|
+
* @param callback - Callback function for each event
|
|
1002
|
+
* @returns Unwatch function
|
|
1003
|
+
*/
|
|
1004
|
+
watchTradeEvents(token, callback) {
|
|
1005
|
+
return this.publicClient.watchContractEvent({
|
|
1006
|
+
address: this.factoryAddress,
|
|
1007
|
+
abi: MANIA_FACTORY_ABI,
|
|
1008
|
+
eventName: "TradeEvent",
|
|
1009
|
+
args: token ? { mint: token } : void 0,
|
|
1010
|
+
onLogs: (logs) => {
|
|
1011
|
+
for (const log of logs) {
|
|
1012
|
+
const args = log.args;
|
|
1013
|
+
callback(args);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Watch for bonding curve completion events
|
|
1020
|
+
*
|
|
1021
|
+
* @param callback - Callback function for each event
|
|
1022
|
+
* @returns Unwatch function
|
|
1023
|
+
*/
|
|
1024
|
+
watchCompleteEvents(callback) {
|
|
1025
|
+
return this.publicClient.watchContractEvent({
|
|
1026
|
+
address: this.factoryAddress,
|
|
1027
|
+
abi: MANIA_FACTORY_ABI,
|
|
1028
|
+
eventName: "CompleteEvent",
|
|
1029
|
+
onLogs: (logs) => {
|
|
1030
|
+
for (const log of logs) {
|
|
1031
|
+
const args = log.args;
|
|
1032
|
+
callback(args);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Watch for migration events
|
|
1039
|
+
*
|
|
1040
|
+
* @param callback - Callback function for each event
|
|
1041
|
+
* @returns Unwatch function
|
|
1042
|
+
*/
|
|
1043
|
+
watchMigrationEvents(callback) {
|
|
1044
|
+
return this.publicClient.watchContractEvent({
|
|
1045
|
+
address: this.factoryAddress,
|
|
1046
|
+
abi: MANIA_FACTORY_ABI,
|
|
1047
|
+
eventName: "CompleteManiaAmmMigrationEvent",
|
|
1048
|
+
onLogs: (logs) => {
|
|
1049
|
+
for (const log of logs) {
|
|
1050
|
+
const args = log.args;
|
|
1051
|
+
callback(args);
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
// ========== UTILITY METHODS ==========
|
|
1057
|
+
/**
|
|
1058
|
+
* Check if a token's bonding curve is complete
|
|
1059
|
+
*
|
|
1060
|
+
* @param token - Token address
|
|
1061
|
+
*/
|
|
1062
|
+
async isComplete(token) {
|
|
1063
|
+
const curve = await this.getBondingCurve(token);
|
|
1064
|
+
return curve.complete;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Check if a token has been migrated
|
|
1068
|
+
*
|
|
1069
|
+
* @param token - Token address
|
|
1070
|
+
*/
|
|
1071
|
+
async isMigrated(token) {
|
|
1072
|
+
const curve = await this.getBondingCurve(token);
|
|
1073
|
+
return curve.realEthReserves === 0n && curve.virtualEthReserves === 0n && curve.realTokenReserves === 0n && curve.virtualTokenReserves === 0n;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Get the factory contract address
|
|
1077
|
+
*/
|
|
1078
|
+
getFactoryAddress() {
|
|
1079
|
+
return this.factoryAddress;
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Get the public client
|
|
1083
|
+
*/
|
|
1084
|
+
getPublicClient() {
|
|
1085
|
+
return this.publicClient;
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Get the wallet client
|
|
1089
|
+
*/
|
|
1090
|
+
getWalletClient() {
|
|
1091
|
+
return this.walletClient;
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
// src/utils.ts
|
|
1096
|
+
var import_viem2 = require("viem");
|
|
1097
|
+
function formatEthValue(wei, decimals = 4) {
|
|
1098
|
+
const ethValue = (0, import_viem2.formatEther)(wei);
|
|
1099
|
+
const num = parseFloat(ethValue);
|
|
1100
|
+
return num.toFixed(decimals);
|
|
1101
|
+
}
|
|
1102
|
+
function formatTokenAmount(amount, decimals = 2) {
|
|
1103
|
+
const ethValue = (0, import_viem2.formatEther)(amount);
|
|
1104
|
+
const num = parseFloat(ethValue);
|
|
1105
|
+
if (num >= 1e9) {
|
|
1106
|
+
return (num / 1e9).toFixed(decimals) + "B";
|
|
1107
|
+
}
|
|
1108
|
+
if (num >= 1e6) {
|
|
1109
|
+
return (num / 1e6).toFixed(decimals) + "M";
|
|
1110
|
+
}
|
|
1111
|
+
if (num >= 1e3) {
|
|
1112
|
+
return (num / 1e3).toFixed(decimals) + "K";
|
|
1113
|
+
}
|
|
1114
|
+
return num.toFixed(decimals);
|
|
1115
|
+
}
|
|
1116
|
+
function parseEthValue(eth) {
|
|
1117
|
+
return (0, import_viem2.parseEther)(eth);
|
|
1118
|
+
}
|
|
1119
|
+
function calculateWithSlippage(amount, slippageBps) {
|
|
1120
|
+
const slippageMultiplier = BPS_DENOMINATOR - BigInt(slippageBps);
|
|
1121
|
+
return amount * slippageMultiplier / BPS_DENOMINATOR;
|
|
1122
|
+
}
|
|
1123
|
+
function calculateMigrationProgress(realEthReserves) {
|
|
1124
|
+
if (realEthReserves >= MIGRATION_THRESHOLD) {
|
|
1125
|
+
return 100;
|
|
1126
|
+
}
|
|
1127
|
+
return Number(realEthReserves * 10000n / MIGRATION_THRESHOLD) / 100;
|
|
1128
|
+
}
|
|
1129
|
+
function formatPrice(priceWei) {
|
|
1130
|
+
const price = Number(priceWei) / 1e18;
|
|
1131
|
+
if (price < 1e-6) {
|
|
1132
|
+
return price.toExponential(4);
|
|
1133
|
+
}
|
|
1134
|
+
if (price < 1e-4) {
|
|
1135
|
+
return price.toFixed(8);
|
|
1136
|
+
}
|
|
1137
|
+
if (price < 0.01) {
|
|
1138
|
+
return price.toFixed(6);
|
|
1139
|
+
}
|
|
1140
|
+
return price.toFixed(4);
|
|
1141
|
+
}
|
|
1142
|
+
function formatMarketCap(marketCapWei) {
|
|
1143
|
+
const ethValue = Number((0, import_viem2.formatEther)(marketCapWei));
|
|
1144
|
+
if (ethValue >= 1e3) {
|
|
1145
|
+
return (ethValue / 1e3).toFixed(2) + "K ETH";
|
|
1146
|
+
}
|
|
1147
|
+
return ethValue.toFixed(2) + " ETH";
|
|
1148
|
+
}
|
|
1149
|
+
function isValidAddress(address) {
|
|
1150
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
1151
|
+
}
|
|
1152
|
+
function truncateAddress(address, chars = 4) {
|
|
1153
|
+
const start = address.slice(0, chars + 2);
|
|
1154
|
+
const end = address.slice(-chars);
|
|
1155
|
+
return start + "..." + end;
|
|
1156
|
+
}
|
|
1157
|
+
function bpsToPercent(bps) {
|
|
1158
|
+
return Number(bps) / 100;
|
|
1159
|
+
}
|
|
1160
|
+
function percentToBps(percent) {
|
|
1161
|
+
return Math.round(percent * 100);
|
|
1162
|
+
}
|
|
1163
|
+
function calculatePriceImpact(currentPrice, newPrice) {
|
|
1164
|
+
if (currentPrice === 0n) return 0;
|
|
1165
|
+
return Number((newPrice - currentPrice) * 10000n / currentPrice) / 100;
|
|
1166
|
+
}
|
|
1167
|
+
function sleep(ms) {
|
|
1168
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1169
|
+
}
|
|
1170
|
+
async function withRetry(fn, maxRetries = 3, baseDelayMs = 1e3) {
|
|
1171
|
+
let lastError;
|
|
1172
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
1173
|
+
try {
|
|
1174
|
+
return await fn();
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1177
|
+
if (i < maxRetries - 1) {
|
|
1178
|
+
await sleep(baseDelayMs * Math.pow(2, i));
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
throw lastError;
|
|
1183
|
+
}
|
|
1184
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1185
|
+
0 && (module.exports = {
|
|
1186
|
+
BPS_DENOMINATOR,
|
|
1187
|
+
BondingCurve,
|
|
1188
|
+
CHAIN_CONFIGS,
|
|
1189
|
+
CREATOR_FEE_BASIS_POINTS,
|
|
1190
|
+
DEFAULT_SLIPPAGE_BPS,
|
|
1191
|
+
ERC20_ABI,
|
|
1192
|
+
MANIA_FACTORY_ABI,
|
|
1193
|
+
MAX_FEE_BASIS_POINTS,
|
|
1194
|
+
MAX_MIGRATE_FEES,
|
|
1195
|
+
MIGRATION_THRESHOLD,
|
|
1196
|
+
ManiaSDK,
|
|
1197
|
+
PROTOCOL_FEE_BASIS_POINTS,
|
|
1198
|
+
TICK_LOWER,
|
|
1199
|
+
TICK_UPPER,
|
|
1200
|
+
TOKENS_FOR_LP,
|
|
1201
|
+
TOTAL_FEE_BASIS_POINTS,
|
|
1202
|
+
UNISWAP_FEE_TIER,
|
|
1203
|
+
bpsToPercent,
|
|
1204
|
+
calculateBuyAmount,
|
|
1205
|
+
calculateMigrationProgress,
|
|
1206
|
+
calculatePriceImpact,
|
|
1207
|
+
calculateSellAmount,
|
|
1208
|
+
calculateWithSlippage,
|
|
1209
|
+
formatEthValue,
|
|
1210
|
+
formatMarketCap,
|
|
1211
|
+
formatPrice,
|
|
1212
|
+
formatTokenAmount,
|
|
1213
|
+
getChainConfig,
|
|
1214
|
+
isValidAddress,
|
|
1215
|
+
parseEthValue,
|
|
1216
|
+
percentToBps,
|
|
1217
|
+
sleep,
|
|
1218
|
+
truncateAddress,
|
|
1219
|
+
withRetry
|
|
1220
|
+
});
|