@intentsolutionsio/token-launch-tracker 1.0.0 → 1.0.4
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 +19 -9
- package/agents/launch-tracker-agent.md +26 -3
- package/package.json +1 -1
- package/skills/skill-adapter/references/README.md +0 -1
- package/skills/skill-adapter/references/examples.md +6 -0
- package/skills/tracking-token-launches/ARD.md +9 -0
- package/skills/tracking-token-launches/PRD.md +3 -0
- package/skills/tracking-token-launches/SKILL.md +30 -6
- package/skills/tracking-token-launches/references/errors.md +38 -0
- package/skills/tracking-token-launches/references/examples.md +19 -0
- package/skills/tracking-token-launches/references/implementation.md +9 -1
- package/skills/tracking-token-launches/scripts/dex_sources.py +2 -0
- package/skills/tracking-token-launches/scripts/event_monitor.py +20 -35
- package/skills/tracking-token-launches/scripts/formatters.py +27 -39
- package/skills/tracking-token-launches/scripts/launch_tracker.py +49 -158
- package/skills/tracking-token-launches/scripts/token_analyzer.py +61 -62
|
@@ -3,31 +3,40 @@
|
|
|
3
3
|
## RPC Connection Errors
|
|
4
4
|
|
|
5
5
|
### Connection Timeout
|
|
6
|
+
|
|
6
7
|
```
|
|
7
8
|
Error: RPC connection timeout after 30s
|
|
8
9
|
```
|
|
10
|
+
|
|
9
11
|
**Cause:** RPC endpoint is overloaded or unreachable.
|
|
10
12
|
**Solution:**
|
|
13
|
+
|
|
11
14
|
1. Try a different RPC URL with `--rpc-url`
|
|
12
15
|
2. Use a backup RPC provider (Alchemy, Chainstack, Infura, or QuickNode)
|
|
13
16
|
3. Reduce request frequency
|
|
14
17
|
|
|
15
18
|
### RPC Rate Limited
|
|
19
|
+
|
|
16
20
|
```
|
|
17
21
|
Error: 429 Too Many Requests
|
|
18
22
|
```
|
|
23
|
+
|
|
19
24
|
**Cause:** Exceeded RPC provider rate limits.
|
|
20
25
|
**Solution:**
|
|
26
|
+
|
|
21
27
|
1. Add delay between requests
|
|
22
28
|
2. Upgrade to paid RPC tier
|
|
23
29
|
3. Use multiple RPC endpoints with round-robin
|
|
24
30
|
|
|
25
31
|
### Invalid Chain
|
|
32
|
+
|
|
26
33
|
```
|
|
27
34
|
Error: Unsupported chain: xyz
|
|
28
35
|
```
|
|
36
|
+
|
|
29
37
|
**Cause:** Chain not in supported list.
|
|
30
38
|
**Solution:**
|
|
39
|
+
|
|
31
40
|
1. Use `python launch_tracker.py chains` to see supported chains
|
|
32
41
|
2. Check spelling (lowercase required)
|
|
33
42
|
3. Add custom chain config if needed
|
|
@@ -35,21 +44,27 @@ Error: Unsupported chain: xyz
|
|
|
35
44
|
## Event Parsing Errors
|
|
36
45
|
|
|
37
46
|
### No Pairs Found
|
|
47
|
+
|
|
38
48
|
```
|
|
39
49
|
No new pairs found in the specified timeframe.
|
|
40
50
|
```
|
|
51
|
+
|
|
41
52
|
**Cause:** No PairCreated events in time window.
|
|
42
53
|
**Solution:**
|
|
54
|
+
|
|
43
55
|
1. Extend the time window with `--hours`
|
|
44
56
|
2. Check if correct chain is selected
|
|
45
57
|
3. Verify DEX is active on that chain
|
|
46
58
|
|
|
47
59
|
### Log Parsing Failed
|
|
60
|
+
|
|
48
61
|
```
|
|
49
62
|
Error parsing log: list index out of range
|
|
50
63
|
```
|
|
64
|
+
|
|
51
65
|
**Cause:** Unexpected event format (V3 vs V2).
|
|
52
66
|
**Solution:**
|
|
67
|
+
|
|
53
68
|
1. Check DEX version compatibility
|
|
54
69
|
2. Verify factory address is correct
|
|
55
70
|
3. Enable `--verbose` for debugging
|
|
@@ -57,31 +72,40 @@ Error parsing log: list index out of range
|
|
|
57
72
|
## Contract Analysis Errors
|
|
58
73
|
|
|
59
74
|
### Bytecode Not Found
|
|
75
|
+
|
|
60
76
|
```
|
|
61
77
|
Error: Contract has no bytecode
|
|
62
78
|
```
|
|
79
|
+
|
|
63
80
|
**Cause:** Address is not a contract (EOA) or wrong network.
|
|
64
81
|
**Solution:**
|
|
82
|
+
|
|
65
83
|
1. Verify address is a contract
|
|
66
84
|
2. Check correct chain is selected
|
|
67
85
|
3. Confirm address checksum
|
|
68
86
|
|
|
69
87
|
### ABI Decoding Failed
|
|
88
|
+
|
|
70
89
|
```
|
|
71
90
|
Error decoding string: Unknown
|
|
72
91
|
```
|
|
92
|
+
|
|
73
93
|
**Cause:** Non-standard ERC20 implementation.
|
|
74
94
|
**Solution:**
|
|
95
|
+
|
|
75
96
|
1. Use `--verbose` to see raw data
|
|
76
97
|
2. Token may use bytes32 for name/symbol
|
|
77
98
|
3. Some tokens have non-standard decimals
|
|
78
99
|
|
|
79
100
|
### Verification Check Failed
|
|
101
|
+
|
|
80
102
|
```
|
|
81
103
|
Verification check error: API key invalid
|
|
82
104
|
```
|
|
105
|
+
|
|
83
106
|
**Cause:** Etherscan API key issues.
|
|
84
107
|
**Solution:**
|
|
108
|
+
|
|
85
109
|
1. Set `ETHERSCAN_API_KEY` environment variable
|
|
86
110
|
2. Use `--etherscan-key` flag
|
|
87
111
|
3. Check API key is valid for that chain
|
|
@@ -89,21 +113,27 @@ Verification check error: API key invalid
|
|
|
89
113
|
## Token Analysis Errors
|
|
90
114
|
|
|
91
115
|
### Risk Analysis Incomplete
|
|
116
|
+
|
|
92
117
|
```
|
|
93
118
|
Warning: Some risk indicators could not be checked
|
|
94
119
|
```
|
|
120
|
+
|
|
95
121
|
**Cause:** Limited bytecode or API access.
|
|
96
122
|
**Solution:**
|
|
123
|
+
|
|
97
124
|
1. Check if contract is verified
|
|
98
125
|
2. Provide Etherscan API key
|
|
99
126
|
3. Some indicators require source code
|
|
100
127
|
|
|
101
128
|
### Proxy Detection Failed
|
|
129
|
+
|
|
102
130
|
```
|
|
103
131
|
Warning: Could not determine if contract is proxy
|
|
104
132
|
```
|
|
133
|
+
|
|
105
134
|
**Cause:** Storage slot access issue.
|
|
106
135
|
**Solution:**
|
|
136
|
+
|
|
107
137
|
1. Some RPCs don't support eth_getStorageAt
|
|
108
138
|
2. Use a full node RPC
|
|
109
139
|
3. Proxy detection is best-effort
|
|
@@ -111,21 +141,27 @@ Warning: Could not determine if contract is proxy
|
|
|
111
141
|
## Environment Errors
|
|
112
142
|
|
|
113
143
|
### Missing Dependencies
|
|
144
|
+
|
|
114
145
|
```
|
|
115
146
|
ImportError: requests library required
|
|
116
147
|
```
|
|
148
|
+
|
|
117
149
|
**Cause:** Python requests not installed.
|
|
118
150
|
**Solution:**
|
|
151
|
+
|
|
119
152
|
```bash
|
|
120
153
|
pip install requests
|
|
121
154
|
```
|
|
122
155
|
|
|
123
156
|
### Environment Variable Missing
|
|
157
|
+
|
|
124
158
|
```
|
|
125
159
|
Error: No RPC URL configured
|
|
126
160
|
```
|
|
161
|
+
|
|
127
162
|
**Cause:** Chain RPC URL not found.
|
|
128
163
|
**Solution:**
|
|
164
|
+
|
|
129
165
|
1. Set `{CHAIN}_RPC_URL` environment variable
|
|
130
166
|
2. Use `--rpc-url` flag
|
|
131
167
|
3. Check config/settings.yaml
|
|
@@ -153,11 +189,13 @@ The system uses this fallback chain for RPC failures:
|
|
|
153
189
|
## Debug Mode
|
|
154
190
|
|
|
155
191
|
Enable verbose output for troubleshooting:
|
|
192
|
+
|
|
156
193
|
```bash
|
|
157
194
|
python launch_tracker.py --verbose recent --chain ethereum
|
|
158
195
|
```
|
|
159
196
|
|
|
160
197
|
This shows:
|
|
198
|
+
|
|
161
199
|
- RPC requests being made
|
|
162
200
|
- Raw response data
|
|
163
201
|
- Parsing steps
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
## Recent Token Launches
|
|
4
4
|
|
|
5
5
|
### Basic Usage
|
|
6
|
+
|
|
6
7
|
```bash
|
|
7
8
|
# Show launches from last 24 hours on Ethereum
|
|
8
9
|
python launch_tracker.py recent --chain ethereum
|
|
@@ -20,6 +21,7 @@ python launch_tracker.py recent --chain ethereum
|
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
### With Risk Analysis
|
|
24
|
+
|
|
23
25
|
```bash
|
|
24
26
|
# Include token and contract analysis
|
|
25
27
|
python launch_tracker.py recent --chain base --analyze --hours 12
|
|
@@ -28,12 +30,14 @@ python launch_tracker.py recent --chain base --analyze --hours 12
|
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
### Filter by DEX
|
|
33
|
+
|
|
31
34
|
```bash
|
|
32
35
|
# Only show Uniswap V2 launches
|
|
33
36
|
python launch_tracker.py recent --chain ethereum --dex "Uniswap V2"
|
|
34
37
|
```
|
|
35
38
|
|
|
36
39
|
### JSON Output
|
|
40
|
+
|
|
37
41
|
```bash
|
|
38
42
|
# Get JSON for programmatic use
|
|
39
43
|
python launch_tracker.py -f json recent --chain bsc --hours 6 --limit 10
|
|
@@ -42,6 +46,7 @@ python launch_tracker.py -f json recent --chain bsc --hours 6 --limit 10
|
|
|
42
46
|
## Token Details
|
|
43
47
|
|
|
44
48
|
### Get Full Token Information
|
|
49
|
+
|
|
45
50
|
```bash
|
|
46
51
|
python launch_tracker.py detail \
|
|
47
52
|
--address 0x6982508145454ce325ddbe47a25d4ec3d2311933 \
|
|
@@ -90,6 +95,7 @@ python launch_tracker.py detail \
|
|
|
90
95
|
## Risk Analysis
|
|
91
96
|
|
|
92
97
|
### Analyze Token Contract
|
|
98
|
+
|
|
93
99
|
```bash
|
|
94
100
|
python launch_tracker.py risk \
|
|
95
101
|
--address 0x1234567890abcdef1234567890abcdef12345678 \
|
|
@@ -125,6 +131,7 @@ python launch_tracker.py risk \
|
|
|
125
131
|
```
|
|
126
132
|
|
|
127
133
|
### With Etherscan Verification
|
|
134
|
+
|
|
128
135
|
```bash
|
|
129
136
|
# Include contract verification check
|
|
130
137
|
python launch_tracker.py risk \
|
|
@@ -136,6 +143,7 @@ python launch_tracker.py risk \
|
|
|
136
143
|
## Launch Summary
|
|
137
144
|
|
|
138
145
|
### Cross-Chain Summary
|
|
146
|
+
|
|
139
147
|
```bash
|
|
140
148
|
python launch_tracker.py summary --hours 24
|
|
141
149
|
|
|
@@ -162,6 +170,7 @@ python launch_tracker.py summary --hours 24
|
|
|
162
170
|
```
|
|
163
171
|
|
|
164
172
|
### Specific Chains Only
|
|
173
|
+
|
|
165
174
|
```bash
|
|
166
175
|
# Only Ethereum and Base
|
|
167
176
|
python launch_tracker.py summary --chains ethereum,base --hours 12
|
|
@@ -170,6 +179,7 @@ python launch_tracker.py summary --chains ethereum,base --hours 12
|
|
|
170
179
|
## List DEXes and Chains
|
|
171
180
|
|
|
172
181
|
### Show Supported Chains
|
|
182
|
+
|
|
173
183
|
```bash
|
|
174
184
|
python launch_tracker.py chains
|
|
175
185
|
|
|
@@ -187,6 +197,7 @@ python launch_tracker.py chains
|
|
|
187
197
|
```
|
|
188
198
|
|
|
189
199
|
### Show DEXes for Chain
|
|
200
|
+
|
|
190
201
|
```bash
|
|
191
202
|
python launch_tracker.py dexes --chain bsc
|
|
192
203
|
|
|
@@ -201,6 +212,7 @@ python launch_tracker.py dexes --chain bsc
|
|
|
201
212
|
```
|
|
202
213
|
|
|
203
214
|
### All DEXes (JSON)
|
|
215
|
+
|
|
204
216
|
```bash
|
|
205
217
|
python launch_tracker.py -f json dexes
|
|
206
218
|
```
|
|
@@ -208,6 +220,7 @@ python launch_tracker.py -f json dexes
|
|
|
208
220
|
## Custom RPC Usage
|
|
209
221
|
|
|
210
222
|
### Use Custom RPC Endpoint
|
|
223
|
+
|
|
211
224
|
```bash
|
|
212
225
|
python launch_tracker.py recent \
|
|
213
226
|
--chain ethereum \
|
|
@@ -215,6 +228,7 @@ python launch_tracker.py recent \
|
|
|
215
228
|
```
|
|
216
229
|
|
|
217
230
|
### Via Environment Variable
|
|
231
|
+
|
|
218
232
|
```bash
|
|
219
233
|
export ETHEREUM_RPC_URL=https://mainnet.infura.io/v3/YOUR_PROJECT_ID
|
|
220
234
|
python launch_tracker.py recent --chain ethereum
|
|
@@ -223,6 +237,7 @@ python launch_tracker.py recent --chain ethereum
|
|
|
223
237
|
## Verbose Output
|
|
224
238
|
|
|
225
239
|
### Debug Mode
|
|
240
|
+
|
|
226
241
|
```bash
|
|
227
242
|
python launch_tracker.py --verbose recent --chain base --hours 1
|
|
228
243
|
|
|
@@ -238,6 +253,7 @@ python launch_tracker.py --verbose recent --chain base --hours 1
|
|
|
238
253
|
## Common Workflows
|
|
239
254
|
|
|
240
255
|
### Find High-Risk New Tokens
|
|
256
|
+
|
|
241
257
|
```bash
|
|
242
258
|
# Get recent launches with analysis, output as JSON
|
|
243
259
|
python launch_tracker.py -f json recent \
|
|
@@ -251,6 +267,7 @@ jq '.[] | select(.analysis.risk_score >= 70)' launches.json
|
|
|
251
267
|
```
|
|
252
268
|
|
|
253
269
|
### Monitor Multiple Chains
|
|
270
|
+
|
|
254
271
|
```bash
|
|
255
272
|
#!/bin/bash
|
|
256
273
|
for chain in ethereum bsc base arbitrum; do
|
|
@@ -260,6 +277,7 @@ done
|
|
|
260
277
|
```
|
|
261
278
|
|
|
262
279
|
### Export to CSV (via jq)
|
|
280
|
+
|
|
263
281
|
```bash
|
|
264
282
|
python launch_tracker.py -f json recent --chain ethereum --hours 24 | \
|
|
265
283
|
jq -r '.[] | [.pair.timestamp, .pair.dex, .token_info.symbol // "???", .analysis.risk_score // 0] | @csv'
|
|
@@ -268,6 +286,7 @@ python launch_tracker.py -f json recent --chain ethereum --hours 24 | \
|
|
|
268
286
|
## Integration Examples
|
|
269
287
|
|
|
270
288
|
### Python Import
|
|
289
|
+
|
|
271
290
|
```python
|
|
272
291
|
from event_monitor import EventMonitor
|
|
273
292
|
from token_analyzer import TokenAnalyzer
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
## Implementation Guide
|
|
2
2
|
|
|
3
3
|
### Step 1: Configure Data Sources
|
|
4
|
+
|
|
4
5
|
Set up connections to crypto data providers:
|
|
6
|
+
|
|
5
7
|
1. Use Read tool to load API credentials from ${CLAUDE_SKILL_DIR}/config/crypto-apis.env
|
|
6
8
|
2. Configure blockchain RPC endpoints for target networks
|
|
7
9
|
3. Set up exchange API connections if required
|
|
@@ -9,7 +11,9 @@ Set up connections to crypto data providers:
|
|
|
9
11
|
5. Test connectivity and authentication
|
|
10
12
|
|
|
11
13
|
### Step 2: Query Crypto Data
|
|
14
|
+
|
|
12
15
|
Retrieve relevant blockchain and market data:
|
|
16
|
+
|
|
13
17
|
1. Use Bash(crypto:launch-*) to execute crypto data queries
|
|
14
18
|
2. Fetch real-time prices, volumes, and market cap data
|
|
15
19
|
3. Query blockchain for on-chain metrics and transactions
|
|
@@ -17,7 +21,9 @@ Retrieve relevant blockchain and market data:
|
|
|
17
21
|
5. Aggregate data from multiple sources for accuracy
|
|
18
22
|
|
|
19
23
|
### Step 3: Analyze and Process
|
|
24
|
+
|
|
20
25
|
Process crypto data to generate insights:
|
|
26
|
+
|
|
21
27
|
- Calculate key metrics (returns, volatility, correlation)
|
|
22
28
|
- Identify patterns and anomalies in data
|
|
23
29
|
- Apply technical indicators or on-chain signals
|
|
@@ -25,7 +31,9 @@ Process crypto data to generate insights:
|
|
|
25
31
|
- Generate actionable insights and alerts
|
|
26
32
|
|
|
27
33
|
### Step 4: Generate Reports
|
|
34
|
+
|
|
28
35
|
Document findings in ${CLAUDE_SKILL_DIR}/crypto-reports/:
|
|
36
|
+
|
|
29
37
|
- Market summary with key price movements
|
|
30
38
|
- Detailed analysis with charts and metrics
|
|
31
39
|
- Trading signals or opportunity recommendations
|
|
@@ -19,6 +19,7 @@ PAIR_CREATED_TOPIC = "0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31a
|
|
|
19
19
|
@dataclass
|
|
20
20
|
class DexFactory:
|
|
21
21
|
"""DEX factory configuration."""
|
|
22
|
+
|
|
22
23
|
name: str
|
|
23
24
|
address: str
|
|
24
25
|
version: str # v2 or v3
|
|
@@ -28,6 +29,7 @@ class DexFactory:
|
|
|
28
29
|
@dataclass
|
|
29
30
|
class ChainConfig:
|
|
30
31
|
"""Chain configuration."""
|
|
32
|
+
|
|
31
33
|
name: str
|
|
32
34
|
chain_id: int
|
|
33
35
|
rpc_url: str
|
|
@@ -31,6 +31,7 @@ from dex_sources import (
|
|
|
31
31
|
@dataclass
|
|
32
32
|
class PairCreated:
|
|
33
33
|
"""New trading pair event."""
|
|
34
|
+
|
|
34
35
|
block_number: int
|
|
35
36
|
tx_hash: str
|
|
36
37
|
timestamp: int
|
|
@@ -45,6 +46,7 @@ class PairCreated:
|
|
|
45
46
|
@dataclass
|
|
46
47
|
class TokenBasicInfo:
|
|
47
48
|
"""Basic token info from RPC."""
|
|
49
|
+
|
|
48
50
|
address: str
|
|
49
51
|
name: str
|
|
50
52
|
symbol: str
|
|
@@ -54,12 +56,7 @@ class TokenBasicInfo:
|
|
|
54
56
|
class EventMonitor:
|
|
55
57
|
"""Monitor blockchain events for new pairs."""
|
|
56
58
|
|
|
57
|
-
def __init__(
|
|
58
|
-
self,
|
|
59
|
-
chain: str = "ethereum",
|
|
60
|
-
rpc_url: str = None,
|
|
61
|
-
verbose: bool = False
|
|
62
|
-
):
|
|
59
|
+
def __init__(self, chain: str = "ethereum", rpc_url: str = None, verbose: bool = False):
|
|
63
60
|
"""Initialize event monitor.
|
|
64
61
|
|
|
65
62
|
Args:
|
|
@@ -69,10 +66,7 @@ class EventMonitor:
|
|
|
69
66
|
"""
|
|
70
67
|
self.chain = chain.lower()
|
|
71
68
|
self.config = get_chain_config(chain)
|
|
72
|
-
self.rpc_url = rpc_url or os.environ.get(
|
|
73
|
-
f"{chain.upper()}_RPC_URL",
|
|
74
|
-
self.config.rpc_url
|
|
75
|
-
)
|
|
69
|
+
self.rpc_url = rpc_url or os.environ.get(f"{chain.upper()}_RPC_URL", self.config.rpc_url)
|
|
76
70
|
self.verbose = verbose
|
|
77
71
|
# Use a size-limited cache to prevent memory leaks on long-running instances
|
|
78
72
|
# Keeps most recent 1000 block timestamps
|
|
@@ -111,10 +105,7 @@ class EventMonitor:
|
|
|
111
105
|
if block_number in self._block_cache:
|
|
112
106
|
return self._block_cache[block_number]
|
|
113
107
|
|
|
114
|
-
block = self._rpc_call(
|
|
115
|
-
"eth_getBlockByNumber",
|
|
116
|
-
[hex(block_number), False]
|
|
117
|
-
)
|
|
108
|
+
block = self._rpc_call("eth_getBlockByNumber", [hex(block_number), False])
|
|
118
109
|
|
|
119
110
|
if block:
|
|
120
111
|
timestamp = int(block.get("timestamp", "0x0"), 16)
|
|
@@ -136,11 +127,7 @@ class EventMonitor:
|
|
|
136
127
|
result = self._rpc_call("eth_blockNumber")
|
|
137
128
|
return int(result, 16)
|
|
138
129
|
|
|
139
|
-
def get_recent_pairs(
|
|
140
|
-
self,
|
|
141
|
-
hours: int = 24,
|
|
142
|
-
dex: str = None
|
|
143
|
-
) -> List[PairCreated]:
|
|
130
|
+
def get_recent_pairs(self, hours: int = 24, dex: str = None) -> List[PairCreated]:
|
|
144
131
|
"""Get recently created pairs.
|
|
145
132
|
|
|
146
133
|
Args:
|
|
@@ -168,12 +155,17 @@ class EventMonitor:
|
|
|
168
155
|
|
|
169
156
|
for factory_addr in factory_addresses:
|
|
170
157
|
try:
|
|
171
|
-
logs = self._rpc_call(
|
|
172
|
-
"
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
158
|
+
logs = self._rpc_call(
|
|
159
|
+
"eth_getLogs",
|
|
160
|
+
[
|
|
161
|
+
{
|
|
162
|
+
"fromBlock": hex(from_block),
|
|
163
|
+
"toBlock": "latest",
|
|
164
|
+
"address": factory_addr,
|
|
165
|
+
"topics": [PAIR_CREATED_TOPIC],
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
)
|
|
177
169
|
|
|
178
170
|
for log in logs or []:
|
|
179
171
|
pair = self._parse_pair_created(log, factory_addr)
|
|
@@ -189,11 +181,7 @@ class EventMonitor:
|
|
|
189
181
|
|
|
190
182
|
return pairs
|
|
191
183
|
|
|
192
|
-
def _parse_pair_created(
|
|
193
|
-
self,
|
|
194
|
-
log: Dict,
|
|
195
|
-
factory_address: str
|
|
196
|
-
) -> Optional[PairCreated]:
|
|
184
|
+
def _parse_pair_created(self, log: Dict, factory_address: str) -> Optional[PairCreated]:
|
|
197
185
|
"""Parse PairCreated event log."""
|
|
198
186
|
try:
|
|
199
187
|
block_number = int(log["blockNumber"], 16)
|
|
@@ -265,10 +253,7 @@ class EventMonitor:
|
|
|
265
253
|
def _call_contract(self, address: str, data: str) -> Optional[str]:
|
|
266
254
|
"""Make eth_call to contract."""
|
|
267
255
|
try:
|
|
268
|
-
result = self._rpc_call("eth_call", [
|
|
269
|
-
{"to": address, "data": data},
|
|
270
|
-
"latest"
|
|
271
|
-
])
|
|
256
|
+
result = self._rpc_call("eth_call", [{"to": address, "data": data}, "latest"])
|
|
272
257
|
return result if result and result != "0x" else None
|
|
273
258
|
except Exception:
|
|
274
259
|
return None
|
|
@@ -289,7 +274,7 @@ class EventMonitor:
|
|
|
289
274
|
# ABI encoded string: offset (32 bytes) + length (32 bytes) + data
|
|
290
275
|
if len(data) >= 128:
|
|
291
276
|
length = int(data[64:128], 16)
|
|
292
|
-
string_data = data[128:128 + length * 2]
|
|
277
|
+
string_data = data[128 : 128 + length * 2]
|
|
293
278
|
return bytes.fromhex(string_data).decode("utf-8", errors="ignore")
|
|
294
279
|
|
|
295
280
|
return bytes.fromhex(data).decode("utf-8", errors="ignore").strip("\x00")
|
|
@@ -58,7 +58,7 @@ def format_risk_badge(score: int) -> str:
|
|
|
58
58
|
|
|
59
59
|
def format_supply(supply: int, decimals: int) -> str:
|
|
60
60
|
"""Format token supply."""
|
|
61
|
-
value = supply / (10
|
|
61
|
+
value = supply / (10**decimals)
|
|
62
62
|
|
|
63
63
|
if value >= 1e12:
|
|
64
64
|
return f"{value / 1e12:.2f}T"
|
|
@@ -72,11 +72,7 @@ def format_supply(supply: int, decimals: int) -> str:
|
|
|
72
72
|
return f"{value:.2f}"
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def format_new_pairs_table(
|
|
76
|
-
pairs: List[Any],
|
|
77
|
-
token_infos: Dict[str, Any],
|
|
78
|
-
analyses: Dict[str, Any]
|
|
79
|
-
) -> str:
|
|
75
|
+
def format_new_pairs_table(pairs: List[Any], token_infos: Dict[str, Any], analyses: Dict[str, Any]) -> str:
|
|
80
76
|
"""Format new pairs as table.
|
|
81
77
|
|
|
82
78
|
Args:
|
|
@@ -110,10 +106,7 @@ def format_new_pairs_table(
|
|
|
110
106
|
pair_str = format_address(pair.pair_address, 8)
|
|
111
107
|
time_str = format_age(pair.timestamp)
|
|
112
108
|
|
|
113
|
-
lines.append(
|
|
114
|
-
f"{time_str:<12} {token_str:<20} {pair.dex:<15} "
|
|
115
|
-
f"{pair.chain:<10} {risk_str:<15} {pair_str:<18}"
|
|
116
|
-
)
|
|
109
|
+
lines.append(f"{time_str:<12} {token_str:<20} {pair.dex:<15} {pair.chain:<10} {risk_str:<15} {pair_str:<18}")
|
|
117
110
|
|
|
118
111
|
lines.append("=" * 90)
|
|
119
112
|
lines.append(f"Total: {len(pairs)} new pairs")
|
|
@@ -121,12 +114,7 @@ def format_new_pairs_table(
|
|
|
121
114
|
return "\n".join(lines)
|
|
122
115
|
|
|
123
116
|
|
|
124
|
-
def format_launch_detail(
|
|
125
|
-
pair: Any,
|
|
126
|
-
token_info: Any,
|
|
127
|
-
analysis: Any,
|
|
128
|
-
chain_config: Any
|
|
129
|
-
) -> str:
|
|
117
|
+
def format_launch_detail(pair: Any, token_info: Any, analysis: Any, chain_config: Any) -> str:
|
|
130
118
|
"""Format detailed launch info.
|
|
131
119
|
|
|
132
120
|
Args:
|
|
@@ -158,26 +146,30 @@ def format_launch_detail(
|
|
|
158
146
|
]
|
|
159
147
|
|
|
160
148
|
if token_info:
|
|
161
|
-
lines.extend(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
149
|
+
lines.extend(
|
|
150
|
+
[
|
|
151
|
+
"TOKEN INFO",
|
|
152
|
+
"-" * 60,
|
|
153
|
+
f"Decimals: {token_info.decimals}",
|
|
154
|
+
f"Total Supply: {format_supply(token_info.total_supply, token_info.decimals)}",
|
|
155
|
+
f"Owner: {format_address(token_info.owner) if token_info.owner else 'None'}",
|
|
156
|
+
f"Verified: {'Yes' if token_info.is_verified else 'No'}",
|
|
157
|
+
"",
|
|
158
|
+
]
|
|
159
|
+
)
|
|
170
160
|
|
|
171
161
|
if analysis:
|
|
172
|
-
lines.extend(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
162
|
+
lines.extend(
|
|
163
|
+
[
|
|
164
|
+
"RISK ANALYSIS",
|
|
165
|
+
"-" * 60,
|
|
166
|
+
f"Risk Score: {analysis.risk_score}/100 {format_risk_badge(analysis.risk_score)}",
|
|
167
|
+
f"Is Proxy: {'Yes' if analysis.is_proxy else 'No'}",
|
|
168
|
+
f"Ownership: {'Renounced' if analysis.ownership_renounced else 'Active'}",
|
|
169
|
+
"",
|
|
170
|
+
"Indicators:",
|
|
171
|
+
]
|
|
172
|
+
)
|
|
181
173
|
|
|
182
174
|
for ind in analysis.indicators:
|
|
183
175
|
severity_marker = {
|
|
@@ -254,11 +246,7 @@ def format_json(data: Any) -> str:
|
|
|
254
246
|
if hasattr(data, "__dict__"):
|
|
255
247
|
return json.dumps(vars(data), indent=2, default=str)
|
|
256
248
|
elif isinstance(data, list):
|
|
257
|
-
return json.dumps(
|
|
258
|
-
[vars(x) if hasattr(x, "__dict__") else x for x in data],
|
|
259
|
-
indent=2,
|
|
260
|
-
default=str
|
|
261
|
-
)
|
|
249
|
+
return json.dumps([vars(x) if hasattr(x, "__dict__") else x for x in data], indent=2, default=str)
|
|
262
250
|
else:
|
|
263
251
|
return json.dumps(data, indent=2, default=str)
|
|
264
252
|
|