fourmm 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 +147 -0
- package/dist/bin.d.ts +9 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +14 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +319 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +35 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +145 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/query.d.ts +51 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +364 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/token.d.ts +55 -0
- package/dist/commands/token.d.ts.map +1 -0
- package/dist/commands/token.js +650 -0
- package/dist/commands/token.js.map +1 -0
- package/dist/commands/tools.d.ts +54 -0
- package/dist/commands/tools.d.ts.map +1 -0
- package/dist/commands/tools.js +499 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commands/trade.d.ts +63 -0
- package/dist/commands/trade.d.ts.map +1 -0
- package/dist/commands/trade.js +933 -0
- package/dist/commands/trade.js.map +1 -0
- package/dist/commands/transfer.d.ts +51 -0
- package/dist/commands/transfer.d.ts.map +1 -0
- package/dist/commands/transfer.js +728 -0
- package/dist/commands/transfer.js.map +1 -0
- package/dist/commands/wallet.d.ts +111 -0
- package/dist/commands/wallet.d.ts.map +1 -0
- package/dist/commands/wallet.js +716 -0
- package/dist/commands/wallet.js.map +1 -0
- package/dist/contracts/erc20.d.ts +72 -0
- package/dist/contracts/erc20.d.ts.map +1 -0
- package/dist/contracts/erc20.js +55 -0
- package/dist/contracts/erc20.js.map +1 -0
- package/dist/contracts/fourmemeMmRouter.d.ts +68 -0
- package/dist/contracts/fourmemeMmRouter.d.ts.map +1 -0
- package/dist/contracts/fourmemeMmRouter.js +48 -0
- package/dist/contracts/fourmemeMmRouter.js.map +1 -0
- package/dist/contracts/pancakeRouter.d.ts +73 -0
- package/dist/contracts/pancakeRouter.d.ts.map +1 -0
- package/dist/contracts/pancakeRouter.js +50 -0
- package/dist/contracts/pancakeRouter.js.map +1 -0
- package/dist/contracts/tokenManager2.d.ts +193 -0
- package/dist/contracts/tokenManager2.d.ts.map +1 -0
- package/dist/contracts/tokenManager2.js +108 -0
- package/dist/contracts/tokenManager2.js.map +1 -0
- package/dist/contracts/tokenManagerHelper3.d.ts +118 -0
- package/dist/contracts/tokenManagerHelper3.d.ts.map +1 -0
- package/dist/contracts/tokenManagerHelper3.js +66 -0
- package/dist/contracts/tokenManagerHelper3.js.map +1 -0
- package/dist/datastore/cache.d.ts +20 -0
- package/dist/datastore/cache.d.ts.map +1 -0
- package/dist/datastore/cache.js +45 -0
- package/dist/datastore/cache.js.map +1 -0
- package/dist/datastore/index.d.ts +85 -0
- package/dist/datastore/index.d.ts.map +1 -0
- package/dist/datastore/index.js +341 -0
- package/dist/datastore/index.js.map +1 -0
- package/dist/datastore/paths.d.ts +17 -0
- package/dist/datastore/paths.d.ts.map +1 -0
- package/dist/datastore/paths.js +39 -0
- package/dist/datastore/paths.js.map +1 -0
- package/dist/datastore/types.d.ts +105 -0
- package/dist/datastore/types.d.ts.map +1 -0
- package/dist/datastore/types.js +8 -0
- package/dist/datastore/types.js.map +1 -0
- package/dist/fourmeme/auth.d.ts +22 -0
- package/dist/fourmeme/auth.d.ts.map +1 -0
- package/dist/fourmeme/auth.js +78 -0
- package/dist/fourmeme/auth.js.map +1 -0
- package/dist/fourmeme/create.d.ts +31 -0
- package/dist/fourmeme/create.d.ts.map +1 -0
- package/dist/fourmeme/create.js +111 -0
- package/dist/fourmeme/create.js.map +1 -0
- package/dist/fourmeme/upload.d.ts +16 -0
- package/dist/fourmeme/upload.d.ts.map +1 -0
- package/dist/fourmeme/upload.js +52 -0
- package/dist/fourmeme/upload.js.map +1 -0
- package/dist/lib/bundle.d.ts +51 -0
- package/dist/lib/bundle.d.ts.map +1 -0
- package/dist/lib/bundle.js +95 -0
- package/dist/lib/bundle.js.map +1 -0
- package/dist/lib/config.d.ts +58 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +183 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/const.d.ts +165 -0
- package/dist/lib/const.d.ts.map +1 -0
- package/dist/lib/const.js +98 -0
- package/dist/lib/const.js.map +1 -0
- package/dist/lib/env.d.ts +14 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +18 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/guards.d.ts +44 -0
- package/dist/lib/guards.d.ts.map +1 -0
- package/dist/lib/guards.js +65 -0
- package/dist/lib/guards.js.map +1 -0
- package/dist/lib/identify.d.ts +85 -0
- package/dist/lib/identify.d.ts.map +1 -0
- package/dist/lib/identify.js +88 -0
- package/dist/lib/identify.js.map +1 -0
- package/dist/lib/pricing.d.ts +62 -0
- package/dist/lib/pricing.d.ts.map +1 -0
- package/dist/lib/pricing.js +302 -0
- package/dist/lib/pricing.js.map +1 -0
- package/dist/lib/routing.d.ts +57 -0
- package/dist/lib/routing.d.ts.map +1 -0
- package/dist/lib/routing.js +67 -0
- package/dist/lib/routing.js.map +1 -0
- package/dist/lib/slippage.d.ts +29 -0
- package/dist/lib/slippage.d.ts.map +1 -0
- package/dist/lib/slippage.js +110 -0
- package/dist/lib/slippage.js.map +1 -0
- package/dist/lib/tracker.d.ts +68 -0
- package/dist/lib/tracker.d.ts.map +1 -0
- package/dist/lib/tracker.js +155 -0
- package/dist/lib/tracker.js.map +1 -0
- package/dist/lib/viem.d.ts +12 -0
- package/dist/lib/viem.d.ts.map +1 -0
- package/dist/lib/viem.js +44 -0
- package/dist/lib/viem.js.map +1 -0
- package/dist/lib/wallet-rows.d.ts +30 -0
- package/dist/lib/wallet-rows.d.ts.map +1 -0
- package/dist/lib/wallet-rows.js +9 -0
- package/dist/lib/wallet-rows.js.map +1 -0
- package/dist/lib/walletClient.d.ts +16 -0
- package/dist/lib/walletClient.d.ts.map +1 -0
- package/dist/lib/walletClient.js +26 -0
- package/dist/lib/walletClient.js.map +1 -0
- package/dist/wallets/groups/encrypt.d.ts +26 -0
- package/dist/wallets/groups/encrypt.d.ts.map +1 -0
- package/dist/wallets/groups/encrypt.js +52 -0
- package/dist/wallets/groups/encrypt.js.map +1 -0
- package/dist/wallets/groups/generate.d.ts +19 -0
- package/dist/wallets/groups/generate.d.ts.map +1 -0
- package/dist/wallets/groups/generate.js +36 -0
- package/dist/wallets/groups/generate.js.map +1 -0
- package/dist/wallets/groups/store.d.ts +107 -0
- package/dist/wallets/groups/store.d.ts.map +1 -0
- package/dist/wallets/groups/store.js +254 -0
- package/dist/wallets/groups/store.js.map +1 -0
- package/package.json +50 -0
- package/skills/SKILL.md +187 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token pricing.
|
|
3
|
+
*
|
|
4
|
+
* Dual-path:
|
|
5
|
+
* 1. Bonding curve (liquidityAdded=false) → read on-chain
|
|
6
|
+
* TokenManagerHelper3.getTokenInfo.lastPrice directly. No network round-trip
|
|
7
|
+
* to a third-party pricing API.
|
|
8
|
+
* 2. Graduated (liquidityAdded=true) → query GeckoTerminal
|
|
9
|
+
* (`/api/v2/networks/bsc/tokens/{address}`) for PancakeSwap-derived price.
|
|
10
|
+
*
|
|
11
|
+
* BNB→USD conversion uses CoinGecko's free simple-price endpoint, cached for
|
|
12
|
+
* 60 seconds in DataStore's global/bnb-price.json.
|
|
13
|
+
*
|
|
14
|
+
* All price outputs are in BNB per token (float). The raw on-chain lastPrice
|
|
15
|
+
* is BNB-wei per smallest-token-unit (18 decimals); for meme tokens the price
|
|
16
|
+
* is tiny (well below 1e-6 BNB), so converting via Number() is safe in
|
|
17
|
+
* practice. If Week 3 bot code cares about sub-wei precision we'll switch to
|
|
18
|
+
* BigInt-to-string formatting.
|
|
19
|
+
*/
|
|
20
|
+
import type { Address, PublicClient } from 'viem';
|
|
21
|
+
import type { TradingPathKind } from '../datastore/types.js';
|
|
22
|
+
/**
|
|
23
|
+
* Where the price value came from (freshness axis).
|
|
24
|
+
*
|
|
25
|
+
* - `live` → just fetched from Helper3 or GeckoTerminal this invocation
|
|
26
|
+
* - `cache` → served from DataStore pool-info within TTL
|
|
27
|
+
* - `stale` → cache hit beyond TTL but upstream fetch failed (best-effort)
|
|
28
|
+
*/
|
|
29
|
+
export type PriceOrigin = 'live' | 'cache' | 'stale';
|
|
30
|
+
export type TokenPrice = {
|
|
31
|
+
ca: Address;
|
|
32
|
+
priceBnb: number;
|
|
33
|
+
priceUsd: number;
|
|
34
|
+
/** Trading path: bonding-curve / pancake (static token property) */
|
|
35
|
+
path: TradingPathKind;
|
|
36
|
+
/** Freshness of this value */
|
|
37
|
+
origin: PriceOrigin;
|
|
38
|
+
/** Unix ms */
|
|
39
|
+
updatedAt: number;
|
|
40
|
+
liquidityAdded: boolean;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Get the current price of a Four.meme token.
|
|
44
|
+
*
|
|
45
|
+
* Cache-through: checks DataStore pool-info first; on miss or expiry,
|
|
46
|
+
* fetches live and writes back. Network failures are surfaced via
|
|
47
|
+
* throwing, but we fall back to cache if the fresh fetch fails and a stale
|
|
48
|
+
* value exists.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getTokenPrice(client: PublicClient, ca: Address, options?: {
|
|
51
|
+
fetchImpl?: typeof fetch | undefined;
|
|
52
|
+
}): Promise<TokenPrice>;
|
|
53
|
+
/**
|
|
54
|
+
* Get BNB → USD price with 60s cache.
|
|
55
|
+
*
|
|
56
|
+
* Primary source: on-chain PancakeSwap WBNB/USDT pair reserves (no external
|
|
57
|
+
* API dependency — uses the same viem client that's already working for RPC).
|
|
58
|
+
*
|
|
59
|
+
* Fallback: CoinGecko simple/price endpoint (may fail in restricted networks).
|
|
60
|
+
*/
|
|
61
|
+
export declare function getBnbUsdPrice(clientOrFetch?: PublicClient | typeof fetch | undefined): Promise<number>;
|
|
62
|
+
//# sourceMappingURL=pricing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/lib/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAwBjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAEpD,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,OAAO,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,IAAI,EAAE,eAAe,CAAA;IACrB,8BAA8B;IAC9B,MAAM,EAAE,WAAW,CAAA;IACnB,cAAc;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAcD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,EAAE,EAAE,OAAO,EACX,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,KAAK,GAAG,SAAS,CAAA;CAAO,GACrD,OAAO,CAAC,UAAU,CAAC,CAmJrB;AAuCD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,aAAa,CAAC,EAAE,YAAY,GAAG,OAAO,KAAK,GAAG,SAAS,GACtD,OAAO,CAAC,MAAM,CAAC,CA0DjB"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token pricing.
|
|
3
|
+
*
|
|
4
|
+
* Dual-path:
|
|
5
|
+
* 1. Bonding curve (liquidityAdded=false) → read on-chain
|
|
6
|
+
* TokenManagerHelper3.getTokenInfo.lastPrice directly. No network round-trip
|
|
7
|
+
* to a third-party pricing API.
|
|
8
|
+
* 2. Graduated (liquidityAdded=true) → query GeckoTerminal
|
|
9
|
+
* (`/api/v2/networks/bsc/tokens/{address}`) for PancakeSwap-derived price.
|
|
10
|
+
*
|
|
11
|
+
* BNB→USD conversion uses CoinGecko's free simple-price endpoint, cached for
|
|
12
|
+
* 60 seconds in DataStore's global/bnb-price.json.
|
|
13
|
+
*
|
|
14
|
+
* All price outputs are in BNB per token (float). The raw on-chain lastPrice
|
|
15
|
+
* is BNB-wei per smallest-token-unit (18 decimals); for meme tokens the price
|
|
16
|
+
* is tiny (well below 1e-6 BNB), so converting via Number() is safe in
|
|
17
|
+
* practice. If Week 3 bot code cares about sub-wei precision we'll switch to
|
|
18
|
+
* BigInt-to-string formatting.
|
|
19
|
+
*/
|
|
20
|
+
import { TOKEN_MANAGER_HELPER3, PANCAKE_V2_FACTORY, WBNB } from './const.js';
|
|
21
|
+
import { tokenManagerHelper3Abi } from '../contracts/tokenManagerHelper3.js';
|
|
22
|
+
import { getDataStore } from '../datastore/index.js';
|
|
23
|
+
import { loadConfig } from './config.js';
|
|
24
|
+
// PancakeFactory getPair — to resolve LP pair address for graduated tokens
|
|
25
|
+
const pancakeFactoryAbi = [
|
|
26
|
+
{
|
|
27
|
+
type: 'function',
|
|
28
|
+
name: 'getPair',
|
|
29
|
+
stateMutability: 'view',
|
|
30
|
+
inputs: [
|
|
31
|
+
{ name: 'tokenA', type: 'address' },
|
|
32
|
+
{ name: 'tokenB', type: 'address' },
|
|
33
|
+
],
|
|
34
|
+
outputs: [{ name: 'pair', type: 'address' }],
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
// ============================================================
|
|
38
|
+
// Constants
|
|
39
|
+
// ============================================================
|
|
40
|
+
const BNB_DECIMALS = 18;
|
|
41
|
+
const POOL_CACHE_TTL_MS = 30_000;
|
|
42
|
+
const BNB_PRICE_TTL_MS = 60_000;
|
|
43
|
+
// ============================================================
|
|
44
|
+
// Public API
|
|
45
|
+
// ============================================================
|
|
46
|
+
/**
|
|
47
|
+
* Get the current price of a Four.meme token.
|
|
48
|
+
*
|
|
49
|
+
* Cache-through: checks DataStore pool-info first; on miss or expiry,
|
|
50
|
+
* fetches live and writes back. Network failures are surfaced via
|
|
51
|
+
* throwing, but we fall back to cache if the fresh fetch fails and a stale
|
|
52
|
+
* value exists.
|
|
53
|
+
*/
|
|
54
|
+
export async function getTokenPrice(client, ca, options = {}) {
|
|
55
|
+
const ds = getDataStore();
|
|
56
|
+
// Check cached pool-info first
|
|
57
|
+
const cached = ds.getPoolInfo(ca);
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
if (cached && now - cached.updatedAt < POOL_CACHE_TTL_MS) {
|
|
60
|
+
return {
|
|
61
|
+
ca,
|
|
62
|
+
priceBnb: cached.priceBnb,
|
|
63
|
+
priceUsd: cached.priceUsd,
|
|
64
|
+
path: cached.path,
|
|
65
|
+
origin: 'cache',
|
|
66
|
+
updatedAt: cached.updatedAt,
|
|
67
|
+
liquidityAdded: cached.path === 'pancake',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Hit Helper3 to learn graduation status + on-chain lastPrice
|
|
71
|
+
let info;
|
|
72
|
+
try {
|
|
73
|
+
info = await client.readContract({
|
|
74
|
+
address: TOKEN_MANAGER_HELPER3,
|
|
75
|
+
abi: tokenManagerHelper3Abi,
|
|
76
|
+
functionName: 'getTokenInfo',
|
|
77
|
+
args: [ca],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (cached) {
|
|
82
|
+
// Fresh fetch failed — return the stale cache we have
|
|
83
|
+
return {
|
|
84
|
+
ca,
|
|
85
|
+
priceBnb: cached.priceBnb,
|
|
86
|
+
priceUsd: cached.priceUsd,
|
|
87
|
+
path: cached.path,
|
|
88
|
+
origin: 'stale',
|
|
89
|
+
updatedAt: cached.updatedAt,
|
|
90
|
+
liquidityAdded: cached.path === 'pancake',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
throw new Error(`pricing.getTokenPrice(${ca}): Helper3 read failed${err instanceof Error ? `: ${err.message}` : ''}`);
|
|
94
|
+
}
|
|
95
|
+
const version = info[0];
|
|
96
|
+
const lastPrice = info[3];
|
|
97
|
+
const liquidityAdded = info[11];
|
|
98
|
+
if (version === 0n) {
|
|
99
|
+
throw new Error(`pricing.getTokenPrice(${ca}): token not found on Four.meme`);
|
|
100
|
+
}
|
|
101
|
+
// ---- Branch 1: bonding curve ----
|
|
102
|
+
if (!liquidityAdded) {
|
|
103
|
+
const priceBnb = Number(lastPrice) / 10 ** BNB_DECIMALS;
|
|
104
|
+
const bnbUsd = await getBnbUsdPrice(client).catch(() => 0);
|
|
105
|
+
const priceUsd = priceBnb * bnbUsd;
|
|
106
|
+
ds.savePoolInfo({
|
|
107
|
+
ca,
|
|
108
|
+
pairAddress: '',
|
|
109
|
+
path: 'bonding-curve',
|
|
110
|
+
priceBnb,
|
|
111
|
+
priceUsd,
|
|
112
|
+
graduationProgress: null,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
ca,
|
|
117
|
+
priceBnb,
|
|
118
|
+
priceUsd,
|
|
119
|
+
path: 'bonding-curve',
|
|
120
|
+
origin: 'live',
|
|
121
|
+
updatedAt: now,
|
|
122
|
+
liquidityAdded: false,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// ---- Branch 2: graduated → GeckoTerminal ----
|
|
126
|
+
const gecko = await fetchGeckoTerminalPrice(ca, options.fetchImpl).catch(() => null);
|
|
127
|
+
if (!gecko) {
|
|
128
|
+
if (cached) {
|
|
129
|
+
return {
|
|
130
|
+
ca,
|
|
131
|
+
priceBnb: cached.priceBnb,
|
|
132
|
+
priceUsd: cached.priceUsd,
|
|
133
|
+
path: cached.path,
|
|
134
|
+
origin: 'stale',
|
|
135
|
+
updatedAt: cached.updatedAt,
|
|
136
|
+
liquidityAdded: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`pricing.getTokenPrice(${ca}): token has graduated but GeckoTerminal fetch failed`);
|
|
140
|
+
}
|
|
141
|
+
const bnbUsd = await getBnbUsdPrice(client).catch(() => 0);
|
|
142
|
+
const priceUsd = gecko.priceUsd;
|
|
143
|
+
// Convert USD → BNB when we know the BNB price
|
|
144
|
+
const priceBnb = bnbUsd > 0 ? priceUsd / bnbUsd : 0;
|
|
145
|
+
// Resolve PancakeSwap LP pair address so `query kline` can auto-resolve it
|
|
146
|
+
let pairAddress = '';
|
|
147
|
+
try {
|
|
148
|
+
const pair = await client.readContract({
|
|
149
|
+
address: PANCAKE_V2_FACTORY,
|
|
150
|
+
abi: pancakeFactoryAbi,
|
|
151
|
+
functionName: 'getPair',
|
|
152
|
+
args: [ca, WBNB],
|
|
153
|
+
});
|
|
154
|
+
if (pair && pair !== '0x0000000000000000000000000000000000000000') {
|
|
155
|
+
pairAddress = pair;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Factory query failed — pairAddress stays empty, kline needs --pool
|
|
160
|
+
}
|
|
161
|
+
ds.savePoolInfo({
|
|
162
|
+
ca,
|
|
163
|
+
pairAddress,
|
|
164
|
+
path: 'pancake',
|
|
165
|
+
priceBnb,
|
|
166
|
+
priceUsd,
|
|
167
|
+
graduationProgress: null,
|
|
168
|
+
updatedAt: now,
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
ca,
|
|
172
|
+
priceBnb,
|
|
173
|
+
priceUsd,
|
|
174
|
+
path: 'pancake',
|
|
175
|
+
origin: 'live',
|
|
176
|
+
updatedAt: now,
|
|
177
|
+
liquidityAdded: true,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// ============================================================
|
|
181
|
+
// BNB / USD price (CoinGecko)
|
|
182
|
+
// ============================================================
|
|
183
|
+
/**
|
|
184
|
+
* Get BNB → USD price with 60s cache.
|
|
185
|
+
*
|
|
186
|
+
* We use CoinGecko's public `simple/price` endpoint (no auth). If it fails
|
|
187
|
+
* or is rate-limited, we return the cached value if present, else 0 (so the
|
|
188
|
+
* rest of the pipeline still returns a priceBnb but priceUsd will be 0).
|
|
189
|
+
*/
|
|
190
|
+
// PancakeSwap WBNB/USDT pair — used for on-chain BNB/USD pricing
|
|
191
|
+
// This avoids depending on external APIs (CoinGecko/GeckoTerminal) that
|
|
192
|
+
// may be unreachable from certain environments (e.g. WSL2 TLS issues).
|
|
193
|
+
const PANCAKE_WBNB_USDT_PAIR = '0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae';
|
|
194
|
+
const pairReservesAbi = [
|
|
195
|
+
{
|
|
196
|
+
type: 'function',
|
|
197
|
+
name: 'getReserves',
|
|
198
|
+
stateMutability: 'view',
|
|
199
|
+
inputs: [],
|
|
200
|
+
outputs: [
|
|
201
|
+
{ name: 'reserve0', type: 'uint112' },
|
|
202
|
+
{ name: 'reserve1', type: 'uint112' },
|
|
203
|
+
{ name: 'blockTimestampLast', type: 'uint32' },
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: 'function',
|
|
208
|
+
name: 'token0',
|
|
209
|
+
stateMutability: 'view',
|
|
210
|
+
inputs: [],
|
|
211
|
+
outputs: [{ name: '', type: 'address' }],
|
|
212
|
+
},
|
|
213
|
+
];
|
|
214
|
+
/**
|
|
215
|
+
* Get BNB → USD price with 60s cache.
|
|
216
|
+
*
|
|
217
|
+
* Primary source: on-chain PancakeSwap WBNB/USDT pair reserves (no external
|
|
218
|
+
* API dependency — uses the same viem client that's already working for RPC).
|
|
219
|
+
*
|
|
220
|
+
* Fallback: CoinGecko simple/price endpoint (may fail in restricted networks).
|
|
221
|
+
*/
|
|
222
|
+
export async function getBnbUsdPrice(clientOrFetch) {
|
|
223
|
+
const ds = getDataStore();
|
|
224
|
+
const cached = ds.getBnbPrice();
|
|
225
|
+
const now = Date.now();
|
|
226
|
+
if (cached && now - cached.updatedAt < BNB_PRICE_TTL_MS) {
|
|
227
|
+
return cached.priceUsd;
|
|
228
|
+
}
|
|
229
|
+
// Try on-chain first: read PancakeSwap WBNB/USDT reserves
|
|
230
|
+
if (clientOrFetch && typeof clientOrFetch === 'object' && 'readContract' in clientOrFetch) {
|
|
231
|
+
const client = clientOrFetch;
|
|
232
|
+
try {
|
|
233
|
+
const token0 = await client.readContract({
|
|
234
|
+
address: PANCAKE_WBNB_USDT_PAIR,
|
|
235
|
+
abi: pairReservesAbi,
|
|
236
|
+
functionName: 'token0',
|
|
237
|
+
});
|
|
238
|
+
const [reserve0, reserve1] = await client.readContract({
|
|
239
|
+
address: PANCAKE_WBNB_USDT_PAIR,
|
|
240
|
+
abi: pairReservesAbi,
|
|
241
|
+
functionName: 'getReserves',
|
|
242
|
+
});
|
|
243
|
+
// Determine which reserve is WBNB and which is USDT
|
|
244
|
+
const wbnbLower = WBNB.toLowerCase();
|
|
245
|
+
const isToken0Wbnb = token0.toLowerCase() === wbnbLower;
|
|
246
|
+
const wbnbReserve = isToken0Wbnb ? reserve0 : reserve1;
|
|
247
|
+
const usdtReserve = isToken0Wbnb ? reserve1 : reserve0;
|
|
248
|
+
// WBNB = 18 decimals, USDT = 18 decimals on BSC
|
|
249
|
+
if (wbnbReserve > 0n) {
|
|
250
|
+
const price = Number(usdtReserve) / Number(wbnbReserve);
|
|
251
|
+
if (price > 0) {
|
|
252
|
+
ds.saveBnbPrice(price);
|
|
253
|
+
return price;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// On-chain query failed — try API fallback
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Fallback: CoinGecko API
|
|
262
|
+
try {
|
|
263
|
+
const fetchFn = (typeof clientOrFetch === 'function' ? clientOrFetch : globalThis.fetch);
|
|
264
|
+
const res = await fetchFn('https://api.coingecko.com/api/v3/simple/price?ids=binancecoin&vs_currencies=usd');
|
|
265
|
+
if (!res.ok)
|
|
266
|
+
return cached?.priceUsd ?? 0;
|
|
267
|
+
const json = (await res.json());
|
|
268
|
+
const price = json.binancecoin?.usd;
|
|
269
|
+
if (typeof price === 'number' && price > 0) {
|
|
270
|
+
ds.saveBnbPrice(price);
|
|
271
|
+
return price;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// API also failed
|
|
276
|
+
}
|
|
277
|
+
return cached?.priceUsd ?? 0;
|
|
278
|
+
}
|
|
279
|
+
async function fetchGeckoTerminalPrice(ca, fetchImpl) {
|
|
280
|
+
const fetchFn = fetchImpl ?? globalThis.fetch;
|
|
281
|
+
const config = loadConfig();
|
|
282
|
+
const url = `${config.geckoTerminalUrl}/networks/bsc/tokens/${ca}`;
|
|
283
|
+
try {
|
|
284
|
+
const res = await fetchFn(url, {
|
|
285
|
+
headers: { accept: 'application/json' },
|
|
286
|
+
});
|
|
287
|
+
if (!res.ok)
|
|
288
|
+
return null;
|
|
289
|
+
const json = (await res.json());
|
|
290
|
+
const raw = json.data?.attributes?.price_usd;
|
|
291
|
+
if (!raw)
|
|
292
|
+
return null;
|
|
293
|
+
const priceUsd = Number.parseFloat(raw);
|
|
294
|
+
if (!Number.isFinite(priceUsd) || priceUsd <= 0)
|
|
295
|
+
return null;
|
|
296
|
+
return { priceUsd };
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/lib/pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trading path resolution.
|
|
3
|
+
*
|
|
4
|
+
* Every trade / tools command MUST call `resolveTradingPath(client, ca)`
|
|
5
|
+
* before any on-chain write. The result tells the caller which contract
|
|
6
|
+
* interface to use (bonding-curve TokenManager2 vs graduated PancakeSwap
|
|
7
|
+
* router) AND exposes the raw Helper3.getTokenInfo result so downstream
|
|
8
|
+
* code can reuse fields (launchTime, offers, funds, etc.) without firing
|
|
9
|
+
* another RPC call.
|
|
10
|
+
*
|
|
11
|
+
* This is the single source of truth for routing — do NOT hardcode
|
|
12
|
+
* TOKEN_MANAGER_V2 anywhere else.
|
|
13
|
+
*/
|
|
14
|
+
import type { Address, PublicClient, ReadContractReturnType } from 'viem';
|
|
15
|
+
import { tokenManagerHelper3Abi } from '../contracts/tokenManagerHelper3.js';
|
|
16
|
+
/** The raw tuple that Helper3.getTokenInfo returns */
|
|
17
|
+
export type GetTokenInfoResult = ReadContractReturnType<typeof tokenManagerHelper3Abi, 'getTokenInfo'>;
|
|
18
|
+
export type BondingCurvePath = {
|
|
19
|
+
path: 'bonding-curve';
|
|
20
|
+
/** The V1 or V2 TokenManager that owns this token — use this, not a hardcoded address */
|
|
21
|
+
router: Address;
|
|
22
|
+
version: 1 | 2;
|
|
23
|
+
/** Quote token (address(0) = BNB) */
|
|
24
|
+
quote: Address;
|
|
25
|
+
};
|
|
26
|
+
export type PancakePath = {
|
|
27
|
+
path: 'pancake';
|
|
28
|
+
/** PancakeSwap V2 router on BSC mainnet */
|
|
29
|
+
router: Address;
|
|
30
|
+
/** LP pair address if known (0x0 for now; Week 3 will resolve from factory) */
|
|
31
|
+
pairAddress: Address | '0x0000000000000000000000000000000000000000';
|
|
32
|
+
/** Quote token — always BNB for Four.meme graduated tokens */
|
|
33
|
+
quote: Address;
|
|
34
|
+
};
|
|
35
|
+
export type TradingPath = BondingCurvePath | PancakePath;
|
|
36
|
+
/**
|
|
37
|
+
* Full result of a routing call: the chosen path PLUS the raw Helper3 info
|
|
38
|
+
* tuple the caller can reuse. Downstream code reads:
|
|
39
|
+
* - `rawInfo[6]` launchTime (for tradeable check)
|
|
40
|
+
* - `rawInfo[7]` offers
|
|
41
|
+
* - `rawInfo[8]` maxOffers
|
|
42
|
+
* - `rawInfo[9]` funds
|
|
43
|
+
* - `rawInfo[10]` maxFunds
|
|
44
|
+
*/
|
|
45
|
+
export type ResolveResult = {
|
|
46
|
+
tradingPath: TradingPath;
|
|
47
|
+
rawInfo: GetTokenInfoResult;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Inspect a token's on-chain state and pick the correct trading path.
|
|
51
|
+
*
|
|
52
|
+
* Throws on a malformed / unknown token (version 0). Callers should wrap
|
|
53
|
+
* this in the command's error handler so the agent gets a structured error
|
|
54
|
+
* instead of a viem revert trace.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveTradingPath(client: PublicClient, ca: Address): Promise<ResolveResult>;
|
|
57
|
+
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/lib/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,MAAM,CAAA;AAEzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA;AAM5E,sDAAsD;AACtD,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,CACrD,OAAO,sBAAsB,EAC7B,cAAc,CACf,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,eAAe,CAAA;IACrB,yFAAyF;IACzF,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,CAAC,GAAG,CAAC,CAAA;IACd,qCAAqC;IACrC,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,CAAA;IACf,2CAA2C;IAC3C,MAAM,EAAE,OAAO,CAAA;IACf,+EAA+E;IAC/E,WAAW,EAAE,OAAO,GAAG,4CAA4C,CAAA;IACnE,8DAA8D;IAC9D,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,WAAW,CAAA;AAExD;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,WAAW,CAAA;IACxB,OAAO,EAAE,kBAAkB,CAAA;CAC5B,CAAA;AAMD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,YAAY,EACpB,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,aAAa,CAAC,CAgDxB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trading path resolution.
|
|
3
|
+
*
|
|
4
|
+
* Every trade / tools command MUST call `resolveTradingPath(client, ca)`
|
|
5
|
+
* before any on-chain write. The result tells the caller which contract
|
|
6
|
+
* interface to use (bonding-curve TokenManager2 vs graduated PancakeSwap
|
|
7
|
+
* router) AND exposes the raw Helper3.getTokenInfo result so downstream
|
|
8
|
+
* code can reuse fields (launchTime, offers, funds, etc.) without firing
|
|
9
|
+
* another RPC call.
|
|
10
|
+
*
|
|
11
|
+
* This is the single source of truth for routing — do NOT hardcode
|
|
12
|
+
* TOKEN_MANAGER_V2 anywhere else.
|
|
13
|
+
*/
|
|
14
|
+
import { PANCAKE_V2_ROUTER, TOKEN_MANAGER_HELPER3 } from './const.js';
|
|
15
|
+
import { tokenManagerHelper3Abi } from '../contracts/tokenManagerHelper3.js';
|
|
16
|
+
// ============================================================
|
|
17
|
+
// Public API
|
|
18
|
+
// ============================================================
|
|
19
|
+
/**
|
|
20
|
+
* Inspect a token's on-chain state and pick the correct trading path.
|
|
21
|
+
*
|
|
22
|
+
* Throws on a malformed / unknown token (version 0). Callers should wrap
|
|
23
|
+
* this in the command's error handler so the agent gets a structured error
|
|
24
|
+
* instead of a viem revert trace.
|
|
25
|
+
*/
|
|
26
|
+
export async function resolveTradingPath(client, ca) {
|
|
27
|
+
let info;
|
|
28
|
+
try {
|
|
29
|
+
info = await client.readContract({
|
|
30
|
+
address: TOKEN_MANAGER_HELPER3,
|
|
31
|
+
abi: tokenManagerHelper3Abi,
|
|
32
|
+
functionName: 'getTokenInfo',
|
|
33
|
+
args: [ca],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
throw new Error(`resolveTradingPath(${ca}): Helper3 read failed${err instanceof Error ? `: ${err.message}` : ''}`);
|
|
38
|
+
}
|
|
39
|
+
const version = info[0];
|
|
40
|
+
const tokenManager = info[1];
|
|
41
|
+
const quote = info[2];
|
|
42
|
+
const liquidityAdded = info[11];
|
|
43
|
+
if (version === 0n) {
|
|
44
|
+
throw new Error(`resolveTradingPath(${ca}): token not registered with Four.meme TokenManager`);
|
|
45
|
+
}
|
|
46
|
+
let tradingPath;
|
|
47
|
+
if (liquidityAdded) {
|
|
48
|
+
tradingPath = {
|
|
49
|
+
path: 'pancake',
|
|
50
|
+
router: PANCAKE_V2_ROUTER,
|
|
51
|
+
// Pair lookup is Week 3 — Router uses factory.getPair at tx build time
|
|
52
|
+
pairAddress: '0x0000000000000000000000000000000000000000',
|
|
53
|
+
quote,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const normalized = version === 1n ? 1 : 2;
|
|
58
|
+
tradingPath = {
|
|
59
|
+
path: 'bonding-curve',
|
|
60
|
+
router: tokenManager,
|
|
61
|
+
version: normalized,
|
|
62
|
+
quote,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return { tradingPath, rawInfo: info };
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.js","sourceRoot":"","sources":["../../src/lib/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA;AA+C5E,+DAA+D;AAC/D,aAAa;AACb,+DAA+D;AAE/D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAoB,EACpB,EAAW;IAEX,IAAI,IAAwB,CAAA;IAC5B,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC;YAC/B,OAAO,EAAE,qBAAqB;YAC9B,GAAG,EAAE,sBAAsB;YAC3B,YAAY,EAAE,cAAc;YAC5B,IAAI,EAAE,CAAC,EAAE,CAAC;SACX,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,sBAAsB,EAAE,yBACtB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAC9C,EAAE,CACH,CAAA;IACH,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IACvB,MAAM,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IACrB,MAAM,cAAc,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;IAE/B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CACb,sBAAsB,EAAE,qDAAqD,CAC9E,CAAA;IACH,CAAC;IAED,IAAI,WAAwB,CAAA;IAC5B,IAAI,cAAc,EAAE,CAAC;QACnB,WAAW,GAAG;YACZ,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,iBAAiB;YACzB,uEAAuE;YACvE,WAAW,EAAE,4CAA4C;YACzD,KAAK;SACN,CAAA;IACH,CAAC;SAAM,CAAC;QACN,MAAM,UAAU,GAAU,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAChD,WAAW,GAAG;YACZ,IAAI,EAAE,eAAe;YACrB,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,UAAU;YACnB,KAAK;SACN,CAAA;IACH,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AACvC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slippage calculation helpers for Router calls.
|
|
3
|
+
*
|
|
4
|
+
* Two paths:
|
|
5
|
+
* - Bonding curve: uses Helper3.tryBuy to estimate output
|
|
6
|
+
* - PancakeSwap (graduated): uses PancakeRouter.getAmountsOut
|
|
7
|
+
*
|
|
8
|
+
* Without proper minimums, passing 0n makes every tx fully sandwichable.
|
|
9
|
+
*/
|
|
10
|
+
import type { Address, PublicClient } from 'viem';
|
|
11
|
+
/**
|
|
12
|
+
* Compute slippage-protected minimums for volume (buy+sell round-trip).
|
|
13
|
+
*
|
|
14
|
+
* Tries bonding-curve estimation first (tryBuy). If that fails (token has
|
|
15
|
+
* graduated), falls back to PancakeSwap getAmountsOut. If both fail,
|
|
16
|
+
* returns 0n (no protection — last resort).
|
|
17
|
+
*/
|
|
18
|
+
export declare function computeVolumeSlippage(client: PublicClient, token: Address, bnbWei: bigint, slippageBps: number): Promise<{
|
|
19
|
+
minTokenOut: bigint;
|
|
20
|
+
minBnbBack: bigint;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Compute minTokenOut for a turnover (buy-only, no sell leg).
|
|
24
|
+
* Same dual-path logic as computeVolumeSlippage but only the buy side.
|
|
25
|
+
*/
|
|
26
|
+
export declare function computeTurnoverSlippage(client: PublicClient, token: Address, bnbWei: bigint, slippageBps: number): Promise<{
|
|
27
|
+
minTokenOut: bigint;
|
|
28
|
+
}>;
|
|
29
|
+
//# sourceMappingURL=slippage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slippage.d.ts","sourceRoot":"","sources":["../../src/lib/slippage.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAkBjD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA4CtD;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CA6BlC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slippage calculation helpers for Router calls.
|
|
3
|
+
*
|
|
4
|
+
* Two paths:
|
|
5
|
+
* - Bonding curve: uses Helper3.tryBuy to estimate output
|
|
6
|
+
* - PancakeSwap (graduated): uses PancakeRouter.getAmountsOut
|
|
7
|
+
*
|
|
8
|
+
* Without proper minimums, passing 0n makes every tx fully sandwichable.
|
|
9
|
+
*/
|
|
10
|
+
import { TOKEN_MANAGER_HELPER3, PANCAKE_V2_ROUTER, WBNB } from './const.js';
|
|
11
|
+
import { tokenManagerHelper3Abi } from '../contracts/tokenManagerHelper3.js';
|
|
12
|
+
// PancakeSwap getAmountsOut ABI (just what we need)
|
|
13
|
+
const pancakeGetAmountsOutAbi = [
|
|
14
|
+
{
|
|
15
|
+
type: 'function',
|
|
16
|
+
name: 'getAmountsOut',
|
|
17
|
+
stateMutability: 'view',
|
|
18
|
+
inputs: [
|
|
19
|
+
{ name: 'amountIn', type: 'uint256' },
|
|
20
|
+
{ name: 'path', type: 'address[]' },
|
|
21
|
+
],
|
|
22
|
+
outputs: [{ name: 'amounts', type: 'uint256[]' }],
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Compute slippage-protected minimums for volume (buy+sell round-trip).
|
|
27
|
+
*
|
|
28
|
+
* Tries bonding-curve estimation first (tryBuy). If that fails (token has
|
|
29
|
+
* graduated), falls back to PancakeSwap getAmountsOut. If both fail,
|
|
30
|
+
* returns 0n (no protection — last resort).
|
|
31
|
+
*/
|
|
32
|
+
export async function computeVolumeSlippage(client, token, bnbWei, slippageBps) {
|
|
33
|
+
const slipBig = BigInt(slippageBps);
|
|
34
|
+
// --- Try bonding curve first ---
|
|
35
|
+
let minTokenOut = 0n;
|
|
36
|
+
let usedPancake = false;
|
|
37
|
+
try {
|
|
38
|
+
const result = await client.readContract({
|
|
39
|
+
address: TOKEN_MANAGER_HELPER3,
|
|
40
|
+
abi: tokenManagerHelper3Abi,
|
|
41
|
+
functionName: 'tryBuy',
|
|
42
|
+
args: [token, 0n, bnbWei],
|
|
43
|
+
});
|
|
44
|
+
const estimatedTokens = result[2];
|
|
45
|
+
minTokenOut = (estimatedTokens * (10000n - slipBig)) / 10000n;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// tryBuy failed — token is likely graduated. Try PancakeSwap.
|
|
49
|
+
try {
|
|
50
|
+
const amounts = await client.readContract({
|
|
51
|
+
address: PANCAKE_V2_ROUTER,
|
|
52
|
+
abi: pancakeGetAmountsOutAbi,
|
|
53
|
+
functionName: 'getAmountsOut',
|
|
54
|
+
args: [bnbWei, [WBNB, token]],
|
|
55
|
+
});
|
|
56
|
+
if (amounts.length >= 2) {
|
|
57
|
+
const estimatedTokens = amounts[1];
|
|
58
|
+
minTokenOut = (estimatedTokens * (10000n - slipBig)) / 10000n;
|
|
59
|
+
usedPancake = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Both paths failed — throw so callers refuse to send an unprotected tx
|
|
64
|
+
throw new Error('Slippage estimation failed: neither bonding-curve tryBuy nor PancakeSwap getAmountsOut returned a quote. Refusing to send tx with 0 slippage protection.');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// For the sell leg: estimate how much BNB we'd get back after a round-trip.
|
|
68
|
+
// Bonding curve: ~2.5% round-trip fee. PancakeSwap: ~0.5% LP fee × 2 = 1%.
|
|
69
|
+
const estimatedFeeBps = usedPancake ? 150n : 250n;
|
|
70
|
+
const totalDiscountBps = slipBig + estimatedFeeBps;
|
|
71
|
+
const effectiveBps = totalDiscountBps > 10000n ? 10000n : totalDiscountBps;
|
|
72
|
+
const minBnbBack = (bnbWei * (10000n - effectiveBps)) / 10000n;
|
|
73
|
+
return { minTokenOut, minBnbBack };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compute minTokenOut for a turnover (buy-only, no sell leg).
|
|
77
|
+
* Same dual-path logic as computeVolumeSlippage but only the buy side.
|
|
78
|
+
*/
|
|
79
|
+
export async function computeTurnoverSlippage(client, token, bnbWei, slippageBps) {
|
|
80
|
+
const slipBig = BigInt(slippageBps);
|
|
81
|
+
// Try bonding curve
|
|
82
|
+
try {
|
|
83
|
+
const result = await client.readContract({
|
|
84
|
+
address: TOKEN_MANAGER_HELPER3,
|
|
85
|
+
abi: tokenManagerHelper3Abi,
|
|
86
|
+
functionName: 'tryBuy',
|
|
87
|
+
args: [token, 0n, bnbWei],
|
|
88
|
+
});
|
|
89
|
+
return { minTokenOut: (result[2] * (10000n - slipBig)) / 10000n };
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Fallback: PancakeSwap
|
|
93
|
+
try {
|
|
94
|
+
const amounts = await client.readContract({
|
|
95
|
+
address: PANCAKE_V2_ROUTER,
|
|
96
|
+
abi: pancakeGetAmountsOutAbi,
|
|
97
|
+
functionName: 'getAmountsOut',
|
|
98
|
+
args: [bnbWei, [WBNB, token]],
|
|
99
|
+
});
|
|
100
|
+
if (amounts.length >= 2) {
|
|
101
|
+
return { minTokenOut: (amounts[1] * (10000n - slipBig)) / 10000n };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// both failed
|
|
106
|
+
}
|
|
107
|
+
throw new Error('Slippage estimation failed: neither bonding-curve tryBuy nor PancakeSwap getAmountsOut returned a quote. Refusing to send tx with 0 slippage protection.');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=slippage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slippage.js","sourceRoot":"","sources":["../../src/lib/slippage.ts"],"names":[],"mappings":"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"}
|