repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | --- |
| #2 | name: dex-screener-scanner |
| #3 | description: "Automate DexScreener Solana token discovery and screening via browser automation. Navigate dexscreener.com/solana, scrape real-time token listings, filter by volume/liquidity/age/holders, and identify the best opportunities. Triggers: scan dexscreener, find new tokens, find trending tokens, screen Solana tokens, best tokens on Solana, dexscreener scanner." |
| #4 | --- |
| #5 | |
| #6 | # DexScreener Solana Scanner |
| #7 | |
| #8 | Autonomous agent skill for scanning and screening Solana tokens on DexScreener using browser automation (Playwright/Puppeteer). The goal is to navigate `dexscreener.com/solana`, extract token data from live listings, apply screening filters, and identify the best trading opportunities. |
| #9 | |
| #10 | ## Overview |
| #11 | |
| #12 | This skill uses browser automation to: |
| #13 | |
| #14 | 1. Navigate to DexScreener's Solana board |
| #15 | 2. Scrape live token listings (new, trending, gainers, etc.) |
| #16 | 3. Extract key metrics for each token |
| #17 | 4. Apply configurable screening filters to find the best opportunities |
| #18 | 5. Return ranked results with rationale |
| #19 | |
| #20 | ## Workflow |
| #21 | |
| #22 | ### Step 1: Launch Browser & Navigate |
| #23 | |
| #24 | Use a browser automation tool (Playwright or Puppeteer) to open `https://dexscreener.com/solana`. |
| #25 | |
| #26 | ```python |
| #27 | # Example with Playwright |
| #28 | page = await browser.new_page() |
| #29 | await page.goto("https://dexscreener.com/solana", wait_until="domcontentloaded") |
| #30 | ``` |
| #31 | |
| #32 | **Important:** DexScreener is a heavy React SPA. Wait for the token table rows to render before scraping. The table typically appears within 3-8 seconds. |
| #33 | |
| #34 | ### Step 2: Wait for Token Table to Load |
| #35 | |
| #36 | The token listing is a dynamic table. Watch for: |
| #37 | |
| #38 | - CSS selector for table rows: `div[data-group="token-row"]` or `.ds-table-row` or `tr[data-id]` |
| #39 | - The table has multiple tabs: **New**, **Trending**, **Gainers**, **Recently Copied**, etc. |
| #40 | - Wait for at least one row to be visible before scraping |
| #41 | |
| #42 | Recommended wait strategy: |
| #43 | |
| #44 | ```python |
| #45 | # Wait for first token row to appear |
| #46 | await page.wait_for_selector('[data-group="token-row"]', timeout=15000) |
| #47 | # Or wait for the table body |
| #48 | await page.wait_for_selector('table tbody tr', timeout=15000) |
| #49 | ``` |
| #50 | |
| #51 | If the wait times out, the page may be loading slowly — retry once with a longer timeout. |
| #52 | |
| #53 | ### Step 3: Parse Token Listing Data |
| #54 | |
| #55 | Each token row typically contains these fields (column order may vary slightly): |
| #56 | |
| #57 | | Field | Description | Example | |
| #58 | |-------|-------------|---------| |
| #59 | | **#** | Rank/position | 1, 2, 3 | |
| #60 | | **Name/Symbol** | Token name + ticker | "dogwifhat / WIF" | |
| #61 | | **Price** | Current SOL or USD price | "$0.0042" | |
| #62 | | **Price Change** | 5m/1h/6h/24h change % | "+15.3%" | |
| #63 | | **Volume** | 24h trading volume | "$1.2M" | |
| #64 | | **Liquidity** | Pool liquidity | "$45K" | |
| #65 | | **Market Cap** | Fully diluted market cap | "$2.1M" | |
| #66 | | **Age** | How long since creation | "3m" (3 minutes), "1h" | |
| #67 | | **Txns** | Transaction count (buys/sells) | "1.2K / 800" | |
| #68 | | **Holders** | Unique holder count | "450" | |
| #69 | | **FDV / Liq** | FDV-to-Liquidity ratio | "12.5x" | |
| #70 | |
| #71 | **Parsing approach:** |
| #72 | |
| #73 | ```python |
| #74 | rows = await page.query_selector_all('[data-group="token-row"]') |
| #75 | tokens = [] |
| #76 | for row in rows: |
| #77 | cells = await row.query_selector_all('td') |
| #78 | # Extract based on cell index |
| #79 | tokens.append({ |
| #80 | "rank": await cells[0].inner_text(), |
| #81 | "name": await cells[1].inner_text(), |
| #82 | "price": await cells[2].inner_text(), |
| #83 | "change_5m": await cells[3].inner_text(), |
| #84 | "volume": await cells[4].inner_text(), |
| #85 | "liquidity": await cells[5].inner_text(), |
| #86 | "market_cap": await cells[6].inner_text(), |
| #87 | "age": await cells[7].inner_text(), |
| #88 | "txns": await cells[8].inner_text(), |
| #89 | "holders": await cells[9].inner_text(), |
| #90 | # ... etc |
| #91 | }) |
| #92 | ``` |
| #93 | |
| #94 | **Note:** Cell indices may shift if DexScreener updates their layout. Always inspect the actual DOM first. Use `page.evaluate()` for more robust scraping if query selectors are unreliable. |
| #95 | |
| #96 | ### Step 4: Switch Between Listing Tabs |
| #97 | |
| #98 | DexScreener has multiple tabs that reveal different token sets. Click these to scan more broadly: |
| #99 | |
| #100 | ```python |
| #101 | # Available tabs (selectors may vary): |
| #102 | # New: Most recently created pairs |
| #103 | await page.click('button:has-text("New")') |
| #104 | # Trending: Hottest tokens right now |
| #105 | await page.click('button:has-text("Trending")') |
| #106 | # Gainers: Top gainers |
| #107 | await page.click('button:has-text("Gainers")') |
| #108 | # Recently Copied: Tokens being copied from other chains |
| #109 | await page.click('button:has-text("Recently Copied")') |
| #110 | ``` |
| #111 | |
| #112 | Wait 2-3 seconds after clicking a tab for the table to re-render. |
| #113 | |
| #114 | ### Step 5: Scroll to Load More Tokens |
| #115 | |
| #116 | DexScreener loads tokens in batches (about 25 per page). Scroll down to trigger lazy loading: |
| #117 | |
| #118 | ```python |
| #119 | # Scroll to bottom of table to load more |
| #120 | for _ in range(3): |
| #121 | await page.evaluate('window.scrollBy(0, 800)') |
| #122 | await page.wait_for_timeout(1500) |
| #123 | ``` |
| #124 | |
| #125 | **Be respectful:** Don't hammer the page. 2-3 scrolls is enough to get a meaningful sample (~75-100 tokens per tab). |
| #126 | |
| #127 | ### Step 6: Apply Screening Filters |
| #128 | |
| #129 | After collecting raw token data, apply filters to find the "best" tokens. These are the recommended default thresholds: |
| #130 | |
| #131 | | Criterion | Recommended Threshold | Why | |
| #132 | |-----------|----------------------|-----| |
| #133 | | **Min Liquidity** | ≥ $10,000 | Below this = high slippage, rug risk | |
| #134 | | **Min Volume** | ≥ $50,000 (24h) | Shows organic interest | |
| #135 | | **Max Age** | ≤ 48 hours | Catches new launches | |
| #136 | | **Min Holders** | ≥ 50 unique | Indicates distribution, not a single dev wallet | |
| #137 | | **Max Holder Concentration** | Top 10 holders < 20% | Prevents whale manipulation | |
| #138 | | **Min Price Change (5m)** | ≥ 5% (for momentum) | Shows buying pressure | |
| #139 | | **FDV / Liquidity Ratio** | < 50x | Lower = less overvalued relative to available liquidity | |
| #140 | | **Min Txns** | ≥ 100 transactions | Shows real activity | |
| #141 | |
| #142 | **Screening function example:** |
| #143 | |
| #144 | ```python |
| #145 | def screen_tokens(tokens, config=None): |
| #146 | defaults = { |
| #147 | "min_liquidity": 10_000, |
| #148 | "min_volume": 50_000, |
| #149 | "max_age_hours": 48, |
| #150 | "min_holders": 50, |
| #151 | "min_txns": 100, |
| #152 | } |
| #153 | config = {**defaults, **(config or {})} |
| #154 | |
| #155 | passed = [] |
| #156 | for t in tokens: |
| #157 | reasons = [] |
| #158 | if t.get("liquidity_usd", 0) >= config["min_liquidity"]: |
| #159 | reasons.append(f"liquidity={t['liquidity_usd']}") |
| #160 | if t.get("volume_24h", 0) >= config["min_volume"]: |
| #161 | reasons.append(f"volume={t['volume_24h']}") |
| #162 | if t.get("age_hours", 999) <= config["max_age_hours"]: |
| #163 | reasons.append(f"age={t['age_hours']}h") |
| #164 | if t.get("holders", 0) >= config["min_holders"]: |
| #165 | reasons.append(f"holders={t['holders']}") |
| #166 | if t.get("txns", 0) >= config["min_txns"]: |
| #167 | reasons.append(f"txns={t['txns']}") |
| #168 | if reasons: |
| #169 | passed.append((t, reasons)) |
| #170 | |
| #171 | # Sort by volume (descending) - highest volume = most liquid interest |
| #172 | passed.sort(key=lambda x: x[0].get("volume_24h", 0), reverse=True) |
| #173 | return passed |
| #174 | ``` |
| #175 | |
| #176 | ### Step 7: Click Through for Token Details |
| #177 | |
| #178 | For tokens that pass screening, click through to the pair page for deeper analysis: |
| #179 | |
| #180 | ```python |
| #181 | # Click on a token row to navigate to its pair page |
| #182 | pair_link = await row.query_selector('a[href*="/solana/"]') |
| #183 | pair_url = await pair_link.get_attribute('href') |
| #184 | await page.goto(f"https://dexscreener.com{pair_url}") |
| #185 | ``` |
| #186 | |
| #187 | On the pair page you can extract: |
| #188 | - **Full holder distribution** (top holders %) |
| #189 | - **Price chart context** (support/resistance levels) |
| #190 | - **Social links** (Twitter, Telegram, website) |
| #191 | - **Token contract address** (for further analysis) |
| #192 | - **Creator wallet** (check if it's a known rug deployer) |
| #193 | |
| #194 | **Safety check on pair page:** |
| #195 | |
| #196 | ```python |
| #197 | # Check if the token CA has been rug-checked |
| #198 | # Look for "Rug Pull" or scam warnings in the UI |
| #199 | risk_warning = await page.query_selector('[class*="risk"], [class*="warning"], [class*="scam"]') |
| #200 | if risk_warning: |
| #201 | warning_text = await risk_warning.inner_text() |
| #202 | # Flag this token as potentially dangerous |
| #203 | ``` |
| #204 | |
| #205 | ## Token Ranking System |
| #206 | |
| #207 | Rank screened tokens using a scoring system: |
| #208 | |
| #209 | ```python |
| #210 | def score_token(t): |
| #211 | score = 0 |
| #212 | score += min(t.get("volume_24h", 0) / 100_000, 10) # up to 10 pts for volume |
| #213 | score += min(t.get("liquidity_usd", 0) / 50_000, 10) # up to 10 pts for liquidity |
| #214 | score += 5 if t.get("age_hours", 999) < 1 else 0 # bonus for brand new |
| #215 | score += 3 if t.get("change_5m", 0) > 10 else 0 # bonus for strong momentum |
| #216 | score += 2 if t.get("holders", 0) > 200 else 0 # bonus for wide distribution |
| #217 | score -= 5 if t.get("fdv_liquidity_ratio", 0) > 100 else 0 # penalty for high FDV/liq |
| #218 | return score |
| #219 | ``` |
| #220 | |
| #221 | Return results sorted by score descending, with a brief rationale for each pick. |
| #222 | |
| #223 | ## Tab Strategy |
| #224 | |
| #225 | Scan tabs in this priority order for best results: |
| #226 | |
| #227 | 1. **Trending** — Hottest tokens, highest chance of continuation |
| #228 | 2. **New** — Recently created, potential early entries |
| #229 | 3. **Gainers** — Strong momentum plays |
| #230 | 4. **Recently Copied** — Arbitrage opportunities from other chains |
| #231 | |
| #232 | For a comprehensive scan, scrape all 4 tabs and deduplicate by contract address. |
| #233 | |
| #234 | ## Edge Cases & Handling |
| #235 | |
| #236 | | Situation | Handling | |
| #237 | |-----------|----------| |
| #238 | | **Cloudflare/rate limiting** | Add random delays (1-3s) between actions. If blocked, rotate user-agent | |
| #239 | | **No tokens pass filters** | Report honestly: "No tokens meet the criteria. Consider loosening thresholds" | |
| #240 | | **Table fails to load** | Retry with page refresh. If persistent, report error and suggest checking if dexscreener.com is accessible | |
| #241 | | **Dynamic class names** | Use stable data attributes (`[data-group]`) or text matchers instead of CSS classes | |
| #242 | | **Very new tokens (< 1 min)** | May not have full data. These are high-risk; flag them as "extremely early" | |
| #243 | | **Duplicate tokens across tabs** | Deduplicate by contract address to avoid double-counting | |
| #244 | |
| #245 | ## Output Format |
| #246 | |
| #247 | Present findings in a structured format: |
| #248 | |
| #249 | ``` |
| #250 | ## DexScreener Scan Results |
| #251 | |
| #252 | ### Top Picks (Passed Screening) |
| #253 | 1. **$TOKEN** — Score: 18/20 |
| #254 | - Price: $0.0042 | Vol: $1.2M | Liq: $45K | Age: 3m | Holders: 150 |
| #255 | - Rationale: Brand new, strong volume, good liquidity, wide holder distribution |
| #256 | - CA: [contract_address] |
| #257 | |
| #258 | 2. **$TOKEN2** — Score: 14/20 |
| #259 | - ... |
| #260 | |
| #261 | ### Tokens Scanned |
| #262 | - Trending tab: 25 tokens |
| #263 | - New tab: 25 tokens |
| #264 | - Total unique: 48 tokens |
| #265 | - Passed screening: 2 tokens |
| #266 | |
| #267 | ### Market Notes |
| #268 | - Average liquidity across scanned tokens: $12K |
| #269 | - Heaviest volume: $TOKEN ($1.2M) |
| #270 | - Oldest token in top 25: 6h ago |
| #271 | ``` |
| #272 | |
| #273 | ## Scripts |
| #274 | |
| #275 | ### `scripts/scan_dexscreener.py` |
| #276 | Main scanning script that orchestrates: open browser → navigate → scrape → filter → rank → output. |
| #277 | |
| #278 | ### `scripts/screen_tokens.py` |
| #279 | Standalone screening/filtering logic that can be used independently on cached data. Accepts configurable thresholds via stdin or arguments. |
| #280 | |
| #281 | ## References |
| #282 | |
| #283 | ### `references/dexscreener_layout.md` |
| #284 | Current DOM structure and selectors for dexscreener.com/solana. Update this when DexScreener changes their layout. Always inspect the page before scraping to verify selectors are still valid. |
| #285 |