From 0674f4da48a2b2efa31ac1776d7c25cec0d44630 Mon Sep 17 00:00:00 2001 From: Gaurav Agarwal Date: Tue, 11 Aug 2026 21:32:12 +0530 Subject: [PATCH 1/5] docs: add Pons API page for Robinhood Chain (BIT-15195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the Pons V2 bonding-curve launchpad (PonsV2LaunchFactory 0x7ed598bc… and PonsV2LaunchAndBuy 0xe33e9e47…) on Robinhood Chain. Bitquery has no ABI for any Pons V2 contract, and TokenLaunched indexes token, curve and deployer — so the event payload alone never identifies the launched token. The page works around that two ways, both verified live: Call.Output on the launch selectors returns (token, curve), and the Topics filter matches an indexed address to reconstruct a token's full lifecycle from undecoded events. Covers the launch feed and metadata (Call.Input carries name, symbol, IPFS logo, description and socials), bonding-curve trades, the snipe tax decay, graduation, the hooked Uniswap v4 pool, post-graduation Trading/DEXPoolEvents/DEXPoolSlippages queries, and holders. All 63 event topic0 values were computed from the verified contract source and confirmed against live logs. Every GraphQL block in the page was executed against streaming.bitquery.io; the three subscriptions were verified over WebSocket. Co-Authored-By: Claude Opus 5 --- docs/blockchain/robinhood/pons-api.md | 1026 +++++++++++++++++++++++++ sidebars.js | 1 + 2 files changed, 1027 insertions(+) create mode 100644 docs/blockchain/robinhood/pons-api.md diff --git a/docs/blockchain/robinhood/pons-api.md b/docs/blockchain/robinhood/pons-api.md new file mode 100644 index 00000000..15ef2da0 --- /dev/null +++ b/docs/blockchain/robinhood/pons-api.md @@ -0,0 +1,1026 @@ +--- +title: "Pons API — Bonding Curve Launchpad on Robinhood Chain" +description: "Pons API: track the Pons V2 bonding-curve launchpad on Robinhood Chain with Bitquery GraphQL. Query new launches, curve trades, snipe tax, graduations, and Uniswap v4 pools." +sidebar_position: 7 +keywords: + - Pons API + - Pons Robinhood API + - Pons V2 API + - Pons launchpad API + - PonsV2LaunchFactory + - PonsV2LaunchAndBuy + - Pons bonding curve API + - Pons curve trades API + - Pons CurveBuy event + - Pons CurveSell event + - Pons TokenLaunched event + - Pons PoolGraduated event + - Pons LaunchSwept event + - Pons graduation API + - Pons snipe tax + - Pons meme hook + - Pons Uniswap v4 pool + - Pons new token stream + - Pons launch subscription + - Pons token metadata API + - Pons creator fee API + - Robinhood Chain launchpad API + - Robinhood Chain 4663 + - Bitquery Pons API + - Bitquery Robinhood Events API + - Bitquery Robinhood Calls API + - newly launched tokens Robinhood + - meme coin launchpad API + - launch memecoin against tokenized stock + - Pons pair tokens + - Pons sniper bot data + - track Pons launches real time + - Pons holders API + - Pons pool liquidity API +--- + +# Pons API — Bonding Curve Launchpad on Robinhood Chain + +**[Pons](https://www.ponsfamily.com/launchpad)** is a token launchpad on **Robinhood Chain**. Its **V2** contracts run a real **bonding curve** that graduates into a **Uniswap v4 pool behind a Pons-owned hook**, and they let a creator quote a launch in **native ETH, USDG, or a tokenized stock** such as TSLA or NVDA. This guide shows how to track **new Pons launches**, **bonding-curve trades**, **snipe tax**, **graduations**, and **post-graduation prices and liquidity** with Bitquery GraphQL APIs, using the `EVM(network: robinhood)` and `Trading` cubes. + +:::note API Key Required +To query or stream data outside the Bitquery IDE, you need an API access token. + +Follow the steps here: [How to generate Bitquery API token ➤](/docs/authorization/how-to-generate/) +::: + +:::tip Related docs +- [Robinhood Trades API](/docs/blockchain/robinhood/robinhood-trades) +- [Robinhood Meme Coin Launches API](/docs/blockchain/robinhood/robinhood-meme-coin-launches) +- [Pools.trade API on Robinhood](/docs/blockchain/robinhood/pools-trade-api) +- [Flap.sh API on Robinhood](/docs/blockchain/robinhood/flap-sh-api) +- [Robinhood Calls API](/docs/blockchain/robinhood/robinhood-calls-api) +- [Robinhood Token Holders API](/docs/blockchain/robinhood/robinhood-token-holders-api) +- [WebSocket subscriptions](/docs/subscriptions/websockets/) +::: + +--- + +## How a Pons V2 launch works + +Every launch mints a **fixed 1,000,000,000 supply** straight into its own **bonding curve contract**. Traders buy and sell against that curve — not against any DEX — until the curve has taken in its **graduation threshold** of the quote asset. At that point the curve is **swept**, and the proceeds seed a **Uniswap v4 pool** whose liquidity position is **permanently locked**. + +| Property | Value | +| --- | --- | +| Network | Robinhood Chain (`network: robinhood`, chain ID **4663**) | +| Launch supply | `1000000000` (1 billion, decimal-normalized), 18 decimals | +| Pre-graduation venue | **Pons bonding curve**, one contract per token | +| Post-graduation venue | **Uniswap v4** (`Protocol: uniswap_v4`) | +| Graduation threshold | **4.2 ETH** for native-quoted launches; a per-asset amount for ERC-20 quotes | +| Curve trade fee | **100 bps** (1%) of the quote leg | +| Creator tax | set per launch, capped by the factory (`maxCreatorTaxBps`) | +| Launch fee | **0.0005 ETH** | +| Graduated pool `fee` | `0` — **all fees are taken by the hook**, not by the pool | +| Graduated pool `tickSpacing` | `200` | +| Graduated pool `hooks` | `0xe5e702641ea86f4ae6cc3cdaed2b886f976be044` (**PonsV2MemeHook**) | + +### The supply split + +The curve's shape fixes exactly how much supply reaches the pool, and it is the same for every launch regardless of quote asset: + +| Slice | Share of supply | Where it ends up | +| --- | --- | --- | +| Sold on the curve | **5/7** (≈714,285,714) | Buyers | +| Swept at graduation | **2/7** (≈285,714,286) | Split below | +| ↳ seeds the v4 pool | **10/49** (≈204,081,633) | Locked full-range position | +| ↳ permanently locked | **4/49** (≈81,632,653) | `PonsV2LaunchLocker` | + +### How Pons differs from pools.trade {#pons-vs-poolstrade} + +Pons and [pools.trade](/docs/blockchain/robinhood/pools-trade-api) are structurally opposite, and queries do not transfer between them: + +| | Pons V2 | pools.trade | +| --- | --- | --- | +| Pre-graduation venue | Real bonding-curve contract | Uniswap v4 pool from block one | +| Graduation event | **Yes** — `LaunchSwept` + `PoolGraduated` | None | +| Pool `hooks` | PonsV2MemeHook | `0x000…000` | +| Pool `fee` / `tickSpacing` | `0` / `200` | `2500` / `25` or `60` | +| Quote assets | ETH, USDG, tokenized stocks | Mostly native ETH | +| Curve trades in `Trading` cube | **No** | N/A — all trades are pool trades | + +:::caution Pons V1 is a different protocol and is still live +`PonsLaunchFactory` at `0xa5aab3f0c6eeadf30ef1d3eb997108e976351feb` is the **V1** launchpad. It is **still deploying tokens**, it has **no bonding curve** (each token gets a Uniswap V3 pool at launch), and its events have **different signatures and different topic0 values** from V2: + +```text +db51ea9ad51ab453a65a4cb7e60c3cb378c9501bb002609f8f97778fb6c4235a TokenLaunched(address,address,address,address,address,uint256,uint256,uint256,uint256,uint256) +1461370115e1c2be79cb529f8cfcbd11316e789d9c6099fc83417b0b4c48c62a TokenDeployed(address,address,address,address,uint256,uint256) +``` + +Every query on this page targets **V2 only**. A "Pons launches" feed built from V2 alone silently misses all V1 activity — add the V1 factory address and its topic0s if you need both. +::: + +--- + +## Contract addresses + +| Role | Address | Notes | +| --- | --- | --- | +| **Launch factory** (`PonsV2LaunchFactory`) | `0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e` | Emits `TokenLaunched`, `LaunchSwept`, `PoolGraduated` | +| **Launch router** (`PonsV2LaunchAndBuy`) | `0xe33e9e479df8802cb0866d5d05258bec4cf62948` | `launchAndBuy()` — creates the token and executes the creator's first buy in one transaction | +| **Meme hook** (`PonsV2MemeHook`) | `0xe5e702641ea86f4ae6cc3cdaed2b886f976be044` | The v4 hook on every graduated pool; emits `PoolRegistered` and `HookFeeCollected` | +| **Launch locker** (`PonsV2LaunchLocker`) | `0x267444d099b10fb5ed7c3cc7b7c767adca574952` | Holds the locked position NFT and the locked supply | +| **Graduation executor** | `0xc7819b64a1daecd7ec19856d026cb14efbd89046` | Emits `GraduationDustSwept` | +| **Bonding curve** | one per token | Address is the **receiver of the launch mint** — see [Newly launched tokens](#newly-launched-tokens) | +| **Uniswap v4 PoolManager** | `0x8366a39cc670b4001a1121b8f6a443a643e40951` | Shared chain singleton — **not** Pons-only | +| **Pons V1 factory** | `0xa5aab3f0c6eeadf30ef1d3eb997108e976351feb` | Separate protocol, still active — see the caution above | + +:::caution The v4 PoolManager is not a Pons filter +`0x8366a39c…` is the **Uniswap v4 singleton** for all of Robinhood Chain. Every v4 trade on the network routes through it, [pools.trade](/docs/blockchain/robinhood/pools-trade-api) included. What isolates a **graduated Pons pool** is the `hooks` field being `0xe5e70264…` — see [The graduated Uniswap v4 pool](#the-graduated-uniswap-v4-pool). +::: + +### Quote (pair) assets + +Native ETH is the default quote asset, but the factory also approves **USDG and a set of tokenized stocks**, each with its own graduation threshold denominated in that asset's own decimals: + +| Symbol | Address | Decimals | +| --- | --- | --- | +| ETH (native) | `0x0000000000000000000000000000000000000000` | 18 | +| USDG | `0x5fc5360d0400a0fd4f2af552add042d716f1d168` | 6 | +| AAPL | `0xaf3d76f1834a1d425780943c99ea8a608f8a93f9` | 18 | +| AMD | `0x86923f96303d656e4aa86d9d42d1e57ad2023fdc` | 18 | +| AMZN | `0x12f190a9f9d7d37a250758b26824b97ce941bf54` | 18 | +| COIN | `0x6330d8c3178a418788df01a47479c0ce7ccf450b` | 18 | +| CRCL | `0xdf0992e440dd0be65bd8439b609d6d4366bf1cb5` | 18 | +| GME | `0x1b0e319c6a659f002271b69db8a7df2f911c153e` | 18 | +| GOOGL | `0x2e0847e8910a9732eb3fb1bb4b70a580adad4fe3` | 18 | +| META | `0xc0d6457c16cc70d6790dd43521c899c87ce02f35` | 18 | +| MSFT | `0xe93237c50d904957cf27e7b1133b510c669c2e74` | 18 | +| MU | `0xff080c8ce2e5feadaca0da81314ae59d232d4afd` | 18 | +| NVDA | `0xd0601ce157db5bdc3162bbac2a2c8af5320d9eec` | 18 | +| PLTR | `0x894e1ec2d74ffe5aef8dc8a9e84686accb964f2a` | 18 | +| SNDK | `0xb90a19ff0af67f7779aff50a882a9cff42446400` | 18 | +| SPCX | `0x4a0e65a3eccec6dbe60ae065f2e7bb85fae35eea` | 18 | +| SPY | `0x117cc2133c37b721f49de2a7a74833232b3b4c0c` | 18 | +| TSLA | `0x322f0929c4625ed5bad873c95208d54e1c003b2d` | 18 | + +The quote asset of any launch is `pairToken`, the first word of the `TokenLaunched` payload. The set is owner-mutable — the factory emits `PairTokenApprovalUpdated` and `PairTokenEconomicsUpdated` when it changes. + +--- + +## Event reference + +**Bitquery has no ABI for any Pons V2 contract**, so `Log.Signature.Name` is empty on every event on this page and `Arguments` comes back empty. The events are still fully indexed: filter on `Log.Signature.SignatureHash` (topic0) and read the payload from `LogHeader.Data`. + +The **Indexed** column is what makes Pons awkward — indexed arguments live in the log's topics, and Bitquery does not expose topics as an output field. An event with all its addresses indexed has a payload that tells you nothing about *which* token it concerns. Two mechanisms get around that: the [`Topics` filter](#filtering-by-an-indexed-argument) and the [`Calls` cube](#newly-launched-tokens). + +### Factory — `0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e` {#factory-events} + +| Event | Indexed | topic0 (`SignatureHash`) | +| --- | --- | --- | +| `TokenLaunched(address,address,address,address,uint256,uint256)` | 3 | `8d4aad4953d0ca700d468f3753aa14432d1b35b43ec6409f051fb6aa43a89607` | +| `LaunchSwept(address,uint256,uint256)` | 1 | `cdb72f157fd3666758a6ce201387ffb52038c7562e4fff352828da1096c4b6b4` | +| `PoolGraduated(address,uint256,uint256,uint256)` | 1 | `0a44ef75df69c534f43cd6c1aa3ef8983065fe5fe79ef9e79f6494e6f258c259` | +| `GraduationTokensPermanentlyLocked(address,uint256)` | 1 | `a0a18f5bf205becee8b268d7cf69addab8548ae8ef361791464cf0e0e17c1361` | +| `CreatorFeeRecipientUpdated(address,address,address)` | 3 | `308c390ed1ab5873392818e036cabdf408bc8ad042fbaead3108954ff75ba980` | +| `LaunchForceSwept(address)` *(rare)* | 1 | `52c1a28345695afc7f6b7629133124dec5d61ee745affd65e4fd2a776bc05840` | +| `LaunchGraduationRescued(address,address,uint256,uint256)` *(rare)* | 2 | `7017304fdd491394686dce984eac721f0be1a22228346210f16694772bde44ca` | + +### Bonding curve — one contract per token + +| Event | Indexed | topic0 (`SignatureHash`) | +| --- | --- | --- | +| `CurveBuy(address,address,uint256,uint256,uint256,uint256)` | 2 | `ec36bf571f136799e8dc0b0b8bea4b04d8bd3d43de838aab0d5fc21d4cbfc455` | +| `CurveSell(address,address,uint256,uint256,uint256,uint256)` | 2 | `8113d738abdcb6b38357e9d53a54a7157861a09031b453651f0fe7fe151f59df` | +| `SnipeTaxCharged(address,uint256)` | 1 | `3bc39a5562b28f5fe8f36cecabfbaa12bb969acf05717994709225fc412a9934` | +| `SnipeTaxExempted(address)` | 1 | `e4b7e48fbd47c2f602bacadee76ad33b16542ddb4997cfc0de04c311adcfa8c7` | +| `FeesSwept(uint256,uint256,uint256)` | 0 | `9f4cd7c4ed99d08a797804560c9c5d71d2cf7e101f2e3b5e7d1ca8a24c370e4f` | +| `CurveBuyRefunded(address,uint256)` | 1 | `a69e8258ccc7b9bbb70ab953fc2d1062b4ee28b8ca827534097e1732e87b0262` | +| `CurveCompleted(address,uint256,uint256)` | 0 | `f8d37a90738ae063b8b8058b66f5880cf3cf7ab0c5d4fa78219696591dfbfb67` | +| `Initialized(address)` | 0 | `908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e6` | +| `BuybackLocked(uint256,uint256)` *(rare)* | 0 | `5feba9b0d52c92ada4b9c571c2bee52390c54f2947208ab250221e6ee32f12ff` | +| `AutoGraduationFailed(address,uint256)` *(rare)* | 1 | `e2cd2f31ebc05ec28640102987f4c8fc5f20e269e1b3aa82577f3f2f0e35c7c6` | + +### Meme hook — `0xe5e702641ea86f4ae6cc3cdaed2b886f976be044` + +| Event | Indexed | topic0 (`SignatureHash`) | +| --- | --- | --- | +| `PoolRegistered(bytes32,address,address,address)` | 1 | `01bf263a1db1652580721573296e1a1fa70b3d4c87f61d02a69c4e1109d2d573` | +| `HookFeeCollected(bytes32,address,uint256,uint256)` | 1 | `c532c43b3423e14ef72748f1c8291238829ca0af8ba9b67975ad1483485a4b4d` | +| `PoolFeesSwept(bytes32,uint256,uint256,uint256,uint256)` | 1 | `2f3c43579b9064b6f28edcf41608f3815792d274a56afe024359703cb4ea9b30` | + +### Router, locker, vault, executor + +| Event | Emitter | Indexed | topic0 (`SignatureHash`) | +| --- | --- | --- | --- | +| `Launched(address,address,address,address,uint256,uint256)` | router | 3 | `dcacba5e347ae7abd91cb519eb877af8fa7774e347b85dd3ddcd24a2ba8cdf37` | +| `TokenSupplyLocked(address,uint256)` | locker | 1 | `af33c4aba92959b3e7ddc83ab728938262da159a6c05ca836f6c46f9bcb2c740` | +| `PositionLocked(address,uint256)` | locker | 2 | `2cabb2a2973327d5863ceb4707e9441851243897e86d587ee35943599752eb54` | +| `Locked(address,address,uint256,uint256)` | buyback vault | 2 | `967ad762aa9070ada8db64577288e214771e89667066ae38e8750cb8a86c5429` | +| `GraduationDustSwept(address,address,uint256)` | executor | 2 | `80a5a2ff8b8c5533e5862e4e161bbcade9af6fd9d67bef56a590b062107f027f` | + +Every topic0 above was verified two ways: keccak-256 preimage match against the signature from the verified contract source, and live occurrence on Robinhood Chain. Rows marked *(rare)* are admin or failure paths that exist in the ABI but fire infrequently. + +### Querying a raw event by topic0 + +The pattern is the same for every row above — filter on `SignatureHash`, scope with `LogHeader.Address` where the emitter is a fixed contract, and read `LogHeader.Data`: + +```graphql +{ + EVM(network: robinhood) { + Events( + limit: {count: 25} + orderBy: {descending: Block_Time} + where: { + LogHeader: {Address: {is: "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e"}} + Log: { + Signature: { + SignatureHash: {is: "8d4aad4953d0ca700d468f3753aa14432d1b35b43ec6409f051fb6aa43a89607"} + } + } + } + ) { + Block { Time Number } + Transaction { Hash From To } + LogHeader { Data } + } + } +} +``` + +`SignatureHash` values are supplied **without** a `0x` prefix. + +:::caution Scope curve and hook events to an emitter where you can +Bonding curves are one contract per token, so a topic0-only filter on `CurveBuy` is the right scope — it captures every curve on the network at once, and `LogHeader.Address` tells you which one. + +For fixed-emitter events, **always add `LogHeader.Address`**. A signature such as `PoolRegistered(bytes32,address,address,address)` is generic enough that unrelated contracts on the chain emit the same topic0 with a *different* indexing layout — same hash, incompatible payload. Filtering topic0 alone will mix them into your results. +::: + +### Filtering by an indexed argument + +Indexed arguments are not readable, but they **are** filterable. `Topics: {includes: [{Hash: {is: "…"}}]}` matches any topic in the log, including topic0 and any indexed address padded to 32 bytes. This is what makes an all-indexed event like `TokenLaunched` usable — see [Token lifecycle](#token-lifecycle-in-one-query). + +```graphql +Topics: {includes: [{Hash: {is: "0x000000000000000000000000"}}]} +``` + +The `0x` prefix is optional here. `includes`, `excludes`, `startsWith`, `endsWith` and `length` are all available. + +:::note `SignatureHash`, `Topics`, and `Calls` are realtime-only +Filtering by `Log.Signature.SignatureHash` or by `Topics` — and the entire `Calls` cube — is served only by the **realtime** dataset. `dataset: archive` returns `no candidate table can serve: [Log_Signature_SignatureHash]`, and `dataset: combined` returns `no data available yet to query dataset combined` for the Event cube on Robinhood. + +Realtime is a rolling window of recent blocks. To reach further back than that window, use the [launch-mint transfer pattern](#launch-mint-archive), which runs on `archive`. +::: + +### Full signatures for client-side decoding + +```text +# factory — 0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e +8d4aad4953d0ca700d468f3753aa14432d1b35b43ec6409f051fb6aa43a89607 TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold) +cdb72f157fd3666758a6ce201387ffb52038c7562e4fff352828da1096c4b6b4 LaunchSwept(address indexed token, uint256 quoteOut, uint256 tokenOut) +0a44ef75df69c534f43cd6c1aa3ef8983065fe5fe79ef9e79f6494e6f258c259 PoolGraduated(address indexed token, uint256 positionId, uint256 tokenAmount, uint256 pairTokenAmount) +a0a18f5bf205becee8b268d7cf69addab8548ae8ef361791464cf0e0e17c1361 GraduationTokensPermanentlyLocked(address indexed token, uint256 amount) + +# bonding curve — one per token +ec36bf571f136799e8dc0b0b8bea4b04d8bd3d43de838aab0d5fc21d4cbfc455 CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax) +8113d738abdcb6b38357e9d53a54a7157861a09031b453651f0fe7fe151f59df CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax) +3bc39a5562b28f5fe8f36cecabfbaa12bb969acf05717994709225fc412a9934 SnipeTaxCharged(address indexed recipient, uint256 amount) +f8d37a90738ae063b8b8058b66f5880cf3cf7ab0c5d4fa78219696591dfbfb67 CurveCompleted(address recipient, uint256 quoteOut, uint256 tokenOut) + +# meme hook — 0xe5e702641ea86f4ae6cc3cdaed2b886f976be044 +01bf263a1db1652580721573296e1a1fa70b3d4c87f61d02a69c4e1109d2d573 PoolRegistered(PoolId indexed poolId, address memecoin, address quoteToken, address creator) +c532c43b3423e14ef72748f1c8291238829ca0af8ba9b67975ad1483485a4b4d HookFeeCollected(PoolId indexed poolId, address currency, uint256 feeAmount, uint256 taxAmount) + +# router — 0xe33e9e479df8802cb0866d5d05258bec4cf62948 +dcacba5e347ae7abd91cb519eb877af8fa7774e347b85dd3ddcd24a2ba8cdf37 Launched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold) +``` + +--- + +## Newly launched tokens + +`TokenLaunched` indexes **token, curve and deployer**, so its payload carries only `pairToken`, `launchConfigId` and `graduationThreshold`. It is a good *counter* of launches and a good *lifecycle marker*, but on its own it never tells you which token launched. + +The **`Calls` cube solves this completely.** `Call.Output` holds the function's return data, and every Pons launch entry point returns the addresses you need: + +| Selector | Function | `Call.Output` | +| --- | --- | --- | +| `f85f8e41` | `launchAndBuy(...)` on the router | `(address token, address curve, uint256 tokensOut)` | +| `f35abbcf` | `launchToken(params, launchConfigId, pairToken)` | `(address token, address curve)` | +| `a72101af` | `launchToken(params, launchConfigId, pairToken, snipeTaxExemptions)` | `(address token, address curve)` | +| `d6a0eef5` | `launchTokenFor(...)` — what the router calls internally | `(address token, address curve)` | + +### The complete launch feed + +```graphql +{ + EVM(network: robinhood) { + Calls( + limit: {count: 20} + orderBy: {descending: Block_Time} + where: { + Call: { + To: {in: [ + "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e", + "0xe33e9e479df8802cb0866d5d05258bec4cf62948" + ]} + Signature: {SignatureHash: {in: ["f35abbcf", "a72101af", "d6a0eef5", "f85f8e41"]}} + Success: true + } + } + ) { + Block { Time Number } + Transaction { Hash From } + Call { To Value Signature { SignatureHash } Input Output } + } + } +} +``` + +`Transaction.From` is the creator. `Call.Value` is the ETH attached (launch fee plus, on `launchAndBuy`, the creator's first buy). Take the first two 32-byte words of `Call.Output` for the token and the curve: + +```js +const o = call.Output.replace(/^0x/, ''); +const token = '0x' + o.slice(24, 64); +const curve = '0x' + o.slice(88, 128); +``` + +:::caution Filter on `launchTokenFor` and `launchAndBuy`, never both +`launchAndBuy` on the router calls `launchTokenFor` on the factory internally, so a filter matching both selectors returns **two rows for the same launch** — one for the router's outer call, one for the factory's inner call. Either drop `d6a0eef5` (router launches are then attributed to the router call, which is where `Transaction.From` is the real creator), or deduplicate on `Transaction.Hash`. +::: + +### Stream new launches in real time + +```graphql +subscription { + EVM(network: robinhood) { + Calls( + where: { + Call: { + To: {in: [ + "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e", + "0xe33e9e479df8802cb0866d5d05258bec4cf62948" + ]} + Signature: {SignatureHash: {in: ["f35abbcf", "a72101af", "f85f8e41"]}} + Success: true + } + } + ) { + Block { Time } + Transaction { Hash From } + Call { To Value Signature { SignatureHash } Input Output } + } + } +} +``` + +### Token metadata: name, symbol, image, description, socials {#token-metadata} + +Pons puts **all** launch metadata in the call arguments rather than in an event, so `Call.Input` is the only on-chain source for the description, the IPFS image and the social links. The struct is: + +```solidity +struct LaunchParams { + string name; + string symbol; + string logo; // ipfs:// URI + string description; + Socials socials; // (twitter, telegram, discord, website, farcaster) + address creatorFeeRecipient; + uint16 creatorTaxBps; + bool buybackEnabled; + bytes32 expectedEconomics; + bytes32 salt; +} +``` + +ABI-decoding `Call.Input` from the launch feed above yields, for example: + +```text +token 0x6a3b0c271d335450365297cdd10a24dc8364bf63 +curve 0x9314af455ff11d02b5f87317fed8ddc9d6b17bb9 +name TickerYard +symbol YARD +logo ipfs://bafkreifqznqij7bgl7glhavtsg44ra2lf2axifmoc6r7sgwacssmlkmdzm +description Route markets. Open verifiable protocol work. +twitter https://x.com/TickerYardHQ +creatorFeeRecipient 0x84adad3ed94495c978e834bcef1e5a7f533cf981 +creatorTaxBps 100 +``` + +For `launchAndBuy` the outer arguments after the struct are `launchConfigId`, `pairToken`, `quoteIn`, `minTokensOut`, `recipient` and `snipeTaxExemptions[]` — `quoteIn` is the size of the creator's own first buy, which is a useful signal on its own. + +### Full history: the launch mint on `archive` {#launch-mint-archive} + +Because `Calls` is realtime-only, backfills use the **launch mint transfer** instead. It works on `archive`, and it carries more than you might expect: the mint's `Receiver` **is the bonding curve**, and `Currency` gives name, symbol and decimals. + +```graphql +{ + EVM(network: robinhood, dataset: archive) { + Transfers( + limit: {count: 25} + orderBy: {descending: Block_Time} + where: { + Transfer: { + Sender: {is: "0x0000000000000000000000000000000000000000"} + Amount: {eq: "1000000000"} + } + Transaction: {To: {in: [ + "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e", + "0xe33e9e479df8802cb0866d5d05258bec4cf62948" + ]}} + } + ) { + Block { Time Number } + Transaction { Hash From To } + Transfer { + Amount + Receiver + Currency { Name Symbol Decimals SmartContract } + } + } + } +} +``` + +`Transfer.Currency.SmartContract` is the token, `Transfer.Receiver` is its curve, `Transaction.From` is the creator, and `Block.Time` is the launch time. + +:::caution `Transaction.To` misses indirect launches +This pattern only catches launches where the factory or router is the transaction target. A small share of launches route through third-party contracts or arrive inside contract-creation transactions and carry a different `Transaction.To`. The [`Calls` feed](#the-complete-launch-feed) matches on `Call.To`, so it catches those too — treat it as the source of truth within the realtime window, and the transfer pattern as the archive-reachable approximation. +::: + +### Most active token creators + +```graphql +{ + EVM(network: robinhood, dataset: archive) { + Transfers( + limit: {count: 25} + orderBy: {descendingByField: "launches"} + where: { + Transfer: { + Sender: {is: "0x0000000000000000000000000000000000000000"} + Amount: {eq: "1000000000"} + } + Transaction: {To: {in: [ + "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e", + "0xe33e9e479df8802cb0866d5d05258bec4cf62948" + ]}} + Block: {Time: {since_relative: {days_ago: 7}}} + } + ) { + Transaction { From } + launches: count + } + } +} +``` + +--- + +## Bonding-curve trades + +**Pre-graduation trades are not in any DEX cube.** `DEXTrades`, `DEXTradeByTokens` and `Trading.Trades` all return zero rows for a token still on its curve, because there is no pool yet. The curve's own `CurveBuy` and `CurveSell` events are the only source. + +### Every trade on one token's curve + +Get the curve address from the [launch feed](#newly-launched-tokens), then filter on it as the emitter: + +```graphql +{ + EVM(network: robinhood) { + Events( + limit: {count: 50} + orderBy: {descending: Block_Time} + where: { + LogHeader: {Address: {is: "0x45ee6e38b1e8c570de48baf42144cddd7bfb3cc6"}} + Log: {Signature: {SignatureHash: {in: [ + "ec36bf571f136799e8dc0b0b8bea4b04d8bd3d43de838aab0d5fc21d4cbfc455", + "8113d738abdcb6b38357e9d53a54a7157861a09031b453651f0fe7fe151f59df" + ]}}} + } + ) { + Block { Time } + Transaction { Hash From } + LogHeader { Address Data } + Log { Signature { SignatureHash } } + } + } +} +``` + +Drop the `LogHeader.Address` filter to get **every curve trade on the network** in one feed — that is the shape you want for a launch-wide tape, and `LogHeader.Address` identifies the curve on each row. + +### Reading the payload + +Both events carry four 32-byte words. `Transaction.From` is the trader; the indexed `buyer`/`seller` and `recipient` are not readable. + +| Word | `CurveBuy` | `CurveSell` | +| --- | --- | --- | +| 0 | `quoteIn` — quote asset spent | `tokensIn` — tokens sold | +| 1 | `tokensOut` — tokens received | `quoteOut` — quote asset received | +| 2 | `fee` — base fee **plus snipe tax** | `fee` | +| 3 | `tax` — creator tax, paid to the creator in full | `tax` | + +```js +const w = i => BigInt('0x' + data.slice(i * 64, (i + 1) * 64)); +const [quoteIn, tokensOut, fee, tax] = [w(0), w(1), w(2), w(3)]; +const price = Number(quoteIn) / Number(tokensOut); // quote per token +``` + +Both legs are raw integers in their asset's own decimals — 18 for the token, and the quote asset's own for the quote leg (6 for USDG). + +### Snipe tax + +Pons charges a punishing, fast-decaying tax on buys inside the launch window. The curve snapshots the factory's settings when it initializes, so a launch keeps the terms it launched under: + +```text +snipeTaxBps(elapsed) = snipeTaxStartBps >> ((elapsed * 14) / snipeTaxSeconds) +``` + +with integer division, `elapsed` in seconds since the launch transaction, and zero once `elapsed >= snipeTaxSeconds`. At the factory's current settings — `snipeTaxStartBps = 9900`, `snipeTaxSeconds = 3` — that resolves to **9900 bps in the launch second, 618 bps in the next, 19 bps in the next, then zero**. Both settings are owner-mutable; read the current values with `snipeTaxStartBps()` (`0x50e25ac2`) and `snipeTaxSeconds()` (`0x6783774b`) through the `Calls` cube, taking `Call.Output`. + +Because `CurveBuy.fee` bundles the base fee and the snipe tax, the snipe portion is what makes an early buy's effective rate jump far above the 100 bps baseline. `SnipeTaxCharged` isolates it: + +```graphql +{ + EVM(network: robinhood) { + Events( + limit: {count: 25} + orderBy: {descending: Block_Time} + where: { + Log: {Signature: {SignatureHash: {is: "3bc39a5562b28f5fe8f36cecabfbaa12bb969acf05717994709225fc412a9934"}}} + } + ) { + Block { Time } + Transaction { Hash From } + LogHeader { Address Data } + } + } +} +``` + +`LogHeader.Data` is the single `amount` word, `LogHeader.Address` is the curve, and `Transaction.From` is the wallet that paid it — which is to say, **the sniper**. Creators can pre-declare exempt wallets at launch; those emit `SnipeTaxExempted` in the launch transaction, so the exemption list for a launch is recoverable from its own transaction hash. + +### Graduation progress + +There is no on-chain progress event. Sum the net quote taken in by the curve — `CurveBuy.quoteIn` minus `CurveSell.quoteOut` — and compare it against the launch's `graduationThreshold` (the third word of its `TokenLaunched` payload; **4.2 ETH** for native-quoted launches). The curve's live quote balance is the same figure, so a balance read against the curve address works as a cross-check. + +--- + +## Token lifecycle in one query + +The `Topics` filter turns the factory's all-indexed events into a per-token timeline. Pad the token address to 32 bytes and every factory event that names it comes back in order: + +```graphql +{ + EVM(network: robinhood) { + Events( + limit: {count: 25} + orderBy: {ascending: Block_Time} + where: { + LogHeader: {Address: {is: "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e"}} + Topics: {includes: [{Hash: {is: "0x00000000000000000000000095d3bc5d467d448ac83c5b33ff90f4dcfaf4c1e4"}}]} + } + ) { + Block { Time Number } + Transaction { Hash From To } + LogHeader { Data } + Log { Signature { SignatureHash } } + } + } +} +``` + +Map `SignatureHash` against the [event reference](#factory-events) and you get the full arc: + +```text +TokenLaunched → launch +LaunchSwept → threshold hit, curve drained +GraduationTokensPermanentlyLocked → 4/49 of supply locked forever +PoolGraduated → v4 pool created and seeded +``` + +Swap the factory address for the **curve address** and the same filter returns that token's `CreatorFeeRecipientUpdated` and `SnipeTaxExempted` history. + +--- + +## Graduation + +Graduation is **permissionless and two-phase**. Anyone can trigger it once the threshold is crossed, and in practice keeper bots race for it, so `Transaction.To` on the sweep is usually a third-party contract rather than Pons itself: + +1. **`graduate(address)`** (`0xff6d8d05`) — sweeps curve fees, halts trading, pulls the quote and remaining supply into the factory. Emits `CurveCompleted` on the curve and `LaunchSwept` on the factory. +2. **`createGraduatedPool(address)`** (`0x2f53ef2f`) — creates the v4 pool, mints and locks the full-range position, registers the hook. Emits `GraduationTokensPermanentlyLocked`, `PoolGraduated`, `Initialize` and `ModifyLiquidity` on the PoolManager, `PoolRegistered` on the hook, and `TokenSupplyLocked` + `PositionLocked` on the locker. + +The two phases land in **separate transactions**, seconds to minutes apart. A token in between is `Swept` — drained but not yet tradeable anywhere. + +### Enumerating graduated tokens + +`PoolGraduated` indexes the token, so it cannot tell you *which* token graduated. The hook's **`PoolRegistered` can** — its `memecoin`, `quoteToken` and `creator` are all in the payload: + +```graphql +{ + EVM(network: robinhood) { + Events( + limit: {count: 50} + orderBy: {descending: Block_Time} + where: { + LogHeader: {Address: {is: "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044"}} + Log: {Signature: {SignatureHash: {is: "01bf263a1db1652580721573296e1a1fa70b3d4c87f61d02a69c4e1109d2d573"}}} + } + ) { + Block { Time Number } + Transaction { Hash } + LogHeader { Data } + } + } +} +``` + +```js +const d = log.Data; +const memecoin = '0x' + d.slice(24, 64); +const quoteToken = '0x' + d.slice(88, 128); // 0x000…000 for native ETH +const creator = '0x' + d.slice(152, 192); +``` + +This is the query to run first when you want a **token set** to feed into the `Trading` cube — see [Top graduated tokens](#top-graduated-pons-tokens-by-volume). Keep the `LogHeader.Address` filter on the hook; without it this topic0 also matches an unrelated contract on the chain. + +### The graduated Uniswap v4 pool + +The PoolManager's `Initialize` **is** decoded, so the `PoolKey` reads without manual decoding — and scoping by `Transaction.To` on the factory isolates Pons graduations: + +```graphql +{ + EVM(network: robinhood) { + Events( + limit: {count: 10} + orderBy: {descending: Block_Time} + where: { + LogHeader: {Address: {is: "0x8366a39cc670b4001a1121b8f6a443a643e40951"}} + Log: {Signature: {Name: {is: "Initialize"}}} + Transaction: {To: {is: "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e"}} + } + ) { + Block { Time } + Transaction { Hash } + Arguments { + Name + Type + Value { + ... on EVM_ABI_Address_Value_Arg { address } + ... on EVM_ABI_Integer_Value_Arg { integer } + ... on EVM_ABI_BigInt_Value_Arg { bigInteger } + ... on EVM_ABI_Bytes_Value_Arg { hex } + } + } + } + } +} +``` + +Returns `id` (the **PoolId** you need for [liquidity and slippage](#pool-liquidity-slippage-and-balance-changes)), `currency0`, `currency1`, `fee`, `tickSpacing`, `hooks`, `sqrtPriceX96` and `tick`. Every Pons pool comes back with `fee: 0`, `tickSpacing: 200` and `hooks: 0xe5e702641ea86f4ae6cc3cdaed2b886f976be044`. + +:::note Why `fee` is zero +Trading fees on a graduated Pons pool are charged by the **hook**, not by the pool. The pool's own LP fee is `0`, and `HookFeeCollected` on `0xe5e70264…` is where the fee and the creator tax actually show up. Reading `fee` from the `PoolKey` and calling it the trading cost will understate it to zero. +::: + +--- + +## Trading data (post-graduation) + +Once graduated, a Pons token is an ordinary Uniswap v4 market: + +| Field | Value | +| --- | --- | +| `Pair.Market.ProtocolFamily` | `Uniswap` | +| `Pair.Market.Protocol` | `uniswap_v4` | +| `Pair.Market.Network` | `Robinhood` | + +:::caution There is no `Pons` protocol label +Pons tokens **cannot be isolated by protocol filter** — `uniswap_v4` on Robinhood also covers pools created elsewhere. Scope by token address: harvest the set from `PoolRegistered`, then filter `Trading` by `Token.Address: {in: [...]}`. + +`Pair.Pool.Address` is the v4 PoolManager singleton on every row, not a per-pool address. Use the `PoolId` from `Initialize` when you need to identify one pool. +::: + +### Latest trades for a graduated token + +```graphql +{ + Trading { + Trades( + limit: {count: 50} + orderBy: {descending: Block_Time} + where: { + Pair: { + Token: {Address: {is: "0x95d3bc5d467d448ac83c5b33ff90f4dcfaf4c1e4"}} + Market: {Network: {is: "Robinhood"}} + } + } + ) { + Block { Time } + Side + Price + PriceInUsd + Amounts { Base Quote } + AmountsInUsd { Base Quote } + Trader { Address } + TransactionHeader { Hash } + Pair { + Token { Address Symbol Name } + QuoteToken { Address Symbol } + Market { Protocol ProtocolFamily Network } + } + } + } +} +``` + +:::note Legs are not duplicated on Pons pools +Unlike [pools.trade](/docs/blockchain/robinhood/pools-trade-api#latest-trades-for-a-poolstrade-token), Pons v4 pools return **one row per trade leg** — measured samples deduplicate to a 1.0× factor on `(TransactionHeader.Hash, Block.Time, Side, Amounts.Base, Pair.QuoteToken.Symbol, Trader.Address)`. No dedup pass is needed before summing volume. + +One user swap can still fan out into several routed legs across different quote pairs in the same transaction, so summing every leg overstates end-user volume. Graduated tokens commonly trade against both ETH and USDG. +::: + +### OHLCV price candles + +```graphql +{ + Trading { + Tokens( + limit: {count: 24} + orderBy: {descending: Block_Time} + where: { + Token: {Address: {is: "0x95d3bc5d467d448ac83c5b33ff90f4dcfaf4c1e4"} Network: {is: "Robinhood"}} + Interval: {Time: {Duration: {eq: 3600}}} + } + ) { + Block { Time } + Token { Address Symbol Name } + Volume { Usd } + Price { Ohlc { Open High Low Close } } + } + } +} +``` + +Change `Duration` to `60`, `300`, `900` or `86400` for other candle sizes. Add `Supply { MarketCap CirculatingSupply }` for FDV. + +### Top graduated Pons tokens by volume + +Pass a token set harvested from [`PoolRegistered`](#enumerating-graduated-tokens): + +```graphql +{ + Trading { + Tokens( + limit: {count: 25} + orderBy: {descendingByField: "vol"} + where: { + Token: { + Address: {in: [ + "0x95d3bc5d467d448ac83c5b33ff90f4dcfaf4c1e4", + "0xddec0170ceb4426ea05f2fbd485dffa4fafa6615", + "0xd928a068d2b90798373a470c9d9ba562322acdef" + ]} + Network: {is: "Robinhood"} + } + Interval: {Time: {Duration: {eq: 3600}}} + Block: {Time: {since_relative: {days_ago: 1}}} + } + ) { + Token { Address Symbol Name } + vol: sum(of: Volume_Usd) + trades: count + } + } +} +``` + +:::note Keep per-interval metrics out of aggregations +Selecting a per-row metric such as `Supply { MarketCap }` alongside `sum(of: Volume_Usd)` adds it as a grouping key, so you get one row **per interval** instead of one row per token. Time windows go in `Block: {Time: …}` — `Interval.Time.Since` is not a valid field. +::: + +--- + +## Pool liquidity, slippage, and balance changes + +These three cubes are **realtime-only** on Robinhood — `archive` and `combined` both error — so use them for live monitoring and persist what you need. All three key off the **`PoolId`** from `Initialize`. + +### Live pool liquidity (depth) + +```graphql +{ + EVM(network: robinhood) { + DEXPoolEvents( + limit: {count: 10} + orderBy: {descending: Block_Time} + where: { + PoolEvent: {Pool: {PoolId: {is: "0x99b36f2b55ff70f807132c497431c399c5db8301ba1a43f3e70dc1d08b908eaa"}}} + } + ) { + Block { Time } + Log { Signature { Name } } + PoolEvent { + Dex { ProtocolName ProtocolVersion } + Pool { PoolId CurrencyA { Symbol } CurrencyB { Symbol } } + Liquidity { AmountCurrencyA AmountCurrencyAInUSD AmountCurrencyB AmountCurrencyBInUSD } + } + } + } +} +``` + +`AmountCurrencyA` is the quote side (ETH in the example) and `AmountCurrencyB` the token side. The token side's USD value reads `0` for unpriced launch tokens — value the pool from the quote leg. + +### Per-swap slippage + +```graphql +{ + EVM(network: robinhood) { + DEXPoolSlippages( + limit: {count: 10} + orderBy: {descending: Block_Time} + where: { + Price: {Pool: {PoolId: {is: "0x99b36f2b55ff70f807132c497431c399c5db8301ba1a43f3e70dc1d08b908eaa"}}} + } + ) { + Block { Time } + Price { + Dex { ProtocolName } + Pool { PoolId CurrencyA { Symbol } CurrencyB { Symbol } } + AtoB { Price MaxAmountIn MinAmountOut } + SlippageBasisPoints + } + } + } +} +``` + +Streamed with `SlippageBasisPoints: {gt: 100}`, this is a ready-made toxic-fill alert. + +### Per-transaction balance changes + +```graphql +{ + EVM(network: robinhood) { + TransactionBalances( + limit: {count: 10} + orderBy: {descending: Block_Time} + where: { + TokenBalance: {Currency: {SmartContract: {is: "0x95d3bc5d467d448ac83c5b33ff90f4dcfaf4c1e4"}}} + } + ) { + Block { Time } + Transaction { Hash From } + TokenBalance { + Address + PreBalance + PostBalance + HasPreBalance + TotalSupply + Currency { Symbol } + } + } + } +} +``` + +:::note Check `HasPreBalance` +When `HasPreBalance` is `false`, `PreBalance` reads `0` meaning "unknown", not "zero". Treat the delta as reliable only when it is `true`. `TotalSupply` reads `1000000000` for every Pons launch, which is a cheap sanity check that you are looking at the right contract. +::: + +--- + +## Holders and supply + +```graphql +{ + EVM(dataset: combined, network: robinhood) { + Holders( + limit: {count: 100} + orderBy: {descending: Balance_Amount} + where: { + Currency: {SmartContract: {is: "0x95d3bc5d467d448ac83c5b33ff90f4dcfaf4c1e4"}} + Balance: {Amount: {gt: "0"}} + Holder: {Address: {notIn: [ + "0x8366a39cc670b4001a1121b8f6a443a643e40951", + "0x267444d099b10fb5ed7c3cc7b7c767adca574952", + "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044" + ]}} + } + ) { + Holder { Address } + Balance { Amount FirstChangeTime LastChangeTime UpdateCount } + } + } +} +``` + +:::caution Exclude three protocol addresses, not one +On a graduated Pons token the top holders are all protocol contracts: + +- **`0x8366a39c…`** — the v4 PoolManager, which custodies the pool's liquidity +- **`0x267444d0…`** — `PonsV2LaunchLocker`, holding the permanently locked 4/49 of supply +- **`0xe5e70264…`** — the meme hook, holding accrued fees in the token + +Leave them in and the protocol itself dominates every holder count, concentration ratio and top-wallet leaderboard. The `notIn` filter above removes all three. + +For a token **still on its curve**, the curve contract holds all unsold supply and should be excluded the same way. +::: + +For circulating supply and market cap, see [Robinhood Token Supply](/docs/blockchain/robinhood/robinhood-token-supply). + +--- + +## Streaming + +Every query on this page runs as a subscription — switch `query` to `subscription` and drop `limit`/`orderBy`. Connect to `wss://streaming.bitquery.io/graphql?token=YOUR_TOKEN` with the `graphql-transport-ws` subprotocol (`connection_init` → `connection_ack` → `subscribe`). See [WebSocket authentication](/docs/authorization/websocket/). + +The three feeds worth running continuously: + +```graphql +# 1. Every new launch, with token + curve + full metadata +subscription { + EVM(network: robinhood) { + Calls(where: {Call: { + To: {in: ["0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e", "0xe33e9e479df8802cb0866d5d05258bec4cf62948"]} + Signature: {SignatureHash: {in: ["f35abbcf", "a72101af", "f85f8e41"]}} + Success: true + }}) { + Block { Time } + Transaction { Hash From } + Call { To Value Input Output } + } + } +} +``` + +```graphql +# 2. Every bonding-curve trade on the network +subscription { + EVM(network: robinhood) { + Events(where: {Log: {Signature: {SignatureHash: {in: [ + "ec36bf571f136799e8dc0b0b8bea4b04d8bd3d43de838aab0d5fc21d4cbfc455", + "8113d738abdcb6b38357e9d53a54a7157861a09031b453651f0fe7fe151f59df" + ]}}}}) { + Block { Time } + Transaction { Hash From } + LogHeader { Address Data } + Log { Signature { SignatureHash } } + } + } +} +``` + +```graphql +# 3. Every graduation, with the token address readable in the payload +subscription { + EVM(network: robinhood) { + Events(where: { + LogHeader: {Address: {is: "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044"}} + Log: {Signature: {SignatureHash: {is: "01bf263a1db1652580721573296e1a1fa70b3d4c87f61d02a69c4e1109d2d573"}}} + }) { + Block { Time } + Transaction { Hash } + LogHeader { Data } + } + } +} +``` + +--- + +## FAQ + +### How do I detect a newly launched Pons token? + +Subscribe to the `Calls` cube on the factory and router and read `Call.Output` for the token and curve addresses. `TokenLaunched` indexes all three of its addresses, so the event alone cannot identify the token. See [Newly launched tokens](#newly-launched-tokens). + +### Why do my Pons trade queries return nothing? + +Almost certainly because the token has not graduated. Bonding-curve trades exist only as `CurveBuy` / `CurveSell` events on the token's own curve contract — `DEXTrades` and `Trading.Trades` have no rows until the v4 pool is created. See [Bonding-curve trades](#bonding-curve-trades). + +### Where do I get a token's name, symbol, image, and socials? + +Name, symbol and decimals come from `Transfer.Currency` on any transfer. The IPFS image, description and social links exist only in the launch call's arguments — ABI-decode `Call.Input`. See [Token metadata](#token-metadata). + +### Why is my effective fee far above 1%? + +The snipe tax. `CurveBuy.fee` bundles the 100 bps base fee with the launch-window penalty, which starts at 9,900 bps and halves down to zero within seconds. `SnipeTaxCharged` isolates the penalty. See [Snipe tax](#snipe-tax). + +### How do I tell a Pons pool from any other Uniswap v4 pool on Robinhood? + +By the `hooks` field: `0xe5e702641ea86f4ae6cc3cdaed2b886f976be044`. There is no `Pons` protocol label in the `Trading` cube, and the v4 PoolManager address is shared by the whole chain. + +### Can I get Pons history older than the realtime window? + +Partly. `SignatureHash` filters, `Topics` filters and the whole `Calls` cube are realtime-only. For older launches use the [archive launch-mint pattern](#launch-mint-archive); for older prices use the `Trading` cube, which is not subject to that limit. + +### Does this page cover Pons V1? + +No. V1 is a separate, still-active protocol with no bonding curve and different event signatures — see the [caution above](#pons-vs-poolstrade). + +--- + +## Next steps + +- [Robinhood Trades API](/docs/blockchain/robinhood/robinhood-trades) — full trade schema for the network +- [Robinhood Calls API](/docs/blockchain/robinhood/robinhood-calls-api) — more on `Call.Input` / `Call.Output` and internal calls +- [Robinhood Meme Coin Launches API](/docs/blockchain/robinhood/robinhood-meme-coin-launches) — compare launchpads side by side +- [Pools.trade API](/docs/blockchain/robinhood/pools-trade-api) — the other major Robinhood Chain launchpad +- [Robinhood Token Holders API](/docs/blockchain/robinhood/robinhood-token-holders-api) — holder distribution queries +- [WebSocket subscriptions](/docs/subscriptions/websockets/) — turn any query above into a live stream diff --git a/sidebars.js b/sidebars.js index 709b8a73..06f33c90 100644 --- a/sidebars.js +++ b/sidebars.js @@ -914,6 +914,7 @@ const sidebars = { "blockchain/robinhood/robinhood-token-holders-api", "blockchain/robinhood/robinhood-meme-coin-launches", "blockchain/robinhood/pools-trade-api", + "blockchain/robinhood/pons-api", "blockchain/robinhood/flap-sh-api", "blockchain/robinhood/bags-fm-api", "blockchain/robinhood/robinhood-token-supply", From b19760df54a2c93e1f5cc1252ae140360f99c446 Mon Sep 17 00:00:00 2001 From: Gaurav Agarwal Date: Tue, 11 Aug 2026 21:52:53 +0530 Subject: [PATCH 2/5] docs: audit fixes for Pons API page - Drop full contract addresses from three h3 headings; they made the right-hand TOC unreadable. Addresses moved to an "Emitter:" line and the headings pinned to stable explicit ids. - Curve trade fee is read from the launch config (curveFeeBps), not a protocol constant. Verified in PonsV2LaunchFactory source; every sampled launch uses config 0, but the page no longer states 100 bps as if it were fixed. Verified while auditing: graduate() and createGraduatedPool() carry no access modifier, so "permissionless" is accurate; and a Pons V1 launch transaction does emit PoolCreated plus Initialize/Mint on a Uniswap V3 WETH pool, confirming the V1 caution box. Co-Authored-By: Claude Opus 5 --- docs/blockchain/robinhood/pons-api.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/blockchain/robinhood/pons-api.md b/docs/blockchain/robinhood/pons-api.md index 15ef2da0..00609c8d 100644 --- a/docs/blockchain/robinhood/pons-api.md +++ b/docs/blockchain/robinhood/pons-api.md @@ -72,7 +72,7 @@ Every launch mints a **fixed 1,000,000,000 supply** straight into its own **bond | Pre-graduation venue | **Pons bonding curve**, one contract per token | | Post-graduation venue | **Uniswap v4** (`Protocol: uniswap_v4`) | | Graduation threshold | **4.2 ETH** for native-quoted launches; a per-asset amount for ERC-20 quotes | -| Curve trade fee | **100 bps** (1%) of the quote leg | +| Curve trade fee | **100 bps** (1%) of the quote leg — from the launch config, not a protocol constant | | Creator tax | set per launch, capped by the factory (`maxCreatorTaxBps`) | | Launch fee | **0.0005 ETH** | | Graduated pool `fee` | `0` — **all fees are taken by the hook**, not by the pool | @@ -168,7 +168,9 @@ The quote asset of any launch is `pairToken`, the first word of the `TokenLaunch The **Indexed** column is what makes Pons awkward — indexed arguments live in the log's topics, and Bitquery does not expose topics as an output field. An event with all its addresses indexed has a payload that tells you nothing about *which* token it concerns. Two mechanisms get around that: the [`Topics` filter](#filtering-by-an-indexed-argument) and the [`Calls` cube](#newly-launched-tokens). -### Factory — `0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e` {#factory-events} +### Factory events {#factory-events} + +Emitter: `0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e` | Event | Indexed | topic0 (`SignatureHash`) | | --- | --- | --- | @@ -180,7 +182,9 @@ The **Indexed** column is what makes Pons awkward — indexed arguments live in | `LaunchForceSwept(address)` *(rare)* | 1 | `52c1a28345695afc7f6b7629133124dec5d61ee745affd65e4fd2a776bc05840` | | `LaunchGraduationRescued(address,address,uint256,uint256)` *(rare)* | 2 | `7017304fdd491394686dce984eac721f0be1a22228346210f16694772bde44ca` | -### Bonding curve — one contract per token +### Bonding curve events {#curve-events} + +Emitter: one contract per token — see [Newly launched tokens](#newly-launched-tokens). | Event | Indexed | topic0 (`SignatureHash`) | | --- | --- | --- | @@ -195,7 +199,9 @@ The **Indexed** column is what makes Pons awkward — indexed arguments live in | `BuybackLocked(uint256,uint256)` *(rare)* | 0 | `5feba9b0d52c92ada4b9c571c2bee52390c54f2947208ab250221e6ee32f12ff` | | `AutoGraduationFailed(address,uint256)` *(rare)* | 1 | `e2cd2f31ebc05ec28640102987f4c8fc5f20e269e1b3aa82577f3f2f0e35c7c6` | -### Meme hook — `0xe5e702641ea86f4ae6cc3cdaed2b886f976be044` +### Meme hook events {#hook-events} + +Emitter: `0xe5e702641ea86f4ae6cc3cdaed2b886f976be044` | Event | Indexed | topic0 (`SignatureHash`) | | --- | --- | --- | @@ -506,7 +512,7 @@ Drop the `LogHeader.Address` filter to get **every curve trade on the network** ### Reading the payload -Both events carry four 32-byte words. `Transaction.From` is the trader; the indexed `buyer`/`seller` and `recipient` are not readable. +Both events carry four 32-byte words. `Transaction.From` is the trader; the indexed `buyer`/`seller` and `recipient` are not readable. The base fee rate comes from the launch's config (`curveFeeBps`) rather than a protocol constant, so derive it from the events rather than assuming 100 bps. | Word | `CurveBuy` | `CurveSell` | | --- | --- | --- | From 1070a331d81471c2217087c5638e2e0ab1697671 Mon Sep 17 00:00:00 2001 From: Gaurav Agarwal Date: Tue, 11 Aug 2026 22:14:36 +0530 Subject: [PATCH 3/5] docs: make Pons queries archive-capable, add Datasets section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The page previously claimed SignatureHash filters, Topics filters and "the entire Calls cube" were realtime-only, and pointed users at the launch-mint transfer as the only route to history. Retesting showed that was wrong on two counts. What actually forces realtime is `Log.Signature.SignatureHash` and `Call.Signature.SignatureHash` — and it forces it whether you FILTER on the field or merely SELECT it, which is the easy trap. The earlier finding was a false attribution: the failing query happened to select SignatureHash while filtering on Topics. Verified working on realtime + archive + combined: - Topics: {includes: [{Hash: …}]}, including topic0, with multiple hashes combined as AND - Call.Input / Call.Output, including Input: {startsWith: [...]}, which accepts a list of selectors So the whole page — the Calls launch feed included — is archive-capable. Changes: - New Datasets section: omitting `dataset` silently gives you realtime, which is the usual cause of "my query returns nothing older than a few days". Includes the per-construct realtime/archive/combined matrix and links to the canonical dataset docs. - All 10 topic0 and selector filters switched to the archive-capable forms, and SignatureHash removed from every selection set. - Launch-mint transfer section reframed: a second, independent route to the same list (and the convenient one for metadata), not the only way to reach history. All 20 GraphQL blocks re-executed on realtime; the 7 converted ones also verified on archive and combined. Build + check-links clean; 48 in-page anchors resolve. Co-Authored-By: Claude Opus 5 --- docs/blockchain/robinhood/pons-api.md | 100 +++++++++++++++++--------- 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/docs/blockchain/robinhood/pons-api.md b/docs/blockchain/robinhood/pons-api.md index 00609c8d..f12979f8 100644 --- a/docs/blockchain/robinhood/pons-api.md +++ b/docs/blockchain/robinhood/pons-api.md @@ -116,6 +116,51 @@ Every query on this page targets **V2 only**. A "Pons launches" feed built from --- +## Datasets {#datasets} + +:::danger Omitting `dataset` gives you realtime — a rolling window, not history +`EVM(network: robinhood)` with no `dataset` argument queries the **realtime** dataset, which holds only a rolling window of recent blocks. This is the single most common reason a Pons query "works" but returns nothing older than a few days, and nothing in the response says which dataset served it. + +If you want history, say so explicitly: + +| Dataset | What it covers | When to use it | +| --- | --- | --- | +| *(omitted)* → `realtime` | Rolling recent window | Live streams, dashboards of the last few hours | +| `dataset: archive` | Full history from the chain's indexing start | Backfills, per-day counts, anything dated | +| `dataset: combined` | Archive merged with the realtime tail | A continuous view from launch day to now; slower | + +```graphql +EVM(network: robinhood, dataset: archive) { ... } +EVM(network: robinhood, dataset: combined) { ... } +``` + +See [Dataset options](/docs/graphql/dataset/options), [archive](/docs/graphql/dataset/archive), [realtime](/docs/graphql/dataset/realtime), [combined](/docs/graphql/dataset/combined), and [data coverage and retention](/docs/graphql/data-coverage-retention). +::: + +**Every query on this page runs on `archive` and `combined`** — Pons V2 history reaches back to the first V2 launch — with two exceptions, both verified: + +| Construct | realtime | archive | combined | +| --- | --- | --- | --- | +| `Topics: {includes: […]}` filter (incl. topic0) | ✅ | ✅ | ✅ | +| `Call.Input` / `Call.Output`, incl. `Input: {startsWith: […]}` | ✅ | ✅ | ✅ | +| `LogHeader.Address` / `LogHeader.Data`, `Log.Signature.Name` | ✅ | ✅ | ✅ | +| `Transfers`, `Holders`, `DEXTrades`, `Trading` | ✅ | ✅ | ✅ | +| **`Log.Signature.SignatureHash` / `Call.Signature.SignatureHash`** | ✅ | ❌ | ❌ | +| **`DEXPoolEvents`, `DEXPoolSlippages`, `TransactionBalances`** | ✅ | ❌ | ❌ | + +:::caution `SignatureHash` breaks archive whether you filter *or* select it +Both of these force realtime, and the second one is easy to miss because the filter looks innocent: + +```graphql +where: { Log: { Signature: { SignatureHash: {is: "…"} } } } # filtering → realtime only +Log { Signature { SignatureHash } } # selecting → realtime only +``` + +On `archive` either one fails with `no candidate table can serve: [Log_Signature_SignatureHash]`; on `combined` it returns `no data available yet to query dataset combined`. **`Topics: {includes: [{Hash: {is: ""}}]}` is the drop-in replacement** and works on all three datasets — so prefer it, and keep `SignatureHash` out of your selection set. That is exactly what the queries below do. +::: + +--- + ## Contract addresses | Role | Address | Notes | @@ -164,7 +209,7 @@ The quote asset of any launch is `pairToken`, the first word of the `TokenLaunch ## Event reference -**Bitquery has no ABI for any Pons V2 contract**, so `Log.Signature.Name` is empty on every event on this page and `Arguments` comes back empty. The events are still fully indexed: filter on `Log.Signature.SignatureHash` (topic0) and read the payload from `LogHeader.Data`. +**Bitquery has no ABI for any Pons V2 contract**, so `Log.Signature.Name` is empty on every event on this page and `Arguments` comes back empty. The events are still fully indexed: filter on the event's **topic0** and read the payload from `LogHeader.Data`. The **Indexed** column is what makes Pons awkward — indexed arguments live in the log's topics, and Bitquery does not expose topics as an output field. An event with all its addresses indexed has a payload that tells you nothing about *which* token it concerns. Two mechanisms get around that: the [`Topics` filter](#filtering-by-an-indexed-argument) and the [`Calls` cube](#newly-launched-tokens). @@ -223,7 +268,7 @@ Every topic0 above was verified two ways: keccak-256 preimage match against the ### Querying a raw event by topic0 -The pattern is the same for every row above — filter on `SignatureHash`, scope with `LogHeader.Address` where the emitter is a fixed contract, and read `LogHeader.Data`: +The pattern is the same for every row above — match the topic0 with `Topics: {includes: […]}`, scope with `LogHeader.Address` where the emitter is a fixed contract, and read `LogHeader.Data`: ```graphql { @@ -233,11 +278,7 @@ The pattern is the same for every row above — filter on `SignatureHash`, scope orderBy: {descending: Block_Time} where: { LogHeader: {Address: {is: "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e"}} - Log: { - Signature: { - SignatureHash: {is: "8d4aad4953d0ca700d468f3753aa14432d1b35b43ec6409f051fb6aa43a89607"} - } - } + Topics: {includes: [{Hash: {is: "8d4aad4953d0ca700d468f3753aa14432d1b35b43ec6409f051fb6aa43a89607"}}]} } ) { Block { Time Number } @@ -248,7 +289,7 @@ The pattern is the same for every row above — filter on `SignatureHash`, scope } ``` -`SignatureHash` values are supplied **without** a `0x` prefix. +Topic0 values work with or without the `0x` prefix. `Log: {Signature: {SignatureHash: {is: "…"}}}` is an equivalent filter, but it pins the query to the realtime dataset — see [Datasets](#datasets). :::caution Scope curve and hook events to an emitter where you can Bonding curves are one contract per token, so a topic0-only filter on `CurveBuy` is the right scope — it captures every curve on the network at once, and `LogHeader.Address` tells you which one. @@ -266,11 +307,7 @@ Topics: {includes: [{Hash: {is: "0x000000000000000000000000 Date: Tue, 11 Aug 2026 22:16:57 +0530 Subject: [PATCH 4/5] =?UTF-8?q?docs:=20pools.trade=20events=20are=20now=20?= =?UTF-8?q?decoded=20=E2=80=94=20correct=20the=20event=20reference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bitquery has since added ABIs for all 13 pools.trade events this page documented as raw, including the entire Crowd Launch (CCA) auction. Re-measured against the live API on 11 Aug 2026. - Event reference: all 15 rows now decoded; topic0 column retained - New "Reading decoded arguments": tuple components flatten into one row each, carrying the tuple's type name and distinguished only by Index (UERC20Metadata, PoolKey, MigratorParameters) - Flag the TokenCreated name collision: the entry contract and the factory emit different signatures under the same name, so Name alone matches both - Token metadata: description / external link / image now read straight from Arguments, replacing the client-side ABI-decoding instructions - Crowd Launch bids and clearing price rewritten onto decoded arguments - Dataset note: SignatureHash remains realtime-only (re-confirmed), and archive coverage of these events is still thin Co-Authored-By: Claude Opus 5 --- docs/blockchain/robinhood/pools-trade-api.md | 147 ++++++++++++------- 1 file changed, 90 insertions(+), 57 deletions(-) diff --git a/docs/blockchain/robinhood/pools-trade-api.md b/docs/blockchain/robinhood/pools-trade-api.md index 10b5f87e..8911c368 100644 --- a/docs/blockchain/robinhood/pools-trade-api.md +++ b/docs/blockchain/robinhood/pools-trade-api.md @@ -124,31 +124,49 @@ pools.trade ran on `0x00004c4c…` from 8 July before `0x0000ffff…` took over ## Event reference -`Log.Signature.Name` is populated only for events Bitquery has an ABI for. On pools.trade, **one launch event is decoded and the rest are raw** — including the entire Crowd Launch auction. Raw events are still fully queryable by their `SignatureHash` (topic0). +**Every pools.trade event is decoded.** `Log.Signature.Name` is populated and `Arguments` returns named, typed values — including the entire Crowd Launch auction, which earlier versions of this page documented as raw. Filtering by topic0 (`SignatureHash`) still works and is still the better choice in one specific case, covered below. | Event | Emitter | Decoded? | topic0 (`SignatureHash`) | | --- | --- | --- | --- | -| `TokenCreated(address)` | entry | ✅ **Yes** — filter by `Name` | `2e2b3f61b70d2d131b2a807371103cc98d51adcaa5e9a8f9c32658ad8426e74e` | -| `Initialize` / `ModifyLiquidity` / `Swap` | v4 PoolManager | ✅ Yes — filter by `Name` | — | -| `TokenDistributed` | entry | ❌ Raw | `67226bacccef969dab310a9e55dc1cf821363658e433fd330344f5cc00c79ac8` | -| `TokenCreated` *(metadata overload)* | factory | ❌ Raw | `4ef8284ecf42d4cd19686572ffd87f630858c82398911e776cb831de35eddbf4` | -| `TokenLaunched` | launchpad | ❌ Raw | `3b3d2bafdcae274a232217e1f80ee4305d3af6aa25c8b14b1681bd68d18042a4` | -| `DistributionInitialized` | launchpad | ❌ Raw | `0afd26d7f0833a451173acef122d058906aa7708ceb6f67ea7471a649d88b44b` | -| `BidSubmitted` | CCA auction | ❌ Raw | `650baad5cd8ca09b8f580be220fa04ce2ba905a041f764b6a3fe2c848eb70540` | -| `ClearingPriceUpdated` | CCA auction | ❌ Raw | `30adbe996d7a69a21fdebcc1f8a46270bf6c22d505a7d872c1ab4767aa707609` | -| `CheckpointUpdated` | CCA auction | ❌ Raw | `f1e4b6d7d0d7c5deb6393a39862d66a2f2ecb034f3283a8a597f9bf0c36f76fa` | -| `TickInitialized` | CCA auction | ❌ Raw | `7fdd20e2dbf90ff60a7d9be5ad62f1ec6d9d9cba8b36174a3839cafd059f0958` | -| `NextActiveTickUpdated` | CCA auction | ❌ Raw | `b9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b0` | -| `AuctionStepRecorded` | CCA auction | ❌ Raw | `6863f2b489f9186bf89231dc73aa0e9836f536b9ddb0f708f74260ed3160f297` | -| `TokensReceived` | CCA auction | ❌ Raw | `468160b6769cb8abc9324bc14fe70ee0ce87f1e92087186c6ae22a964a04c572` | -| `AuctionCreated` | CCA auction factory | ❌ Raw | `7ede475fad18ccf0039f2b956c4d43a8b4ed0853de4daaa8ae25299f331ae3b9` | -| `InitializerCreated` | initializer registry | ❌ Raw | `6d759545eb439f07e70f45431d6339af7a4f1ffef06d43e8ddf47fdb0799708c` | - -Every topic0 above was verified two ways: keccak-256 preimage match against its signature, and live occurrence on-chain (all 15 fired within the last 24 hours as of 6 Aug 2026). - -### Querying a raw event by topic0 - -Raw means Bitquery has no ABI for the event yet — `Arguments` comes back empty and `Log.Signature.Name` is blank. The event is still fully indexed: filter on the topic0 from the table (no `0x` prefix, realtime dataset only — see the note below) and you get every occurrence, with the undecoded payload in `LogHeader.Data`. This example queries `TokenDistributed` — the entry contract's distribution-complete event — but the shape is identical for **any** ❌ Raw row: swap the `SignatureHash` value. +| `TokenCreated(address)` | entry | ✅ Yes | `2e2b3f61b70d2d131b2a807371103cc98d51adcaa5e9a8f9c32658ad8426e74e` | +| `Initialize` / `ModifyLiquidity` / `Swap` | v4 PoolManager | ✅ Yes | — | +| `TokenDistributed` | entry | ✅ Yes | `67226bacccef969dab310a9e55dc1cf821363658e433fd330344f5cc00c79ac8` | +| `TokenCreated` *(metadata overload)* | factory | ✅ Yes | `4ef8284ecf42d4cd19686572ffd87f630858c82398911e776cb831de35eddbf4` | +| `TokenLaunched` | launchpad | ✅ Yes | `3b3d2bafdcae274a232217e1f80ee4305d3af6aa25c8b14b1681bd68d18042a4` | +| `DistributionInitialized` | launchpad | ✅ Yes | `0afd26d7f0833a451173acef122d058906aa7708ceb6f67ea7471a649d88b44b` | +| `BidSubmitted` | CCA auction | ✅ Yes | `650baad5cd8ca09b8f580be220fa04ce2ba905a041f764b6a3fe2c848eb70540` | +| `ClearingPriceUpdated` | CCA auction | ✅ Yes | `30adbe996d7a69a21fdebcc1f8a46270bf6c22d505a7d872c1ab4767aa707609` | +| `CheckpointUpdated` | CCA auction | ✅ Yes | `f1e4b6d7d0d7c5deb6393a39862d66a2f2ecb034f3283a8a597f9bf0c36f76fa` | +| `TickInitialized` | CCA auction | ✅ Yes | `7fdd20e2dbf90ff60a7d9be5ad62f1ec6d9d9cba8b36174a3839cafd059f0958` | +| `NextActiveTickUpdated` | CCA auction | ✅ Yes | `b9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b0` | +| `AuctionStepRecorded` | CCA auction | ✅ Yes | `6863f2b489f9186bf89231dc73aa0e9836f536b9ddb0f708f74260ed3160f297` | +| `TokensReceived` | CCA auction | ✅ Yes | `468160b6769cb8abc9324bc14fe70ee0ce87f1e92087186c6ae22a964a04c572` | +| `AuctionCreated` | CCA auction factory | ✅ Yes | `7ede475fad18ccf0039f2b956c4d43a8b4ed0853de4daaa8ae25299f331ae3b9` | +| `InitializerCreated` | initializer registry | ✅ Yes | `6d759545eb439f07e70f45431d6339af7a4f1ffef06d43e8ddf47fdb0799708c` | + +Both columns were verified live on 11 Aug 2026: every event above returned decoded `Arguments` from the API, and every topic0 matches its signature by keccak-256 preimage. + +### Reading decoded arguments + +Two things about the `Arguments` shape are easy to trip over. + +**Tuple components are flattened, and they all share the tuple's type name.** A struct argument does not arrive as one nested value — it arrives as one row per component, each carrying the *tuple's* name rather than the field's name, distinguished only by `Index`. For the factory's `TokenCreated(address,(string,string,string,bytes))`: + +| `Name` | `Index` | `Type` | Meaning | +| --- | --- | --- | --- | +| `tokenAddress` | 0 | `address` | the new token | +| `UERC20Metadata` | 0 | `string` | description | +| `UERC20Metadata` | 1 | `string` | external / social URL (often empty) | +| `UERC20Metadata` | 2 | `string` | image URI — `ipfs://…` or `https://…` | +| `UERC20Metadata` | 3 | `bytes` | extra payload (usually empty) | + +`TokenLaunched` behaves the same way: `poolId`, `token` and `finalPositionRecipient` are named normally, then five `PoolKey` rows follow — index 0 `currency0`, 1 `currency1`, 2 `fee`, 3 `tickSpacing`, 4 `hooks`. `InitializerCreated` flattens its nested struct into eleven `MigratorParameters` rows. + +**`TokenCreated` is emitted by two different contracts with two different signatures.** The entry contract emits `TokenCreated(address)` and the factory emits the metadata overload `TokenCreated(address,(string,string,string,bytes))`. Filtering on `Name: {is: "TokenCreated"}` alone matches **both**. Disambiguate with `LogHeader.Address`, or filter by the topic0, which is unique per signature. + +### Filtering by topic0 + +Topic0 filtering remains available and is the precise way to pin one exact signature — useful for the overloaded `TokenCreated` above. Supply the hash **without** a `0x` prefix; see the dataset note below for its one limitation. ```graphql { @@ -181,12 +199,12 @@ Raw means Bitquery has no ABI for the event yet — `Arguments` comes back empty Two things to know when reading the result: -- **Indexed arguments live in the log's topics, not in `Data`.** `TokenDistributed(address,address,uint256)` indexes both addresses, so `LogHeader.Data` is a single 32-byte word — the `uint256` amount. To recover the token and recipient, join back through `Transaction.Hash` to the decoded `TokenCreated` event or the mint transfer in the same transaction. -- **Drop the `LogHeader.Address` filter for per-token contracts.** CCA auction events fire from a fresh contract per Crowd Launch (1,200+ live already), so for those the topic0 filter alone is the right scope — it captures every auction at once, and `LogHeader.Address` tells you which auction each row came from. +- **You no longer need `LogHeader.Data` for this event.** `TokenDistributed` now decodes to `tokenAddress`, `strategy` and `amount`, so read `Arguments` instead of hand-parsing the payload. `Data` is still returned if you want the raw bytes. +- **Drop the `LogHeader.Address` filter for per-token contracts.** CCA auction events fire from a fresh contract per Crowd Launch (1,200+ live already), so filtering on the event alone is the right scope — it captures every auction at once, and `LogHeader.Address` tells you which auction each row came from. -### Full signatures for client-side decoding +### Full signatures -The full signatures below (with argument types) are what you need to ABI-decode `LogHeader.Data` client-side, grouped by emitter address for copy-paste. +Now that every event is decoded you rarely need these, but the full signatures are the quickest way to see each event's argument types and tuple layout, grouped by emitter address. ```text # entry contracts — 0x0000ffffbe8efe702c8703ae3477ff5de3d319c0 (current) @@ -219,7 +237,9 @@ b9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b0 NextActiveTick ``` :::note `SignatureHash` filters need the realtime dataset -Filtering by `Log: {Signature: {SignatureHash: …}}` is only served by the **realtime** dataset — `dataset: archive` returns `no archive or API tables found for cube Event`, and `dataset: combined` returns `no data available yet to query dataset combined`. Use `Log.Signature.Name` (decoded events) for archive/combined queries, and omit `dataset` for raw topic0 queries. +Filtering by `Log: {Signature: {SignatureHash: …}}` is served **only** by the realtime dataset. `dataset: archive` returns `no archive or API tables found for cube Event`, and `dataset: combined` returns `no data available yet to query dataset combined`. Re-confirmed 11 Aug 2026 — decoding the ABIs did not change this. + +Filter by `Log.Signature.Name` instead when you need `archive` or `combined`; that path works on all three datasets. Be aware that **archive coverage of these events is still thin** — measured the same day, `TokenLaunched` and `BidSubmitted` returned thousands of realtime rows but zero on archive, while `AuctionCreated` and `TokenDistributed` returned only a handful of July rows. Treat realtime as the source of truth for launchpad and auction history until archive backfills. Note also that `SignatureHash` values are supplied **without** a `0x` prefix. ::: @@ -266,7 +286,7 @@ The decoded `TokenCreated` event on the two entry contracts is the cleanest laun The single argument `token` is the new token's contract address. `Transaction.From` is the creator wallet, and `LogHeader.Address` tells you which entry contract handled the launch. :::note This event is intentionally thin -`TokenCreated(address)` carries **only** the token address — no name, symbol, or image. Those live in the *undecoded* factory event; see [Token metadata](#token-metadata-name-symbol-description-image). +`TokenCreated(address)` carries **only** the token address — no name, symbol, or image. Those live in the factory's separate `TokenCreated` overload; see [Token metadata](#token-metadata-name-symbol-description-image). ::: ### Stream new launches in real time @@ -447,7 +467,9 @@ Metadata splits across two sources. **Name, symbol, decimals, and contract** are `Transaction.From` on the mint is the creator wallet and `Block.Time` is the exact launch time — this works for any pools.trade token regardless of which entry contract or router launched it. -The remaining two fields — the **description** and the **IPFS image URI** that the pools.trade UI renders — exist on-chain only in the factory's `TokenCreated(address, (string,string,string,bytes))` event. Bitquery does not decode it, so read `LogHeader.Data` and ABI-decode client-side. +The remaining fields — the **description**, the **external link**, and the **image URI** that the pools.trade UI renders — exist on-chain only in the factory's `TokenCreated(address,(string,string,string,bytes))` event. That event is now decoded, so you can read them straight out of `Arguments` with no client-side ABI work. + +The filter below pins the factory by address because the entry contract emits a *different* `TokenCreated` under the same name (see [Reading decoded arguments](#reading-decoded-arguments)). ```graphql { @@ -456,16 +478,21 @@ The remaining two fields — the **description** and the **IPFS image URI** that limit: {count: 10} orderBy: {descending: Block_Time} where: { - Log: { - Signature: { - SignatureHash: {is: "4ef8284ecf42d4cd19686572ffd87f630858c82398911e776cb831de35eddbf4"} - } - } + Log: {Signature: {Name: {is: "TokenCreated"}}} + LogHeader: {Address: {is: "0x000000e200088d55c39a11f609e5f667729ad49b"}} } ) { Block { Time } Transaction { Hash } - LogHeader { Address Data } + Arguments { + Name + Index + Type + Value { + ... on EVM_ABI_Address_Value_Arg { address } + ... on EVM_ABI_String_Value_Arg { string } + } + } } } } @@ -473,14 +500,18 @@ The remaining two fields — the **description** and the **IPFS image URI** that [▶ Run this query in the Bitquery IDE](https://ide.bitquery.io/Pools-trade-Token-description-and-image) -`LogHeader.Data` is standard ABI encoding: word 0 is the token address, words 1–5 are offsets into the tuple, and the dynamic `string` sections follow. Decoding a sample yields: +The tuple arrives flattened — `tokenAddress`, then four `UERC20Metadata` rows keyed by `Index`. A live result: ```text -token 0x7a5009db1689992cc56a863b3cbffd763f3d4144 -description "A token launched through Uniswap LiquidityLauncher on Robinhood Chain." -image ipfs://bafkreifh4km3huz6323y3tptlhsn6252q5atgd7zoqurko7kog5bcqs4 +tokenAddress 0x228513f0b584b38415438bd661d7cfab0dbb3077 +UERC20Metadata[0] string "The Stock Impaler" # description +UERC20Metadata[1] string "https://stockimpaler.com/" # external link, often empty +UERC20Metadata[2] string "ipfs://bafkreibersrl3o5pi3vf57d3pk5flqq5bacn…" # image +UERC20Metadata[3] bytes "" # extra, usually empty ``` +The image is not always IPFS — some launches point at an HTTPS CDN URL instead, so treat `UERC20Metadata[2]` as an opaque URI. + --- ## The Uniswap v4 pool behind each launch @@ -853,17 +884,19 @@ The launch transaction also contains the token's mint, the entry contract's `Tok Events( limit: {count: 50} orderBy: {descending: Block_Time} - where: { - Log: { - Signature: { - SignatureHash: {is: "650baad5cd8ca09b8f580be220fa04ce2ba905a041f764b6a3fe2c848eb70540"} - } - } - } + where: {Log: {Signature: {Name: {is: "BidSubmitted"}}}} ) { Block { Time Number } Transaction { Hash From } - LogHeader { Address Data } + LogHeader { Address } + Arguments { + Name + Type + Value { + ... on EVM_ABI_Address_Value_Arg { address } + ... on EVM_ABI_BigInt_Value_Arg { bigInteger } + } + } } } } @@ -871,7 +904,7 @@ The launch transaction also contains the token's mint, the entry contract's `Tok [▶ Run this query in the Bitquery IDE](https://ide.bitquery.io/Pools-trade-Crowd-Launch-bids) -`LogHeader.Address` is the auction contract; `Transaction.From` is the bidder. `BidSubmitted(uint256 auctionId, address bidder, uint256, uint128)` has its first two parameters indexed, so `LogHeader.Data` holds the two remaining numeric fields (amount and tick/quantity). +`BidSubmitted(uint256,address,uint256,uint128)` decodes to four named arguments — `id`, `owner`, `priceQ96` and `amount`. `owner` is the bidder (use it rather than `Transaction.From`, which is whatever contract or router relayed the bid), and `priceQ96` is a Q96 fixed-point price: divide by 2⁹⁶ for a human number. `LogHeader.Address` is the auction contract, which is how you tell the auctions apart — there is no filter needed to span them all. ### Clearing price updates @@ -882,17 +915,17 @@ The launch transaction also contains the token's mint, the entry contract's `Tok limit: {count: 50} orderBy: {descending: Block_Time} where: { - Log: { - Signature: { - SignatureHash: {is: "30adbe996d7a69a21fdebcc1f8a46270bf6c22d505a7d872c1ab4767aa707609"} - } - } + Log: {Signature: {Name: {is: "ClearingPriceUpdated"}}} LogHeader: {Address: {is: "0xD10dc5f79F95E953e710F1eDeBddE0baD2e8fed8"}} } ) { Block { Time } Transaction { Hash } - LogHeader { Address Data } + LogHeader { Address } + Arguments { + Name + Value { ... on EVM_ABI_BigInt_Value_Arg { bigInteger } } + } } } } @@ -900,7 +933,7 @@ The launch transaction also contains the token's mint, the entry contract's `Tok [▶ Run this query in the Bitquery IDE](https://ide.bitquery.io/Pools-trade-Crowd-Launch-clearing-price) -Swap the `SignatureHash` for any row in the [event reference](#event-reference) to follow tick initialization (`TickInitialized`), the moving book edge (`NextActiveTickUpdated`), or auction checkpoints (`CheckpointUpdated`, `AuctionStepRecorded`). +`ClearingPriceUpdated` decodes to `blockNumber` and `clearingPriceQ96`. Drop the `LogHeader.Address` filter to watch every auction at once, or swap the event name for any row in the [event reference](#event-reference) to follow tick initialization (`TickInitialized` → `priceQ96`), the moving book edge (`NextActiveTickUpdated` → `priceQ96`), or auction checkpoints (`CheckpointUpdated` → `blockNumber`, `clearingPriceQ96`, `cumulativeMps`; `AuctionStepRecorded` → `startBlock`, `endBlock`, `mps`). :::note Prices are Q96 fixed-point Clearing, floor, and tick-size prices are **Q96** values. Divide by `2**96` to get a human-readable ratio. @@ -999,11 +1032,11 @@ Because pools.trade tokens trade in **plain Uniswap v4 pools**, they are indexed ### How do I track Crowd Launch bids? -All CCA auction events are undecoded, so filter `Log.Signature.SignatureHash` on `650baad5…` for `BidSubmitted`. Omit `dataset: archive` — `SignatureHash` filtering is realtime-only. +Filter `Log.Signature.Name` on `BidSubmitted` and read `Arguments` — `id`, `owner`, `priceQ96` and `amount` all decode. Each Crowd Launch deploys its own auction contract, so filtering on the event alone spans every live auction at once, with `LogHeader.Address` identifying which one. Stay on realtime: archive currently holds no `BidSubmitted` rows. ### Where do I get a token's name, symbol, and image? -The decoded `TokenCreated(address)` has only the address. Use the mint-transfer query for name/symbol/decimals, or ABI-decode the raw factory event for description and IPFS image — see [Token metadata](#token-metadata-name-symbol-description-image). +The entry contract's `TokenCreated(address)` carries only the address. Use the mint-transfer query for name/symbol/decimals, and the **factory's** `TokenCreated` — now decoded — for description, external link and image; both are in [Token metadata](#token-metadata-name-symbol-description-image). ### Is there a bonding-curve contract to query? From d73e3785904c5c540c3f331899f4446400d61a8a Mon Sep 17 00:00:00 2001 From: Gaurav Agarwal Date: Tue, 11 Aug 2026 22:31:28 +0530 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20audit=20fixes=20=E2=80=94=20correct?= =?UTF-8?q?=20four=20wrong=20queries/claims=20on=20the=20Pons=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full adversarial re-read of the page against the live API. Four real defects, all measured rather than reasoned: 1. The flagship launch feed double-counted. It matched d6a0eef5 (launchTokenFor) alongside f85f8e41 (launchAndBuy) while the caution directly below it warned against exactly that. Measured 200 rows / 143 transactions = 1.4x inflation. Dropped d6a0eef5; now 1.000x. 2. "Swap the factory address for the curve address and the same filter returns that token's history" was false. Curve events index the recipient/account, not the token, so that query returns zero rows. Replaced with a caution explaining where the trick does and does not apply. 3. The graduated-pool query scoped by Transaction.To on the factory, which silently drops graduations triggered by keeper contracts — graduation is permissionless. Measured 33 of 34 over three days. Rescoped onto the `hooks` argument, which returns all 34 and is the semantically correct discriminator. Also archive-capable. 4. The launch-mint section still described the Calls feed as "source of truth within the realtime window" and itself as the "archive-reachable approximation" — stale after the archive rewrite, since both now run on archive. Also: note that Input.startsWith takes a list and the 0x prefix is optional; drop a "100 bps baseline" phrase that contradicted the config-derived fee note; add a tip to check whether Bitquery has since decoded Pons (the sibling pools.trade page just gained ABIs — re-checked, Pons is still fully undecoded). All 20 GraphQL blocks re-executed; build and check-links clean. Co-Authored-By: Claude Opus 5 --- docs/blockchain/robinhood/pons-api.md | 33 ++++++++++++++++++++------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/blockchain/robinhood/pons-api.md b/docs/blockchain/robinhood/pons-api.md index f12979f8..0c84a532 100644 --- a/docs/blockchain/robinhood/pons-api.md +++ b/docs/blockchain/robinhood/pons-api.md @@ -211,6 +211,10 @@ The quote asset of any launch is `pairToken`, the first word of the `TokenLaunch **Bitquery has no ABI for any Pons V2 contract**, so `Log.Signature.Name` is empty on every event on this page and `Arguments` comes back empty. The events are still fully indexed: filter on the event's **topic0** and read the payload from `LogHeader.Data`. +:::tip Check before you build a decoder +ABI coverage gets added over time — the sibling [pools.trade page](/docs/blockchain/robinhood/pools-trade-api) documented raw events until Bitquery decoded them. Run any event query below and look at `Log { Signature { Name } }` and `Arguments`: if they come back populated, skip the manual `LogHeader.Data` decoding on this page and read the arguments directly. +::: + The **Indexed** column is what makes Pons awkward — indexed arguments live in the log's topics, and Bitquery does not expose topics as an output field. An event with all its addresses indexed has a payload that tells you nothing about *which* token it concerns. Two mechanisms get around that: the [`Topics` filter](#filtering-by-an-indexed-argument) and the [`Calls` cube](#newly-launched-tokens). ### Factory events {#factory-events} @@ -347,6 +351,8 @@ The **`Calls` cube solves this completely.** `Call.Output` holds the function's | `a72101af` | `launchToken(params, launchConfigId, pairToken, snipeTaxExemptions)` | `(address token, address curve)` | | `d6a0eef5` | `launchTokenFor(...)` — what the router calls internally | `(address token, address curve)` | +`Input: {startsWith: […]}` accepts a list, and the `0x` prefix on each selector is optional. Do **not** use `Call: {Signature: {SignatureHash: …}}` here — it is the equivalent filter but pins the query to realtime, see [Datasets](#datasets). + ### The complete launch feed ```graphql @@ -361,7 +367,7 @@ The **`Calls` cube solves this completely.** `Call.Output` holds the function's "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e", "0xe33e9e479df8802cb0866d5d05258bec4cf62948" ]} - Input: {startsWith: ["0xf35abbcf", "0xa72101af", "0xd6a0eef5", "0xf85f8e41"]} + Input: {startsWith: ["0xf35abbcf", "0xa72101af", "0xf85f8e41"]} Success: true } } @@ -382,8 +388,10 @@ const token = '0x' + o.slice(24, 64); const curve = '0x' + o.slice(88, 128); ``` -:::caution Filter on `launchTokenFor` and `launchAndBuy`, never both -`launchAndBuy` on the router calls `launchTokenFor` on the factory internally, so a filter matching both selectors returns **two rows for the same launch** — one for the router's outer call, one for the factory's inner call. Either drop `d6a0eef5` (router launches are then attributed to the router call, which is where `Transaction.From` is the real creator), or deduplicate on `Transaction.Hash`. +:::caution Never match `launchTokenFor` and `launchAndBuy` together +`launchAndBuy` on the router calls `launchTokenFor` on the factory internally, so a filter matching both selectors returns **two rows for the same launch** — one for the router's outer call, one for the factory's inner call. Measured over 200 rows, including `d6a0eef5` alongside `f85f8e41` inflates the feed **1.4×**; the three-selector filter above is exactly 1.0×. + +That is why `d6a0eef5` is absent from the query. Router launches are attributed to the router call, which is also where `Transaction.From` is the real creator. If you do need `launchTokenFor` — to catch a launch routed through some other contract — add it and deduplicate on `Transaction.Hash`. ::: ### Stream new launches in real time @@ -482,7 +490,7 @@ The `Calls` feed above already runs on `archive` — just add the argument. But `Transfer.Currency.SmartContract` is the token, `Transfer.Receiver` is its curve, `Transaction.From` is the creator, and `Block.Time` is the launch time. :::caution `Transaction.To` misses indirect launches -This pattern only catches launches where the factory or router is the transaction target. A small share of launches route through third-party contracts or arrive inside contract-creation transactions and carry a different `Transaction.To`. The [`Calls` feed](#the-complete-launch-feed) matches on `Call.To`, so it catches those too — treat it as the source of truth within the realtime window, and the transfer pattern as the archive-reachable approximation. +This pattern only catches launches where the factory or router is the transaction target. A small share of launches route through third-party contracts or arrive inside contract-creation transactions and carry a different `Transaction.To`. The [`Calls` feed](#the-complete-launch-feed) matches on `Call.To`, so it catches those too — and it runs on `archive` as well. Treat the call feed as the source of truth, and this one as the convenient metadata-carrying view. ::: ### Most active token creators @@ -575,7 +583,7 @@ snipeTaxBps(elapsed) = snipeTaxStartBps >> ((elapsed * 14) / snipeTaxSeconds) with integer division, `elapsed` in seconds since the launch transaction, and zero once `elapsed >= snipeTaxSeconds`. At the factory's current settings — `snipeTaxStartBps = 9900`, `snipeTaxSeconds = 3` — that resolves to **9900 bps in the launch second, 618 bps in the next, 19 bps in the next, then zero**. Both settings are owner-mutable; read the current values with `snipeTaxStartBps()` (`0x50e25ac2`) and `snipeTaxSeconds()` (`0x6783774b`) through the `Calls` cube, taking `Call.Output`. -Because `CurveBuy.fee` bundles the base fee and the snipe tax, the snipe portion is what makes an early buy's effective rate jump far above the 100 bps baseline. `SnipeTaxCharged` isolates it: +Because `CurveBuy.fee` bundles the base fee and the snipe tax, the snipe portion is what makes an early buy's effective rate jump far above the launch's base rate. `SnipeTaxCharged` isolates it: ```graphql { @@ -635,7 +643,9 @@ GraduationTokensPermanentlyLocked → 4/49 of supply locked forever PoolGraduated → v4 pool created and seeded ``` -Swap the factory address for the **curve address** and the same filter returns that token's `CreatorFeeRecipientUpdated` and `SnipeTaxExempted` history. +:::caution This works on the factory, not on the curve +The trick only works where the token address is actually one of the log's topics. Factory events index the token, so they match. The **curve's** events do not — `CreatorFeeRecipientUpdated` indexes the two recipient addresses and `SnipeTaxExempted` indexes the exempted account, so filtering a curve address by the token topic returns **zero rows**. Query curve history by the curve's own address plus the event topic0 instead. +::: --- @@ -682,7 +692,7 @@ This is the query to run first when you want a **token set** to feed into the `T ### The graduated Uniswap v4 pool -The PoolManager's `Initialize` **is** decoded, so the `PoolKey` reads without manual decoding — and scoping by `Transaction.To` on the factory isolates Pons graduations: +The PoolManager's `Initialize` **is** decoded, so the `PoolKey` reads without manual decoding. Scope it by the **`hooks` argument** — the Pons hook is what makes a pool a Pons pool: ```graphql { @@ -693,7 +703,10 @@ The PoolManager's `Initialize` **is** decoded, so the `PoolKey` reads without ma where: { LogHeader: {Address: {is: "0x8366a39cc670b4001a1121b8f6a443a643e40951"}} Log: {Signature: {Name: {is: "Initialize"}}} - Transaction: {To: {is: "0x7ed598bcef8bd9edd8c97a195c6d13f40801ec7e"}} + Arguments: {includes: { + Name: {is: "hooks"} + Value: {Address: {is: "0xe5e702641ea86f4ae6cc3cdaed2b886f976be044"}} + }} } ) { Block { Time } @@ -715,6 +728,10 @@ The PoolManager's `Initialize` **is** decoded, so the `PoolKey` reads without ma Returns `id` (the **PoolId** you need for [liquidity and slippage](#pool-liquidity-slippage-and-balance-changes)), `currency0`, `currency1`, `fee`, `tickSpacing`, `hooks`, `sqrtPriceX96` and `tick`. Every Pons pool comes back with `fee: 0`, `tickSpacing: 200` and `hooks: 0xe5e702641ea86f4ae6cc3cdaed2b886f976be044`. +:::note Filter on the hook, not on `Transaction.To` +Scoping this query with `Transaction: {To: {is: ""}}` looks equivalent and is not: graduation is [permissionless](#graduation), so a keeper contract can call `createGraduatedPool` and carry a different `Transaction.To`. Measured over three days, the `Transaction.To` form returned 33 of 34 graduations while the `hooks` argument filter returned all 34. The hook is on every Pons pool regardless of who triggered it. +::: + :::note Why `fee` is zero Trading fees on a graduated Pons pool are charged by the **hook**, not by the pool. The pool's own LP fee is `0`, and `HookFeeCollected` on `0xe5e70264…` is where the fee and the creator tax actually show up. Reading `fee` from the `PoolKey` and calling it the trading cost will understate it to zero. :::