pons-client 0.1.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 +152 -0
- package/dist/index.cjs +1061 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1862 -0
- package/dist/index.d.ts +1862 -0
- package/dist/index.js +1021 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1021 @@
|
|
|
1
|
+
import { parseAbi, isAddress, createPublicClient, http, getAddress, formatEther, formatUnits, decodeEventLog, keccak256, encodeAbiParameters, parseAbiItem } from 'viem';
|
|
2
|
+
export { decodeEventLog } from 'viem';
|
|
3
|
+
|
|
4
|
+
// src/client.ts
|
|
5
|
+
|
|
6
|
+
// src/constants.ts
|
|
7
|
+
var ROBINHOOD_CHAIN_ID = 4663;
|
|
8
|
+
var BPS = 10000n;
|
|
9
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
10
|
+
var ADDRESSES = {
|
|
11
|
+
factory: "0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e",
|
|
12
|
+
memeHook: "0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044",
|
|
13
|
+
feeEscrow: "0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e",
|
|
14
|
+
buybackVault: "0x42df2a798f82289E177311362e8f5ccC45c1219c",
|
|
15
|
+
launchLocker: "0x267444D099b10fB5Ed7c3Cc7B7c767AdcA574952",
|
|
16
|
+
launchAndBuy: "0xe33E9E479dF8802cb0866d5d05258bEc4cF62948",
|
|
17
|
+
launchDeployer: "0x3711ceA4feaDE896C913C68F01Eda97Cb06D1A42",
|
|
18
|
+
graduationExecutor: "0xC7819B64A1dAECD7eC19856d026cb14EfBd89046",
|
|
19
|
+
graduationGuard: "0xf5695117b99B6f6401e67d4195BD653628176C6C"
|
|
20
|
+
};
|
|
21
|
+
var PHASE = {
|
|
22
|
+
NotGraduated: 0,
|
|
23
|
+
Swept: 1,
|
|
24
|
+
PoolCreated: 2,
|
|
25
|
+
Rescued: 3
|
|
26
|
+
};
|
|
27
|
+
var SNIPE_TAX_DECAY_NS = 5000000000n;
|
|
28
|
+
var MAX_SNIPE_EXEMPTIONS = 32;
|
|
29
|
+
var VESTING_DURATION_SECS = 157680000n;
|
|
30
|
+
var MIGRATION_CLAIM_WINDOW_SECS = 7776000n;
|
|
31
|
+
var factoryAbi = parseAbi([
|
|
32
|
+
// ── Structs ──────────────────────────────────────────────────────────────
|
|
33
|
+
"struct LaunchConfig { uint256 supply; uint256 curveFeeBps; uint256 phantomQuote; uint256 graduationThreshold; uint24 poolFee; int24 tickSpacing; bool enabled; }",
|
|
34
|
+
"struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }",
|
|
35
|
+
"struct TokenParams { string name; string symbol; string logo; string description; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }",
|
|
36
|
+
"struct LaunchedToken { address token; address curve; address deployer; address creatorFeeRecipient; address pairToken; uint256 graduationThreshold; uint24 poolFee; int24 tickSpacing; uint16 creatorTaxBps; bool buybackEnabled; uint8 phase; uint256 sweptQuote; uint256 sweptTokens; uint256 sweptAt; bool exists; }",
|
|
37
|
+
"struct FeePolicy { address protocolFeeRecipient; uint16 protocolFeeShareBps; uint16 buybackBurnBps; uint16 hookFeeBps; uint16 maxInternalPriceImpactBps; }",
|
|
38
|
+
// ── Read Functions ───────────────────────────────────────────────────────
|
|
39
|
+
"function launchConfigCount() view returns (uint256)",
|
|
40
|
+
"function getLaunchConfig(uint256 id) view returns (LaunchConfig)",
|
|
41
|
+
"function getLaunchedToken(address token) view returns (LaunchedToken)",
|
|
42
|
+
"function previewLaunchEconomics(uint256 launchConfigId, address pairToken) view returns (bytes32)",
|
|
43
|
+
"function launchFee() view returns (uint256)",
|
|
44
|
+
"function canLaunch(address launcher) view returns (bool)",
|
|
45
|
+
"function maxCreatorTaxBps() view returns (uint16)",
|
|
46
|
+
"function approvedPairTokens(address pairToken) view returns (bool)",
|
|
47
|
+
"function pairTokenEconomics(address pairToken) view returns (uint256 phantomQuote, uint256 graduationThreshold, uint8 decimals)",
|
|
48
|
+
"function getLaunchFeePolicy(address token) view returns (FeePolicy)",
|
|
49
|
+
"function pendingCreatorFeeRecipient(address token) view returns (address recipient, uint256 effectiveAt, uint256 expiresAt)",
|
|
50
|
+
// ── Write Functions ──────────────────────────────────────────────────────
|
|
51
|
+
"function launchToken(TokenParams params, uint256 launchConfigId, address pairToken) payable returns (address token, address curve)",
|
|
52
|
+
"function launchToken(TokenParams params, uint256 launchConfigId, address pairToken, address[] snipeTaxExemptions) payable returns (address token, address curve)",
|
|
53
|
+
"function createGraduatedPool(address token)",
|
|
54
|
+
"function transferCreatorFeeRecipient(address token, address newRecipient)",
|
|
55
|
+
// ── Events ───────────────────────────────────────────────────────────────
|
|
56
|
+
"event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)",
|
|
57
|
+
"event LaunchSwept(address indexed token, uint256 sweptQuote, uint256 sweptTokens)",
|
|
58
|
+
"event PoolGraduated(address indexed token, address indexed pool)",
|
|
59
|
+
"event CreatorFeeRecipientChangeProposed(address indexed token, address indexed newRecipient, uint256 effectiveAt)",
|
|
60
|
+
"event CreatorFeeRecipientUpdated(address indexed token, address indexed oldRecipient, address indexed newRecipient)"
|
|
61
|
+
]);
|
|
62
|
+
var routerAbi = parseAbi([
|
|
63
|
+
// ── Structs ──────────────────────────────────────────────────────────────
|
|
64
|
+
"struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }",
|
|
65
|
+
"struct TokenParams { string name; string symbol; string logo; string description; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }",
|
|
66
|
+
// ── Write Functions ──────────────────────────────────────────────────────
|
|
67
|
+
"function launchAndBuy(TokenParams params, uint256 launchConfigId, address pairToken, uint256 quoteIn, uint256 minTokensOut, address recipient, address[] snipeTaxExemptions) payable returns (address token, address curve, uint256 tokensOut)"
|
|
68
|
+
]);
|
|
69
|
+
function requireValidAddress(address, label) {
|
|
70
|
+
if (!isAddress(address)) {
|
|
71
|
+
throw new Error(`Invalid ${label} address: ${address}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function requireValidCreatorTax(taxBps, maxBps) {
|
|
75
|
+
if (taxBps < 0) {
|
|
76
|
+
throw new Error("Creator tax cannot be negative");
|
|
77
|
+
}
|
|
78
|
+
if (BigInt(taxBps) > maxBps) {
|
|
79
|
+
throw new Error(`Creator tax ${taxBps} bps exceeds protocol max ${maxBps} bps`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function requireValidExemptions(exemptions) {
|
|
83
|
+
if (exemptions.length > MAX_SNIPE_EXEMPTIONS) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Snipe tax exemption list too long: ${exemptions.length} (max ${MAX_SNIPE_EXEMPTIONS})`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function requirePositiveAmount(amount, label) {
|
|
90
|
+
if (amount <= 0n) {
|
|
91
|
+
throw new Error(`${label} must be positive, got ${amount}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function requireValidSlippage(slippageBps) {
|
|
95
|
+
if (slippageBps < 0 || slippageBps > Number(BPS)) {
|
|
96
|
+
throw new Error(`Invalid slippage: ${slippageBps} bps (must be 0-${BPS})`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function minOutputWithSlippage(quoted, slippageBps) {
|
|
100
|
+
const tolerance = quoted * BigInt(slippageBps) / BPS;
|
|
101
|
+
return quoted - tolerance;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/modules/factory/index.ts
|
|
105
|
+
function createFactoryModule(publicClient, wallet, addresses) {
|
|
106
|
+
const read = async (functionName, args) => publicClient.readContract({
|
|
107
|
+
address: addresses.factory,
|
|
108
|
+
abi: factoryAbi,
|
|
109
|
+
functionName,
|
|
110
|
+
args
|
|
111
|
+
});
|
|
112
|
+
const write = async (functionName, args, value) => {
|
|
113
|
+
if (!wallet) throw new Error("Wallet required for write operations");
|
|
114
|
+
const { request } = await publicClient.simulateContract({
|
|
115
|
+
address: addresses.factory,
|
|
116
|
+
abi: factoryAbi,
|
|
117
|
+
functionName,
|
|
118
|
+
args,
|
|
119
|
+
account: wallet.account,
|
|
120
|
+
value
|
|
121
|
+
});
|
|
122
|
+
return wallet.writeContract(request);
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
async openLaunchConfigs() {
|
|
126
|
+
const count = await read("launchConfigCount");
|
|
127
|
+
const configs = await Promise.all(
|
|
128
|
+
Array.from(
|
|
129
|
+
{ length: Number(count) },
|
|
130
|
+
(_, id) => read("getLaunchConfig", [BigInt(id)])
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
return configs.map((config, id) => ({
|
|
134
|
+
id: BigInt(id),
|
|
135
|
+
supply: config.supply,
|
|
136
|
+
curveFeeBps: config.curveFeeBps,
|
|
137
|
+
phantomQuote: config.phantomQuote,
|
|
138
|
+
graduationThreshold: config.graduationThreshold,
|
|
139
|
+
poolFee: config.poolFee,
|
|
140
|
+
tickSpacing: config.tickSpacing,
|
|
141
|
+
enabled: config.enabled
|
|
142
|
+
})).filter((config) => config.enabled);
|
|
143
|
+
},
|
|
144
|
+
async getLaunchConfig(id) {
|
|
145
|
+
return read("getLaunchConfig", [id]);
|
|
146
|
+
},
|
|
147
|
+
async getLaunchedToken(token) {
|
|
148
|
+
return read("getLaunchedToken", [token]);
|
|
149
|
+
},
|
|
150
|
+
async previewLaunchEconomics(configId, pairToken) {
|
|
151
|
+
return read("previewLaunchEconomics", [configId, pairToken]);
|
|
152
|
+
},
|
|
153
|
+
async launchFee() {
|
|
154
|
+
return read("launchFee");
|
|
155
|
+
},
|
|
156
|
+
async canLaunch(address) {
|
|
157
|
+
return read("canLaunch", [address]);
|
|
158
|
+
},
|
|
159
|
+
async maxCreatorTaxBps() {
|
|
160
|
+
return read("maxCreatorTaxBps");
|
|
161
|
+
},
|
|
162
|
+
async approvedPairTokens(pairToken) {
|
|
163
|
+
return read("approvedPairTokens", [pairToken]);
|
|
164
|
+
},
|
|
165
|
+
async pairTokenEconomics(pairToken) {
|
|
166
|
+
const result = await read("pairTokenEconomics", [
|
|
167
|
+
pairToken
|
|
168
|
+
]);
|
|
169
|
+
return {
|
|
170
|
+
phantomQuote: result[0],
|
|
171
|
+
graduationThreshold: result[1],
|
|
172
|
+
decimals: Number(result[2])
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
async launchToken(params, configId, pairToken, snipeTaxExemptions = []) {
|
|
176
|
+
requireValidAddress(pairToken, "pairToken");
|
|
177
|
+
requireValidCreatorTax(params.creatorTaxBps, await this.maxCreatorTaxBps());
|
|
178
|
+
requireValidExemptions(snipeTaxExemptions);
|
|
179
|
+
const [expectedEconomics, launchFeeAmount] = await Promise.all([
|
|
180
|
+
this.previewLaunchEconomics(configId, pairToken),
|
|
181
|
+
this.launchFee()
|
|
182
|
+
]);
|
|
183
|
+
const pinnedParams = {
|
|
184
|
+
name: params.name,
|
|
185
|
+
symbol: params.symbol,
|
|
186
|
+
logo: params.logo,
|
|
187
|
+
description: params.description,
|
|
188
|
+
socials: params.socials,
|
|
189
|
+
creatorFeeRecipient: params.creatorFeeRecipient,
|
|
190
|
+
creatorTaxBps: params.creatorTaxBps,
|
|
191
|
+
buybackEnabled: params.buybackEnabled,
|
|
192
|
+
expectedEconomics,
|
|
193
|
+
salt: params.salt
|
|
194
|
+
};
|
|
195
|
+
if (snipeTaxExemptions.length > 0) {
|
|
196
|
+
return write(
|
|
197
|
+
"launchToken",
|
|
198
|
+
[pinnedParams, configId, pairToken, snipeTaxExemptions],
|
|
199
|
+
launchFeeAmount
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
return write("launchToken", [pinnedParams, configId, pairToken], launchFeeAmount);
|
|
203
|
+
},
|
|
204
|
+
async launchAndBuy(params, configId, pairToken, quoteIn, minTokensOut, recipient, snipeTaxExemptions = []) {
|
|
205
|
+
requireValidAddress(pairToken, "pairToken");
|
|
206
|
+
requireValidAddress(recipient, "recipient");
|
|
207
|
+
requireValidCreatorTax(params.creatorTaxBps, await this.maxCreatorTaxBps());
|
|
208
|
+
requireValidExemptions(snipeTaxExemptions);
|
|
209
|
+
if (!wallet) throw new Error("Wallet required for write operations");
|
|
210
|
+
const [expectedEconomics, launchFeeAmount] = await Promise.all([
|
|
211
|
+
this.previewLaunchEconomics(configId, pairToken),
|
|
212
|
+
this.launchFee()
|
|
213
|
+
]);
|
|
214
|
+
const pinnedParams = {
|
|
215
|
+
name: params.name,
|
|
216
|
+
symbol: params.symbol,
|
|
217
|
+
logo: params.logo,
|
|
218
|
+
description: params.description,
|
|
219
|
+
socials: params.socials,
|
|
220
|
+
creatorFeeRecipient: params.creatorFeeRecipient,
|
|
221
|
+
creatorTaxBps: params.creatorTaxBps,
|
|
222
|
+
buybackEnabled: params.buybackEnabled,
|
|
223
|
+
expectedEconomics,
|
|
224
|
+
salt: params.salt
|
|
225
|
+
};
|
|
226
|
+
const value = pairToken === ZERO_ADDRESS ? launchFeeAmount + quoteIn : launchFeeAmount;
|
|
227
|
+
const { request, result } = await publicClient.simulateContract({
|
|
228
|
+
address: addresses.launchAndBuy,
|
|
229
|
+
abi: routerAbi,
|
|
230
|
+
functionName: "launchAndBuy",
|
|
231
|
+
args: [
|
|
232
|
+
pinnedParams,
|
|
233
|
+
configId,
|
|
234
|
+
pairToken,
|
|
235
|
+
quoteIn,
|
|
236
|
+
minTokensOut,
|
|
237
|
+
recipient,
|
|
238
|
+
snipeTaxExemptions
|
|
239
|
+
],
|
|
240
|
+
account: wallet.account,
|
|
241
|
+
value
|
|
242
|
+
});
|
|
243
|
+
const hash = await wallet.writeContract(request);
|
|
244
|
+
const typedResult = result;
|
|
245
|
+
return {
|
|
246
|
+
hash,
|
|
247
|
+
token: typedResult[0],
|
|
248
|
+
curve: typedResult[1],
|
|
249
|
+
tokensOut: typedResult[2]
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
var curveAbi = parseAbi([
|
|
255
|
+
// ── Read Functions ───────────────────────────────────────────────────────
|
|
256
|
+
"function getReserves() view returns (uint256 quoteReserve, uint256 tokenReserve)",
|
|
257
|
+
"function realQuoteReserve() view returns (uint256)",
|
|
258
|
+
"function sellableTokens() view returns (uint256)",
|
|
259
|
+
"function reservedTokens() view returns (uint256)",
|
|
260
|
+
"function graduationThreshold() view returns (uint256)",
|
|
261
|
+
"function readyToGraduate() view returns (bool)",
|
|
262
|
+
"function graduated() view returns (bool)",
|
|
263
|
+
"function feeBps() view returns (uint256)",
|
|
264
|
+
"function creatorTaxBps() view returns (uint256)",
|
|
265
|
+
"function buybackEnabled() view returns (bool)",
|
|
266
|
+
"function isNativeQuote() view returns (bool)",
|
|
267
|
+
"function pairToken() view returns (address)",
|
|
268
|
+
"function currentSnipeTaxBps(address recipient) view returns (uint256)",
|
|
269
|
+
"function quoteFeeBalance() view returns (uint256)",
|
|
270
|
+
"function creatorTaxBalance() view returns (uint256)",
|
|
271
|
+
// ── Write Functions ──────────────────────────────────────────────────────
|
|
272
|
+
"function buy(uint256 quoteIn, uint256 minTokensOut, address recipient) payable returns (uint256 tokensOut)",
|
|
273
|
+
"function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient) returns (uint256 quoteOut)",
|
|
274
|
+
"function sweepFees(uint256 minBuybackTokensOut)",
|
|
275
|
+
// ── Events ───────────────────────────────────────────────────────────────
|
|
276
|
+
"event CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax)",
|
|
277
|
+
"event CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax)",
|
|
278
|
+
"event CurveBuyRefunded(address indexed buyer, uint256 refundedQuote)",
|
|
279
|
+
"event CurveCompleted(address indexed token)",
|
|
280
|
+
"event CurveGraduated(address indexed token)",
|
|
281
|
+
"event AutoGraduationFailed(address indexed token)",
|
|
282
|
+
"event FeesSwept(uint256 quoteFees, uint256 tokenFees, uint256 buybackTokens)",
|
|
283
|
+
"event BuybackLocked(address indexed token, uint256 quoteSpent, uint256 tokensLocked)"
|
|
284
|
+
]);
|
|
285
|
+
|
|
286
|
+
// src/utils/math.ts
|
|
287
|
+
function ceilDiv(a, b) {
|
|
288
|
+
if (b === 0n) throw new Error("Division by zero");
|
|
289
|
+
return (a + b - 1n) / b;
|
|
290
|
+
}
|
|
291
|
+
function amountOut(inAmount, reserveIn, reserveOut) {
|
|
292
|
+
if (inAmount === 0n || reserveIn === 0n || reserveOut === 0n) return 0n;
|
|
293
|
+
return inAmount * reserveOut / (reserveIn + inAmount);
|
|
294
|
+
}
|
|
295
|
+
function amountIn(outAmount, reserveIn, reserveOut) {
|
|
296
|
+
if (outAmount === 0n || reserveOut === 0n) return 0n;
|
|
297
|
+
if (outAmount >= reserveOut) throw new Error("Output exceeds reserve");
|
|
298
|
+
return outAmount * reserveIn / (reserveOut - outAmount) + 1n;
|
|
299
|
+
}
|
|
300
|
+
function applyBps(amount, bps) {
|
|
301
|
+
return amount * bps / BPS;
|
|
302
|
+
}
|
|
303
|
+
function subtractBps(amount, bps) {
|
|
304
|
+
return amount - applyBps(amount, bps);
|
|
305
|
+
}
|
|
306
|
+
function marginalPrice(quoteReserve, tokenReserve) {
|
|
307
|
+
if (tokenReserve === 0n) return 0;
|
|
308
|
+
return Number(quoteReserve) / Number(tokenReserve);
|
|
309
|
+
}
|
|
310
|
+
function graduationProgress(raised, threshold) {
|
|
311
|
+
if (threshold === 0n) return 0;
|
|
312
|
+
const clamped = raised > threshold ? threshold : raised;
|
|
313
|
+
return Number(clamped) / Number(threshold);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/modules/curve/index.ts
|
|
317
|
+
function createCurveModule(publicClient, wallet, _addresses) {
|
|
318
|
+
const read = async (contractAddress, functionName, args) => publicClient.readContract({
|
|
319
|
+
address: contractAddress,
|
|
320
|
+
abi: curveAbi,
|
|
321
|
+
functionName,
|
|
322
|
+
args
|
|
323
|
+
});
|
|
324
|
+
const write = async (contractAddress, functionName, args, value) => {
|
|
325
|
+
if (!wallet) throw new Error("Wallet required for write operations");
|
|
326
|
+
const { request } = await publicClient.simulateContract({
|
|
327
|
+
address: contractAddress,
|
|
328
|
+
abi: curveAbi,
|
|
329
|
+
functionName,
|
|
330
|
+
args,
|
|
331
|
+
account: wallet.account,
|
|
332
|
+
value
|
|
333
|
+
});
|
|
334
|
+
return wallet.writeContract(request);
|
|
335
|
+
};
|
|
336
|
+
return {
|
|
337
|
+
async buy(curve, quoteIn, minTokensOut, recipient, value) {
|
|
338
|
+
const isNative = await read(curve, "isNativeQuote");
|
|
339
|
+
const sendValue = isNative ? quoteIn : value ?? 0n;
|
|
340
|
+
const hash = await write(curve, "buy", [quoteIn, minTokensOut, recipient], sendValue);
|
|
341
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
342
|
+
let tokensOut = 0n;
|
|
343
|
+
for (const log of receipt.logs) {
|
|
344
|
+
try {
|
|
345
|
+
const decoded = decodeEventLog({
|
|
346
|
+
abi: curveAbi,
|
|
347
|
+
eventName: "CurveBuy",
|
|
348
|
+
data: log.data,
|
|
349
|
+
topics: log.topics
|
|
350
|
+
});
|
|
351
|
+
if (decoded.eventName === "CurveBuy") {
|
|
352
|
+
tokensOut = decoded.args.tokensOut;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return { tokensOut, hash };
|
|
359
|
+
},
|
|
360
|
+
async sell(curve, tokensIn, minQuoteOut, recipient) {
|
|
361
|
+
const hash = await write(curve, "sell", [tokensIn, minQuoteOut, recipient]);
|
|
362
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
363
|
+
let quoteOut = 0n;
|
|
364
|
+
for (const log of receipt.logs) {
|
|
365
|
+
try {
|
|
366
|
+
const decoded = decodeEventLog({
|
|
367
|
+
abi: curveAbi,
|
|
368
|
+
eventName: "CurveSell",
|
|
369
|
+
data: log.data,
|
|
370
|
+
topics: log.topics
|
|
371
|
+
});
|
|
372
|
+
if (decoded.eventName === "CurveSell") {
|
|
373
|
+
quoteOut = decoded.args.quoteOut;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return { quoteOut, hash };
|
|
380
|
+
},
|
|
381
|
+
async quoteBuy(curve, quoteIn, recipient) {
|
|
382
|
+
const results = await Promise.all([
|
|
383
|
+
read(curve, "getReserves"),
|
|
384
|
+
read(curve, "sellableTokens"),
|
|
385
|
+
read(curve, "feeBps"),
|
|
386
|
+
read(curve, "creatorTaxBps"),
|
|
387
|
+
read(curve, "currentSnipeTaxBps", [recipient])
|
|
388
|
+
]);
|
|
389
|
+
const reserves = results[0];
|
|
390
|
+
const sellable = results[1];
|
|
391
|
+
const feeBps = results[2];
|
|
392
|
+
const creatorTaxBps = results[3];
|
|
393
|
+
const rawSnipeBps = results[4];
|
|
394
|
+
const quoteReserve = reserves[0];
|
|
395
|
+
const tokenReserve = reserves[1];
|
|
396
|
+
let snipeBps = rawSnipeBps;
|
|
397
|
+
if (snipeBps > 0n) {
|
|
398
|
+
const maxSnipeBps = BPS - feeBps - creatorTaxBps - 100n;
|
|
399
|
+
if (snipeBps > maxSnipeBps) snipeBps = maxSnipeBps;
|
|
400
|
+
}
|
|
401
|
+
let spent = quoteIn;
|
|
402
|
+
const fee = spent * feeBps / BPS;
|
|
403
|
+
const tax = spent * creatorTaxBps / BPS;
|
|
404
|
+
const snipeTax = spent * snipeBps / BPS;
|
|
405
|
+
let tokensOut = amountOut(spent - fee - tax - snipeTax, quoteReserve, tokenReserve);
|
|
406
|
+
if (tokensOut > sellable) {
|
|
407
|
+
tokensOut = sellable;
|
|
408
|
+
const net = amountIn(sellable, quoteReserve, tokenReserve);
|
|
409
|
+
const totalBps = BPS - feeBps - creatorTaxBps - snipeBps;
|
|
410
|
+
if (totalBps <= 0n) {
|
|
411
|
+
return { tokensOut, spent: 0n, refund: quoteIn };
|
|
412
|
+
}
|
|
413
|
+
const grossed = ceilDiv(net * BPS, totalBps);
|
|
414
|
+
spent = grossed < quoteIn ? grossed : quoteIn;
|
|
415
|
+
}
|
|
416
|
+
return { tokensOut, spent, refund: quoteIn - spent };
|
|
417
|
+
},
|
|
418
|
+
async quoteSell(curve, tokensIn) {
|
|
419
|
+
const results = await Promise.all([
|
|
420
|
+
read(curve, "getReserves"),
|
|
421
|
+
read(curve, "feeBps"),
|
|
422
|
+
read(curve, "creatorTaxBps")
|
|
423
|
+
]);
|
|
424
|
+
const reserves = results[0];
|
|
425
|
+
const feeBps = results[1];
|
|
426
|
+
const creatorTaxBps = results[2];
|
|
427
|
+
const quoteReserve = reserves[0];
|
|
428
|
+
const tokenReserve = reserves[1];
|
|
429
|
+
const gross = amountOut(tokensIn, tokenReserve, quoteReserve);
|
|
430
|
+
const fee = gross * feeBps / BPS;
|
|
431
|
+
const tax = gross * creatorTaxBps / BPS;
|
|
432
|
+
return { quoteOut: gross - fee - tax };
|
|
433
|
+
},
|
|
434
|
+
async getState(curve) {
|
|
435
|
+
const reserves = await read(curve, "getReserves");
|
|
436
|
+
const [
|
|
437
|
+
realQuoteReserve,
|
|
438
|
+
sellableTokens,
|
|
439
|
+
graduationThreshold,
|
|
440
|
+
readyToGraduate,
|
|
441
|
+
graduated,
|
|
442
|
+
feeBps,
|
|
443
|
+
creatorTaxBps,
|
|
444
|
+
buybackEnabled,
|
|
445
|
+
isNativeQuote,
|
|
446
|
+
pairToken
|
|
447
|
+
] = await Promise.all([
|
|
448
|
+
read(curve, "realQuoteReserve"),
|
|
449
|
+
read(curve, "sellableTokens"),
|
|
450
|
+
read(curve, "graduationThreshold"),
|
|
451
|
+
read(curve, "readyToGraduate"),
|
|
452
|
+
read(curve, "graduated"),
|
|
453
|
+
read(curve, "feeBps"),
|
|
454
|
+
read(curve, "creatorTaxBps"),
|
|
455
|
+
read(curve, "buybackEnabled"),
|
|
456
|
+
read(curve, "isNativeQuote"),
|
|
457
|
+
read(curve, "pairToken")
|
|
458
|
+
]);
|
|
459
|
+
return {
|
|
460
|
+
reserves: {
|
|
461
|
+
quoteReserve: reserves[0],
|
|
462
|
+
tokenReserve: reserves[1]
|
|
463
|
+
},
|
|
464
|
+
realQuoteReserve,
|
|
465
|
+
sellableTokens,
|
|
466
|
+
graduationThreshold,
|
|
467
|
+
readyToGraduate,
|
|
468
|
+
graduated,
|
|
469
|
+
feeBps,
|
|
470
|
+
creatorTaxBps,
|
|
471
|
+
buybackEnabled,
|
|
472
|
+
isNativeQuote,
|
|
473
|
+
pairToken
|
|
474
|
+
};
|
|
475
|
+
},
|
|
476
|
+
async getReserves(curve) {
|
|
477
|
+
const reserves = await read(curve, "getReserves");
|
|
478
|
+
return { quoteReserve: reserves[0], tokenReserve: reserves[1] };
|
|
479
|
+
},
|
|
480
|
+
async sellableTokens(curve) {
|
|
481
|
+
return read(curve, "sellableTokens");
|
|
482
|
+
},
|
|
483
|
+
async readyToGraduate(curve) {
|
|
484
|
+
return read(curve, "readyToGraduate");
|
|
485
|
+
},
|
|
486
|
+
async currentSnipeTaxBps(curve, recipient) {
|
|
487
|
+
return read(curve, "currentSnipeTaxBps", [recipient]);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
var hookAbi = parseAbi([
|
|
492
|
+
// ── Read Functions ───────────────────────────────────────────────────────
|
|
493
|
+
"function pendingFees(bytes32 poolId, address currency) view returns (uint256)",
|
|
494
|
+
"function pendingCreatorTax(bytes32 poolId, address currency) view returns (uint256)",
|
|
495
|
+
// ── Events ───────────────────────────────────────────────────────────────
|
|
496
|
+
"event PoolRegistered(bytes32 indexed poolId, address indexed token, address indexed quoteToken)",
|
|
497
|
+
"event PoolFeesSwept(bytes32 indexed poolId, uint256 quoteFees, uint256 tokenFees, uint256 buybackTokens)",
|
|
498
|
+
"event PoolConversionSkipped(bytes32 indexed poolId, string reason)",
|
|
499
|
+
"event PoolBuybackSkipped(bytes32 indexed poolId, string reason)"
|
|
500
|
+
]);
|
|
501
|
+
|
|
502
|
+
// src/modules/pool/index.ts
|
|
503
|
+
function createPoolModule(publicClient, addresses) {
|
|
504
|
+
return {
|
|
505
|
+
buildPoolKey(launch) {
|
|
506
|
+
const [currency0, currency1] = launch.pairToken.toLowerCase() < launch.token.toLowerCase() ? [launch.pairToken, launch.token] : [launch.token, launch.pairToken];
|
|
507
|
+
return {
|
|
508
|
+
currency0,
|
|
509
|
+
currency1,
|
|
510
|
+
fee: launch.poolFee,
|
|
511
|
+
tickSpacing: launch.tickSpacing,
|
|
512
|
+
hooks: addresses.memeHook
|
|
513
|
+
};
|
|
514
|
+
},
|
|
515
|
+
getPoolId(launch) {
|
|
516
|
+
const poolKey = this.buildPoolKey(launch);
|
|
517
|
+
return keccak256(
|
|
518
|
+
encodeAbiParameters(
|
|
519
|
+
[
|
|
520
|
+
{ type: "address" },
|
|
521
|
+
{ type: "address" },
|
|
522
|
+
{ type: "uint24" },
|
|
523
|
+
{ type: "int24" },
|
|
524
|
+
{ type: "address" }
|
|
525
|
+
],
|
|
526
|
+
[poolKey.currency0, poolKey.currency1, poolKey.fee, poolKey.tickSpacing, poolKey.hooks]
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
},
|
|
530
|
+
async pendingFees(poolId, currency) {
|
|
531
|
+
return publicClient.readContract({
|
|
532
|
+
address: addresses.memeHook,
|
|
533
|
+
abi: hookAbi,
|
|
534
|
+
functionName: "pendingFees",
|
|
535
|
+
args: [poolId, currency]
|
|
536
|
+
});
|
|
537
|
+
},
|
|
538
|
+
async pendingCreatorTax(poolId, currency) {
|
|
539
|
+
return publicClient.readContract({
|
|
540
|
+
address: addresses.memeHook,
|
|
541
|
+
abi: hookAbi,
|
|
542
|
+
functionName: "pendingCreatorTax",
|
|
543
|
+
args: [poolId, currency]
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
var escrowAbi = parseAbi([
|
|
549
|
+
// ── Read Functions ───────────────────────────────────────────────────────
|
|
550
|
+
"function balanceOf(address recipient) view returns (uint256)",
|
|
551
|
+
"function balanceOfToken(address recipient, address token) view returns (uint256)",
|
|
552
|
+
// ── Write Functions ──────────────────────────────────────────────────────
|
|
553
|
+
"function claim()",
|
|
554
|
+
"function claimToken(address token)",
|
|
555
|
+
// ── Events ───────────────────────────────────────────────────────────────
|
|
556
|
+
"event Credited(address indexed recipient, uint256 amount)",
|
|
557
|
+
"event Claimed(address indexed recipient, uint256 amount)",
|
|
558
|
+
"event CreditedToken(address indexed recipient, address indexed token, uint256 amount)",
|
|
559
|
+
"event ClaimedToken(address indexed recipient, address indexed token, uint256 amount)"
|
|
560
|
+
]);
|
|
561
|
+
|
|
562
|
+
// src/modules/fees/index.ts
|
|
563
|
+
function createFesModule(publicClient, wallet, addresses) {
|
|
564
|
+
return {
|
|
565
|
+
async getBalance(recipient) {
|
|
566
|
+
return publicClient.readContract({
|
|
567
|
+
address: addresses.feeEscrow,
|
|
568
|
+
abi: escrowAbi,
|
|
569
|
+
functionName: "balanceOf",
|
|
570
|
+
args: [recipient]
|
|
571
|
+
});
|
|
572
|
+
},
|
|
573
|
+
async getERC20Balance(recipient, token) {
|
|
574
|
+
return publicClient.readContract({
|
|
575
|
+
address: addresses.feeEscrow,
|
|
576
|
+
abi: escrowAbi,
|
|
577
|
+
functionName: "balanceOfToken",
|
|
578
|
+
args: [recipient, token]
|
|
579
|
+
});
|
|
580
|
+
},
|
|
581
|
+
async claim() {
|
|
582
|
+
if (!wallet) throw new Error("Wallet required for claim");
|
|
583
|
+
const { request } = await publicClient.simulateContract({
|
|
584
|
+
address: addresses.feeEscrow,
|
|
585
|
+
abi: escrowAbi,
|
|
586
|
+
functionName: "claim",
|
|
587
|
+
account: wallet.account
|
|
588
|
+
});
|
|
589
|
+
return wallet.writeContract(request);
|
|
590
|
+
},
|
|
591
|
+
async claimToken(token) {
|
|
592
|
+
if (!wallet) throw new Error("Wallet required for claimToken");
|
|
593
|
+
const { request } = await publicClient.simulateContract({
|
|
594
|
+
address: addresses.feeEscrow,
|
|
595
|
+
abi: escrowAbi,
|
|
596
|
+
functionName: "claimToken",
|
|
597
|
+
args: [token],
|
|
598
|
+
account: wallet.account
|
|
599
|
+
});
|
|
600
|
+
return wallet.writeContract(request);
|
|
601
|
+
},
|
|
602
|
+
async getUnsweptBalance(curve, _creatorFeeRecipient) {
|
|
603
|
+
const [pendingFees, pendingCreatorTax] = await Promise.all([
|
|
604
|
+
publicClient.readContract({
|
|
605
|
+
address: curve,
|
|
606
|
+
abi: curveAbi,
|
|
607
|
+
functionName: "quoteFeeBalance"
|
|
608
|
+
}),
|
|
609
|
+
publicClient.readContract({
|
|
610
|
+
address: curve,
|
|
611
|
+
abi: curveAbi,
|
|
612
|
+
functionName: "creatorTaxBalance"
|
|
613
|
+
})
|
|
614
|
+
]);
|
|
615
|
+
return { pendingFees, pendingCreatorTax };
|
|
616
|
+
},
|
|
617
|
+
async getHookPendingFees(poolId, quoteCurrency) {
|
|
618
|
+
const [pendingFees, pendingCreatorTax] = await Promise.all([
|
|
619
|
+
publicClient.readContract({
|
|
620
|
+
address: addresses.memeHook,
|
|
621
|
+
abi: hookAbi,
|
|
622
|
+
functionName: "pendingFees",
|
|
623
|
+
args: [poolId, quoteCurrency]
|
|
624
|
+
}),
|
|
625
|
+
publicClient.readContract({
|
|
626
|
+
address: addresses.memeHook,
|
|
627
|
+
abi: hookAbi,
|
|
628
|
+
functionName: "pendingCreatorTax",
|
|
629
|
+
args: [poolId, quoteCurrency]
|
|
630
|
+
})
|
|
631
|
+
]);
|
|
632
|
+
return { pendingFees, pendingCreatorTax };
|
|
633
|
+
},
|
|
634
|
+
async getFeePolicy(token) {
|
|
635
|
+
return publicClient.readContract({
|
|
636
|
+
address: addresses.factory,
|
|
637
|
+
abi: factoryAbi,
|
|
638
|
+
functionName: "getLaunchFeePolicy",
|
|
639
|
+
args: [token]
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
var vaultAbi = parseAbi([
|
|
645
|
+
// ── Read Functions ───────────────────────────────────────────────────────
|
|
646
|
+
"function totalLocked(address token) view returns (uint256)",
|
|
647
|
+
"function totalReleased(address token) view returns (uint256)",
|
|
648
|
+
"function vestedAmount(address token) view returns (uint256)",
|
|
649
|
+
"function releasable(address token) view returns (uint256)",
|
|
650
|
+
"function vestingStart(address token) view returns (uint256)",
|
|
651
|
+
"function VESTING_DURATION() view returns (uint256)",
|
|
652
|
+
// ── Write Functions ──────────────────────────────────────────────────────
|
|
653
|
+
"function release(address token) returns (uint256 released)",
|
|
654
|
+
// ── Events ───────────────────────────────────────────────────────────────
|
|
655
|
+
"event Locked(address indexed token, uint256 amount, uint256 vestingStart)",
|
|
656
|
+
"event Released(address indexed token, address indexed recipient, uint256 amount)"
|
|
657
|
+
]);
|
|
658
|
+
|
|
659
|
+
// src/modules/buyback/index.ts
|
|
660
|
+
function createBuybackModule(publicClient, wallet, addresses) {
|
|
661
|
+
return {
|
|
662
|
+
async getState(token) {
|
|
663
|
+
const [totalLocked, totalReleased, vestedAmount, releasable, vestingStart, vestingDuration] = await Promise.all([
|
|
664
|
+
publicClient.readContract({
|
|
665
|
+
address: addresses.buybackVault,
|
|
666
|
+
abi: vaultAbi,
|
|
667
|
+
functionName: "totalLocked",
|
|
668
|
+
args: [token]
|
|
669
|
+
}),
|
|
670
|
+
publicClient.readContract({
|
|
671
|
+
address: addresses.buybackVault,
|
|
672
|
+
abi: vaultAbi,
|
|
673
|
+
functionName: "totalReleased",
|
|
674
|
+
args: [token]
|
|
675
|
+
}),
|
|
676
|
+
publicClient.readContract({
|
|
677
|
+
address: addresses.buybackVault,
|
|
678
|
+
abi: vaultAbi,
|
|
679
|
+
functionName: "vestedAmount",
|
|
680
|
+
args: [token]
|
|
681
|
+
}),
|
|
682
|
+
publicClient.readContract({
|
|
683
|
+
address: addresses.buybackVault,
|
|
684
|
+
abi: vaultAbi,
|
|
685
|
+
functionName: "releasable",
|
|
686
|
+
args: [token]
|
|
687
|
+
}),
|
|
688
|
+
publicClient.readContract({
|
|
689
|
+
address: addresses.buybackVault,
|
|
690
|
+
abi: vaultAbi,
|
|
691
|
+
functionName: "vestingStart",
|
|
692
|
+
args: [token]
|
|
693
|
+
}),
|
|
694
|
+
publicClient.readContract({
|
|
695
|
+
address: addresses.buybackVault,
|
|
696
|
+
abi: vaultAbi,
|
|
697
|
+
functionName: "VESTING_DURATION"
|
|
698
|
+
})
|
|
699
|
+
]);
|
|
700
|
+
const progress = totalLocked > 0n ? Number(vestedAmount) / Number(totalLocked) : 0;
|
|
701
|
+
return {
|
|
702
|
+
totalLocked,
|
|
703
|
+
totalReleased,
|
|
704
|
+
vestedAmount,
|
|
705
|
+
releasable,
|
|
706
|
+
vestingStart,
|
|
707
|
+
vestingDuration,
|
|
708
|
+
progress
|
|
709
|
+
};
|
|
710
|
+
},
|
|
711
|
+
async release(token) {
|
|
712
|
+
if (!wallet) throw new Error("Wallet required for release");
|
|
713
|
+
const { request } = await publicClient.simulateContract({
|
|
714
|
+
address: addresses.buybackVault,
|
|
715
|
+
abi: vaultAbi,
|
|
716
|
+
functionName: "release",
|
|
717
|
+
args: [token],
|
|
718
|
+
account: wallet.account
|
|
719
|
+
});
|
|
720
|
+
return wallet.writeContract(request);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function createEventsModule(publicClient, addresses) {
|
|
725
|
+
return {
|
|
726
|
+
async getLaunches(fromBlock, toBlock = "latest") {
|
|
727
|
+
const logs = await publicClient.getLogs({
|
|
728
|
+
address: addresses.factory,
|
|
729
|
+
event: parseAbiItem(
|
|
730
|
+
"event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)"
|
|
731
|
+
),
|
|
732
|
+
fromBlock,
|
|
733
|
+
toBlock
|
|
734
|
+
});
|
|
735
|
+
return logs.map((log) => ({
|
|
736
|
+
token: log.args.token,
|
|
737
|
+
curve: log.args.curve,
|
|
738
|
+
deployer: log.args.deployer,
|
|
739
|
+
pairToken: log.args.pairToken,
|
|
740
|
+
launchConfigId: log.args.launchConfigId,
|
|
741
|
+
graduationThreshold: log.args.graduationThreshold,
|
|
742
|
+
blockNumber: log.blockNumber,
|
|
743
|
+
transactionHash: log.transactionHash
|
|
744
|
+
}));
|
|
745
|
+
},
|
|
746
|
+
async getTrades(curve, fromBlock, toBlock = "latest") {
|
|
747
|
+
const logs = await publicClient.getLogs({
|
|
748
|
+
address: curve,
|
|
749
|
+
events: [
|
|
750
|
+
parseAbiItem(
|
|
751
|
+
"event CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax)"
|
|
752
|
+
),
|
|
753
|
+
parseAbiItem(
|
|
754
|
+
"event CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax)"
|
|
755
|
+
)
|
|
756
|
+
],
|
|
757
|
+
fromBlock,
|
|
758
|
+
toBlock
|
|
759
|
+
});
|
|
760
|
+
const events = [];
|
|
761
|
+
for (const log of logs) {
|
|
762
|
+
if (log.eventName === "CurveBuy") {
|
|
763
|
+
events.push({
|
|
764
|
+
type: "buy",
|
|
765
|
+
buyer: log.args.buyer,
|
|
766
|
+
recipient: log.args.recipient,
|
|
767
|
+
quoteIn: log.args.quoteIn,
|
|
768
|
+
tokensOut: log.args.tokensOut,
|
|
769
|
+
fee: log.args.fee,
|
|
770
|
+
tax: log.args.tax,
|
|
771
|
+
blockNumber: log.blockNumber,
|
|
772
|
+
transactionHash: log.transactionHash
|
|
773
|
+
});
|
|
774
|
+
} else if (log.eventName === "CurveSell") {
|
|
775
|
+
events.push({
|
|
776
|
+
type: "sell",
|
|
777
|
+
seller: log.args.seller,
|
|
778
|
+
recipient: log.args.recipient,
|
|
779
|
+
tokensIn: log.args.tokensIn,
|
|
780
|
+
quoteOut: log.args.quoteOut,
|
|
781
|
+
fee: log.args.fee,
|
|
782
|
+
tax: log.args.tax,
|
|
783
|
+
blockNumber: log.blockNumber,
|
|
784
|
+
transactionHash: log.transactionHash
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
return events;
|
|
789
|
+
},
|
|
790
|
+
async getFeeSweeps(hook, fromBlock, toBlock = "latest") {
|
|
791
|
+
const logs = await publicClient.getLogs({
|
|
792
|
+
address: hook,
|
|
793
|
+
event: parseAbiItem(
|
|
794
|
+
"event PoolFeesSwept(bytes32 indexed poolId, uint256 quoteFees, uint256 tokenFees, uint256 buybackTokens)"
|
|
795
|
+
),
|
|
796
|
+
fromBlock,
|
|
797
|
+
toBlock
|
|
798
|
+
});
|
|
799
|
+
return logs.map((log) => ({
|
|
800
|
+
poolId: log.args.poolId,
|
|
801
|
+
quoteFees: log.args.quoteFees,
|
|
802
|
+
tokenFees: log.args.tokenFees,
|
|
803
|
+
blockNumber: log.blockNumber,
|
|
804
|
+
transactionHash: log.transactionHash
|
|
805
|
+
}));
|
|
806
|
+
},
|
|
807
|
+
async getTakeoverProposals(fromBlock, toBlock = "latest") {
|
|
808
|
+
const logs = await publicClient.getLogs({
|
|
809
|
+
address: addresses.factory,
|
|
810
|
+
event: parseAbiItem(
|
|
811
|
+
"event CreatorFeeRecipientChangeProposed(address indexed token, address indexed newRecipient, uint256 effectiveAt)"
|
|
812
|
+
),
|
|
813
|
+
fromBlock,
|
|
814
|
+
toBlock
|
|
815
|
+
});
|
|
816
|
+
return logs.map((log) => ({
|
|
817
|
+
token: log.args.token,
|
|
818
|
+
newRecipient: log.args.newRecipient,
|
|
819
|
+
effectiveAt: log.args.effectiveAt,
|
|
820
|
+
blockNumber: log.blockNumber,
|
|
821
|
+
transactionHash: log.transactionHash
|
|
822
|
+
}));
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// src/modules/takeover/index.ts
|
|
828
|
+
function createTakeoverModule(publicClient, addresses) {
|
|
829
|
+
return {
|
|
830
|
+
async getPending(token) {
|
|
831
|
+
const [recipient, effectiveAt, expiresAt] = await publicClient.readContract({
|
|
832
|
+
address: addresses.factory,
|
|
833
|
+
abi: factoryAbi,
|
|
834
|
+
functionName: "pendingCreatorFeeRecipient",
|
|
835
|
+
args: [token]
|
|
836
|
+
});
|
|
837
|
+
const isPending = recipient !== ZERO_ADDRESS && effectiveAt > 0n && expiresAt > 0n;
|
|
838
|
+
return {
|
|
839
|
+
proposedRecipient: recipient,
|
|
840
|
+
effectiveAt,
|
|
841
|
+
expiresAt,
|
|
842
|
+
isPending
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
var migrationAbi = parseAbi([
|
|
848
|
+
"function mandate() view returns (uint256)",
|
|
849
|
+
"function totalDeposited() view returns (uint256)",
|
|
850
|
+
"function settled() view returns (bool)",
|
|
851
|
+
"function claimable() view returns (bool)",
|
|
852
|
+
"function epochCount() view returns (uint256)",
|
|
853
|
+
"function getEpoch(uint256 index) view returns (uint256 rate, uint256 cap, uint256 deposited, bool closed)",
|
|
854
|
+
"function deposit(uint256 amount)",
|
|
855
|
+
"function claim()"
|
|
856
|
+
]);
|
|
857
|
+
function createMigrationModule(publicClient, wallet, _addresses) {
|
|
858
|
+
return {
|
|
859
|
+
async getStatus(migrationAddress) {
|
|
860
|
+
const mandate = await publicClient.readContract({
|
|
861
|
+
address: migrationAddress,
|
|
862
|
+
abi: migrationAbi,
|
|
863
|
+
functionName: "mandate"
|
|
864
|
+
});
|
|
865
|
+
const totalDeposited = await publicClient.readContract({
|
|
866
|
+
address: migrationAddress,
|
|
867
|
+
abi: migrationAbi,
|
|
868
|
+
functionName: "totalDeposited"
|
|
869
|
+
});
|
|
870
|
+
const settled = await publicClient.readContract({
|
|
871
|
+
address: migrationAddress,
|
|
872
|
+
abi: migrationAbi,
|
|
873
|
+
functionName: "settled"
|
|
874
|
+
});
|
|
875
|
+
const claimable = await publicClient.readContract({
|
|
876
|
+
address: migrationAddress,
|
|
877
|
+
abi: migrationAbi,
|
|
878
|
+
functionName: "claimable"
|
|
879
|
+
});
|
|
880
|
+
const epochCount = await publicClient.readContract({
|
|
881
|
+
address: migrationAddress,
|
|
882
|
+
abi: migrationAbi,
|
|
883
|
+
functionName: "epochCount"
|
|
884
|
+
});
|
|
885
|
+
const epochPromises = [];
|
|
886
|
+
for (let i = 0; i < Number(epochCount); i++) {
|
|
887
|
+
epochPromises.push(
|
|
888
|
+
publicClient.readContract({
|
|
889
|
+
address: migrationAddress,
|
|
890
|
+
abi: migrationAbi,
|
|
891
|
+
functionName: "getEpoch",
|
|
892
|
+
args: [BigInt(i)]
|
|
893
|
+
}).then((result) => ({
|
|
894
|
+
rate: result[0],
|
|
895
|
+
cap: result[1],
|
|
896
|
+
deposited: result[2],
|
|
897
|
+
closed: result[3]
|
|
898
|
+
}))
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
const epochs = await Promise.all(epochPromises);
|
|
902
|
+
return { mandate, totalDeposited, settled, claimable, epochs };
|
|
903
|
+
},
|
|
904
|
+
async deposit(migrationAddress, amount) {
|
|
905
|
+
if (!wallet) throw new Error("Wallet required for deposit");
|
|
906
|
+
const { request } = await publicClient.simulateContract({
|
|
907
|
+
address: migrationAddress,
|
|
908
|
+
abi: migrationAbi,
|
|
909
|
+
functionName: "deposit",
|
|
910
|
+
args: [amount],
|
|
911
|
+
account: wallet.account
|
|
912
|
+
});
|
|
913
|
+
return wallet.writeContract(request);
|
|
914
|
+
},
|
|
915
|
+
async claim(migrationAddress) {
|
|
916
|
+
if (!wallet) throw new Error("Wallet required for claim");
|
|
917
|
+
const { request } = await publicClient.simulateContract({
|
|
918
|
+
address: migrationAddress,
|
|
919
|
+
abi: migrationAbi,
|
|
920
|
+
functionName: "claim",
|
|
921
|
+
account: wallet.account
|
|
922
|
+
});
|
|
923
|
+
return wallet.writeContract(request);
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/client.ts
|
|
929
|
+
var robinhoodChain = {
|
|
930
|
+
id: ROBINHOOD_CHAIN_ID,
|
|
931
|
+
name: "Robinhood Chain",
|
|
932
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
933
|
+
rpcUrls: {
|
|
934
|
+
default: { http: ["https://rpc.robinhood.com"] }
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
function createPonsClient(config) {
|
|
938
|
+
const chain = {
|
|
939
|
+
...robinhoodChain,
|
|
940
|
+
id: config.chainId ?? ROBINHOOD_CHAIN_ID
|
|
941
|
+
};
|
|
942
|
+
const publicClient = config.publicClient ?? createPublicClient({
|
|
943
|
+
chain,
|
|
944
|
+
transport: http(config.rpcUrl)
|
|
945
|
+
});
|
|
946
|
+
const wallet = config.wallet;
|
|
947
|
+
const addresses = ADDRESSES;
|
|
948
|
+
const factory = createFactoryModule(publicClient, wallet, addresses);
|
|
949
|
+
const curve = createCurveModule(publicClient, wallet);
|
|
950
|
+
const pool = createPoolModule(publicClient, addresses);
|
|
951
|
+
const fees = createFesModule(publicClient, wallet, addresses);
|
|
952
|
+
const buyback = createBuybackModule(publicClient, wallet, addresses);
|
|
953
|
+
const events = createEventsModule(publicClient, addresses);
|
|
954
|
+
const takeover = createTakeoverModule(publicClient, addresses);
|
|
955
|
+
const migration = createMigrationModule(publicClient, wallet);
|
|
956
|
+
return {
|
|
957
|
+
publicClient,
|
|
958
|
+
wallet,
|
|
959
|
+
chainId: chain.id,
|
|
960
|
+
factory,
|
|
961
|
+
curve,
|
|
962
|
+
pool,
|
|
963
|
+
fees,
|
|
964
|
+
buyback,
|
|
965
|
+
events,
|
|
966
|
+
takeover,
|
|
967
|
+
migration
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
var tokenAbi = parseAbi([
|
|
971
|
+
// ── ERC-20 Standard ──────────────────────────────────────────────────────
|
|
972
|
+
"function name() view returns (string)",
|
|
973
|
+
"function symbol() view returns (string)",
|
|
974
|
+
"function decimals() view returns (uint8)",
|
|
975
|
+
"function totalSupply() view returns (uint256)",
|
|
976
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
977
|
+
"function transfer(address to, uint256 amount) returns (bool)",
|
|
978
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
979
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
980
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
981
|
+
// ── Pons Token Info ──────────────────────────────────────────────────────
|
|
982
|
+
"struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }",
|
|
983
|
+
"function getTokenInfo() view returns (address tokenDeployer, string tokenLogo, string tokenDescription, Socials tokenSocials)",
|
|
984
|
+
// ── Events ───────────────────────────────────────────────────────────────
|
|
985
|
+
"event Transfer(address indexed from, address indexed to, uint256 value)",
|
|
986
|
+
"event Approval(address indexed owner, address indexed spender, uint256 value)"
|
|
987
|
+
]);
|
|
988
|
+
var poolManagerAbi = parseAbi([
|
|
989
|
+
// ── Pool Key Struct ──────────────────────────────────────────────────────
|
|
990
|
+
"struct PoolKey { address currency0; address currency1; uint24 fee; int24 tickSpacing; address hooks; }",
|
|
991
|
+
// ── Read Functions ───────────────────────────────────────────────────────
|
|
992
|
+
"function getPool(PoolKey key) view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFeesToken0, uint24 protocolFeesToken1, uint24 lpFeesToken0, uint24 lpFeesToken1)",
|
|
993
|
+
"function getSlot0(bytes32 poolId) view returns (uint160 sqrtPriceX96, int24 tick, uint24 protocolFeesToken0, uint24 protocolFeesToken1, uint24 lpFeesToken0, uint24 lpFeesToken1)",
|
|
994
|
+
// ── Events ───────────────────────────────────────────────────────────────
|
|
995
|
+
"event Swap(bytes32 indexed poolId, address indexed sender, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)",
|
|
996
|
+
"event Initialize(bytes32 indexed poolId, address indexed currency0, address indexed currency1, int24 tickSpacing, address hooks)"
|
|
997
|
+
]);
|
|
998
|
+
function normalizeAddress(address) {
|
|
999
|
+
if (!address || address === ZERO_ADDRESS) return ZERO_ADDRESS;
|
|
1000
|
+
return getAddress(address);
|
|
1001
|
+
}
|
|
1002
|
+
function isValidAddress(address) {
|
|
1003
|
+
try {
|
|
1004
|
+
return isAddress(address);
|
|
1005
|
+
} catch {
|
|
1006
|
+
return false;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function formatETH(value) {
|
|
1010
|
+
return formatEther(value);
|
|
1011
|
+
}
|
|
1012
|
+
function formatTokenAmount(value, decimals) {
|
|
1013
|
+
return formatUnits(value, decimals);
|
|
1014
|
+
}
|
|
1015
|
+
function shortenAddress(address) {
|
|
1016
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
export { ADDRESSES, BPS, MAX_SNIPE_EXEMPTIONS, MIGRATION_CLAIM_WINDOW_SECS, PHASE, ROBINHOOD_CHAIN_ID, SNIPE_TAX_DECAY_NS, VESTING_DURATION_SECS, ZERO_ADDRESS, amountIn, amountOut, applyBps, ceilDiv, createPonsClient, curveAbi, escrowAbi, factoryAbi, formatETH, formatTokenAmount, graduationProgress, hookAbi, isValidAddress, marginalPrice, minOutputWithSlippage, normalizeAddress, poolManagerAbi, requirePositiveAmount, requireValidAddress, requireValidCreatorTax, requireValidExemptions, requireValidSlippage, routerAbi, shortenAddress, subtractBps, tokenAbi, vaultAbi };
|
|
1020
|
+
//# sourceMappingURL=index.js.map
|
|
1021
|
+
//# sourceMappingURL=index.js.map
|