π Poolser β for Uniswap positions.
A macOS menu bar app for monitoring your Uniswap v3 and Uniswap v4 liquidity positions. See unclaimed fees in USD at a glance, check whether positions are in range, and open positions directly in the Uniswap app.
Disclaimer: This is an independent hobby project with no affiliation, association, or connection to Uniswap Labs or the Uniswap Protocol. "Uniswap" is a trademark of Uniswap Labs.
- Live unclaimed fee totals in the menu bar (auto-refresh every 10 minutes + manual refresh)
- Multi-chain Uniswap v3 support on Infura-backed EVM networks:
- Ethereum
- Base
- Arbitrum
- Optimism
- Polygon
- Uniswap v4 position loading + fee calculation on:
- Ethereum
- Base
- Arbitrum
- Optimism
- Polygon
- Blast
- Unichain
- Per-position breakdown: token pair, fee tier, in/out-of-range status, position value, unclaimed fees
- Tick range visualization with:
- in/out-of-range styling
- current tick needle and boundary markers
- compact out-of-range distance labels
- Robust RPC handling:
- retries with backoff + jitter
- request pacing with a shared credit-based limiter
- clearer RPC error surfacing in-app logs/UI
- stale refresh protection (older in-flight loads cannot override newer refreshes)
- Incremental v4 ownership discovery:
- bootstrap scan is chunked and resumable
- bootstrap follow-ups run per chain (accelerated while in progress)
- in-app bootstrap status shows live countdown to next auto-refresh
- ownership cache avoids rescanning full history every refresh
- Out-of-range notifications (opt-in): sends a macOS notification whenever a position moves in or out of range. Enable via Settings β Range Change Notifications. Disabled by default. macOS will prompt for notification permission the first time the app launches.
- Claimed fee history (opt-in): scans on-chain
Collectevents to show total historical claimed fees per v3 position. Enable via Settings β Track Claimed Fees. Disabled by default β the scan makes manyeth_getLogsrequests and can be noticeably request-heavy on fast-block chains like Base or Arbitrum. Results are cached locally and only new blocks are re-scanned on subsequent refreshes. - Chain icons in UI (downloaded once from CoinGecko and cached locally)
- Reads data directly from chain RPC (Infura endpoints derived from your API key), with no third-party indexer
- Click any position to open it on app.uniswap.org
- Launch at Login support (toggle in Settings)
- Native macOS app β no Electron, no web view
Main menu bar popup:
Menu bar overview:
Settings:
Logs:
- macOS 14 (Sonoma) or later
- An Infura API key
- A wallet address that holds Uniswap v3 or v4 positions
The project uses Swift Package Manager with no external dependencies.
git clone https://github.com/GoranStoyanov/Poolser.git
cd Poolser
swift build -c releaseTo run directly:
swift runTo open in Xcode:
open Package.swiftThis repo includes a pre-commit hook that scans staged changes for secrets.
- Install gitleaks:
brew install gitleaks- Enable repo-managed hooks once:
git config core.hooksPath .githooks
chmod +x .githooks/pre-commitAfter setup, each commit runs gitleaks on staged changes and blocks the commit if a secret is detected.
Open Settings (gear icon in the popup, or β,) and configure:
| Field | Description |
|---|---|
| Wallet Address | Your Ethereum address (0xβ¦) |
| Infura API Key | The API key only (no full URL needed) |
| Enabled Networks | Toggle supported Infura networks on/off |
| Refresh Interval | Auto-refresh period |
| RPC Credit Budget | Local pacing budget for RPC calls |
| v4 Log Settings | Chunk size/concurrency/bootstrap controls for v4 log scans |
Important behavior:
- Setting edits are draft-only until you press Save & Refresh
- Closing settings with
X/Escdiscards unsaved changes - Saved settings are persisted in
UserDefaults - Some Infura networks are gated per project; enable the network in your Infura dashboard first if needed
- Uses
balanceOf(owner)on the v3NonfungiblePositionManager - Enumerates token IDs via
tokenOfOwnerByIndex - Loads per-position details via
positions(tokenId)
- Uses
balanceOf(owner)on the v4PositionManager - Reconstructs ownership from
Transferlogs (v4 PM is not ERC-721 Enumerable) - Persists a local ownership cache in
UserDefaults:- last scanned block
- candidate token IDs
- currently owned token IDs
- next bootstrap cursor block (
nextBootstrapFromBlock) for resume - First-time bootstrap progresses in chunks and resumes from cache until caught up
- While bootstrap is active, Poolser schedules accelerated per-chain follow-up refreshes (without changing the normal global refresh interval)
Poolser includes built-in pacing to reduce HTTP 429 rate-limit errors:
- Credit-aware request limiter (safe margin for constrained plans)
- Reduced
eth_getLogsconcurrency/chunk pressure - Retry logic for transient/null/malformed responses
If your provider still rate-limits frequently:
- Increase the refresh interval and avoid rapid manual refresh spam.
- Use a higher-throughput Infura plan.
- Prefer reliable archival/log-capable endpoints for heavy
eth_getLogsworkloads.
-
RPC: HTTP 429- Your RPC provider is throttling requests. See the rate-limiting section above.
-
v4: bootstrap scan in progress (auto-refresh in ~Xs from 0x...)- Expected on first sync for wallets with long history.
0x...is the next resume block and~Xsis a live countdown.
- Expected on first sync for wallets with long history.
-
v4: bootstrap refresh in progress (from 0x...)- Countdown reached zero and the accelerated follow-up refresh is currently running for that chain.
-
v4: no Transfer events found for this wallet (balance=N)- Usually indicates incomplete log coverage from the RPC provider, or bootstrap not completed yet.
-
Intermittent
no result/ bad JSON in logs- Usually provider instability. The app retries automatically, but a more reliable endpoint helps.
-
A network you enabled disappears from selection after refresh
- Your Infura project likely has no access to that network yet. Enable it in the Infura dashboard for your API key, then re-enable it in Settings.
- Poolser auto-disables networks that return Infura access-denied responses.
Poolser communicates only with:
- Infura RPC endpoints derived from your API key (on-chain reads)
- CoinGecko and DefiLlama public APIs (token USD pricing)
- CoinGecko asset-platform metadata/icons (for chain icon rendering + local cache)
No analytics, no tracking, no data leaves your machine beyond those requests.
MIT β see LICENSE.