Developer Integration
Watergun developer reference for GraphQL subgraphs, contract interfaces, and routing primitives.
Everything you need to quote prices, execute swaps, manage LP positions, and build on the Watergun DEX. For $9MM token-specific information and updates, visit 9mm.pro. The former tokenomics, claim, and holder revenue-sharing programs are sunset.
Watergun's DEX and routing interfaces follow familiar Uniswap V2 / V3 and 0x patterns. Verify compatibility against the specific deployed contract or API before integrating. The PulseChain DEX remains with 9mm Pro, not Watergun.
Stack Overview
- Your appReads indexed data and calls on-chain contracts.
- Hosted servicesGraphQL subgraphs, Watergun Swap routing API, and block explorer data.
- On-chain primitivesV2, V3, SmartRouter, UniversalRouter, Permit2, and StableSwap.
| Layer | What it gives you | Where |
|---|---|---|
| Subgraphs | Historical pool / swap / LP event data, GraphQL | deployments repo |
| Block explorer | Transaction lookups, contract verification | 9mm-scan on GitHub |
| V2 contracts | Constant-product AMM (Factory + Router) | On-chain |
| V3 contracts | Concentrated liquidity (Factory + Quoter + SmartRouter + NFPM) | On-chain |
| V4 / Universal Router | Unified routing, Permit2 approvals | On-chain |
| Watergun Swap aggregator | Multi-DEX best-execution routing | swap.watergun.pro |
GraphQL Subgraphs
Per-chain subgraphs index V2 and V3 pools, swaps, liquidity events, and historical metrics. We run our own indexer stack — 9mm-exchange/graph-node — and publish subgraph definitions at 9mm-exchange/subgraphs.
Endpoint URLs
Per-chain endpoints are published in our deployments repo as JSON files (kept current alongside contract addresses):
Base subgraphs
V2 + V3 GraphQL endpoints
BNB Chain subgraphs
V2 + V3 GraphQL endpoints
Ethereum subgraphs
V2 + V3 GraphQL endpoints
Sonic subgraphs
V2 + V3 GraphQL endpoints
Schema
The V2 and V3 subgraphs expose the standard Uniswap-compatible entities — Factory, Pool / Pair, Token, Swap, Mint, Burn, Bundle, PoolDayData, TokenDayData, and so on. If you've queried Uniswap or PancakeSwap subgraphs before, the same queries will work here.
Common Queries
{
pools(first: 20, orderBy: totalValueLockedUSD, orderDirection: desc) {
id
token0 { symbol name decimals }
token1 { symbol name decimals }
feeTier
liquidity
totalValueLockedUSD
volumeUSD
}
}Contract Reference
V2 — Constant Product AMM
The V2 layer is the simplest — a Factory that creates Pair contracts (each is a liquidity pool), and a Router that batches swap / add / remove actions into a single call.
Prop
Type
Key factory method:
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);Key router methods:
function getAmountsOut(uint amountIn, address[] calldata path)
external view returns (uint[] memory amounts);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);Quote example:
// Get USDC output for 1 WETH
IV2Router router = IV2Router(V2_ROUTER_ADDRESS);
address[] memory path = new address[](2);
path[0] = WETH;
path[1] = USDC;
uint[] memory amounts = router.getAmountsOut(1 ether, path);
// amounts[1] = USDC you'd receiveV3 — Concentrated Liquidity
V3 pools take three parameters: token0, token1, and a fee tier. The Watergun V3 fee tiers documented here are:
| Fee tier | fee (uint24) | Typical use |
|---|---|---|
| 0.01% | 100 | Stablecoin pairs |
| 0.05% | 500 | Low-volatility pairs |
| 0.25% | 2500 | Standard pairs |
| 1% | 10000 | Volatile / exotic |
| 2% | 20000 | Highly volatile |
Key V3 contracts:
Prop
Type
Quote a single-pool V3 swap (off-chain):
IQuoterV2 quoter = IQuoterV2(QUOTER_V2_ADDRESS);
(uint256 amountOut, , , ) = quoter.quoteExactInputSingle(
IQuoterV2.QuoteExactInputSingleParams({
tokenIn: WETH,
tokenOut: USDC,
amountIn: 1 ether,
fee: 2500, // 0.25% tier
sqrtPriceLimitX96: 0
})
);import { ethers } from 'ethers';
import QuoterAbi from './abi/QuoterV2.json';
const quoter = new ethers.Contract(QUOTER_V2_ADDRESS, QuoterAbi, provider);
const params = {
tokenIn: WETH,
tokenOut: USDC,
amountIn: ethers.parseEther('1'),
fee: 2500,
sqrtPriceLimitX96: 0n,
};
const [amountOut] = await quoter.quoteExactInputSingle.staticCall(params);
console.log('1 WETH →', ethers.formatUnits(amountOut, 6), 'USDC');Execute a V3 swap via SwapRouter:
ISwapRouter router = ISwapRouter(SWAP_ROUTER_ADDRESS);
IERC20(WETH).approve(SWAP_ROUTER_ADDRESS, 1 ether);
router.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: USDC,
fee: 2500,
recipient: msg.sender,
deadline: block.timestamp + 60,
amountIn: 1 ether,
amountOutMinimum: minUSDCOut,
sqrtPriceLimitX96: 0
})
);SmartRouter — Mixed V2/V3 Routing
For multi-hop routes that may span V2 and V3 pools, use SmartRouter. It accepts encoded paths and picks the optimal execution strategy across both versions in a single transaction.
// Multi-hop V3 encoded path: tokenIn -> fee -> tokenMid -> fee -> tokenOut
bytes memory path = abi.encodePacked(
WETH, uint24(500), USDC, uint24(100), USDT
);
ISmartRouter(SMART_ROUTER_ADDRESS).exactInput(
ISmartRouter.ExactInputParams({
path: path,
recipient: msg.sender,
deadline: block.timestamp + 60,
amountIn: 1 ether,
amountOutMinimum: minOut
})
);SmartRouter addresses per chain live in Deployments.
V4 — UniversalRouter + Permit2
V4 deployments (BNB Chain and Sonic at the time of writing) use the UniversalRouter plus Permit2 for gasless approvals. The router accepts a command-encoded payload and executes arbitrary sequences of V2 / V3 swaps, LP actions, and Permit2 approvals in a single transaction.
- UniversalRouter source: 9mm-exchange/infinity-universal-router
- Permit2 source: 9mm-exchange/permit2
The command format is compatible with Uniswap's Universal Router spec — check the source repos for the full command set and encoding helpers.
StableSwap (BNB Chain)
BNB Chain additionally deploys a StableSwap layer for pegged pairs (stablecoin-to-stablecoin, LSTs, etc.). StableSwap uses a different AMM curve optimized for low-slippage trades between similarly-priced assets. Contracts:
LPFactory,StableFactory,SwapTwoPoolDeployer,SwapThreePoolDeployer,TwoPoolInfo,ThreePoolInfo,StableInfo
Addresses: Deployments → BNB Chain.
Watergun Swap API
The Watergun Swap interface is at swap.watergun.pro; the former 9x product name and 9x.9mm.pro address are retired. Do not rely on older screenshots or source counts as a current quote or venue list.
The Watergun Swap aggregator routes trades across available liquidity sources, including external venues. On PulseChain, the 9mm Pro DEX is a separate venue that Watergun Swap may route through. The API follows 0x-v1 conventions; verify individual response and approval behavior before treating it as a drop-in replacement.
Base URLs (per chain)
| Chain | Base URL |
|---|---|
| PulseChain (default) | https://api.9mm.pro |
| Base | https://api-base.9mm.pro |
| Sonic | https://api-sonic.9mm.pro |
| Additional chains | Published in the deployments repo as new chains come online |
There is also a general-purpose price endpoint at https://price-api.9mm.pro/api/price used by the Watergun Swap front-end for passive price polling.
Endpoints
All endpoints mirror the 0x Protocol Swap API v1 — same routes, same query-parameter names, same response shape.
| Method | Path | Purpose |
|---|---|---|
GET | /swap/v1/sources | List every liquidity source the aggregator routes across |
GET | /swap/v1/price | Indicative price — no calldata, no approval prompts, safe for polling |
GET | /swap/v1/quote | Executable quote with to + data calldata ready for eth_sendTransaction |
The query-parameter and response schema is identical to 0x v1. The historical 0x v1 Swap API reference — parameter names, allowed values, error codes — is the best external spec for Watergun Swap. The 0x-v1-era documentation is maintained (archived) at docs.0x.org and in the 0xProject/0x-api repository.
Query Parameters
| Param | Required | Type | Notes |
|---|---|---|---|
sellToken | yes | address | ERC-20 contract of the token you're selling |
buyToken | yes | address | ERC-20 contract of the token you're buying |
sellAmount | one of | uint | Amount to sell, in base units (wei / smallest unit) |
buyAmount | one of | uint | Amount to receive, in base units |
slippagePercentage | no | float | E.g. 0.01 for 1% — defaults to 1% if omitted |
takerAddress | no | address | Required if you want gas estimation in the response |
excludedSources | no | CSV | Comma-separated list from /swap/v1/sources |
includedSources | no | CSV | Force routing only through specific sources |
Exactly one of sellAmount or buyAmount must be provided (just like 0x v1).
Request Patterns
Get the liquidity-source list — scripts your aggregator can use to track what Watergun Swap routes through:
curl 'https://api.9mm.pro/swap/v1/sources'The response lists currently available sources. Do not hardcode a snapshot of venue names or assume a source is available on every network.
For an indicative price, request /swap/v1/price with the selected network's sellToken, buyToken, and either sellAmount or buyAmount. For an executable quote, request /swap/v1/quote with the same token parameters and a real takerAddress. Inspect the returned to, data, value, allowanceTarget, and gas estimate before presenting a transaction to the user. For $9MM token-specific addresses or trading guidance, visit 9mm.pro.
JavaScript Example
async function quoteSwap({ sellToken, buyToken, sellAmount, taker }) {
const params = new URLSearchParams({
sellToken, buyToken, sellAmount, takerAddress: taker
});
const r = await fetch(`https://api.9mm.pro/swap/v1/quote?${params}`);
if (!r.ok) throw new Error(`Watergun Swap error: ${r.status}`);
return r.json();
}
// Pass verified token addresses and an actual wallet address for the selected network.
// Review the returned allowanceTarget and transaction data before signing.Rate Limits
No published rate limit. The API is aggressively cached and serves cheaply — but please self-throttle. Sustained high-volume traffic from a single IP will be rate-limited.
0x v1 Compatibility Notes
Because Watergun Swap is a 0x Protocol v1 fork:
- Drop-in for 0x v1 clients — swap your base URL from
api.0x.orgtoapi.9mm.proand existing 0x v1 integrations work unchanged - Quote / price response schemas are identical to 0x v1
- Source naming matches 0x conventions (e.g.,
Uniswap_V3,SushiSwap,Balancer_V2) - No Permit2 or AllowanceHolder patterns — those are 0x v2 features. Watergun Swap uses the v1 approval flow: approve
allowanceTarget, then execute the quote - No gasless / meta-transaction API — Watergun Swap v1 is direct-execution only
The archived 0x v1 documentation (docs.0x.org and the 0xProject/0x-api repo) is an authoritative external reference for every parameter and error code — Watergun Swap implements the same contract.
Integrated Liquidity Sources
Source availability changes by network and time. Query /swap/v1/sources against the relevant API base URL for the current list. NineMM_V2 and NineMM_V3, if returned for PulseChain, refer to the 9mm Pro PulseChain DEX rather than a Watergun DEX deployment.
Common Integration Patterns
Discover pools
Use the V3 Factory's getPool(tokenA, tokenB, fee) to find a specific pool, or subscribe to PoolCreated events via subgraph. For V2, use Factory.getPair(tokenA, tokenB).
Quote a swap
For V2: call Router.getAmountsOut(amountIn, path) — free and always works.
For V3: QuoterV2.quoteExactInputSingle(...) in a staticCall (it mutates state internally; never send it as a real transaction).
For optimal routing: hit Watergun Swap for aggregated quotes across our own pools and external sources.
Approve tokens
Call IERC20(token).approve(router, amount) before any swap. On V4, use Permit2 to skip approvals via a signature — one-time permit, infinite reuse.
Execute
Send the swap transaction. Always include a deadline (e.g., block.timestamp + 60 for 1-minute expiry) and amountOutMinimum (slippage protection). A swap with 0 slippage protection is a free lunch for MEV bots.
Watch results
Subscribe to the pool's Swap event, or query the subgraph's swaps entity. The swap hash is returned by the transaction — surface it to your user with a link to 9mm-scan or the chain's canonical explorer.
Tips and Gotchas
Useful Repositories
9mm-exchange/deployments
Canonical contract addresses per chain, per version. Start here.
9mm-exchange/indexer-api
Python backend powering index-api.9mm.pro
9mm-exchange/graph-node
The Graph Protocol indexer we run for subgraphs
9mm-exchange/subgraphs
Subgraph definitions for V2 / V3 pools, swaps, LP events
9mm-exchange/infinity-universal-router
V4 Universal Router (Permit2-enabled)
9mm-exchange/permit2
Next-gen token approvals for V4
9mm-exchange/9mm-scan
Legacy Blockscout-based explorer repository
Help & Feedback
- Telegram: t.me/watergunpro — fastest path to dev support
- GitHub issues: github.com/9mm-exchange — open on the relevant repo
If you ship a Watergun integration, let us know — we'll add you to Partners & Integrations. For $9MM token integrations, start at 9mm.pro.
Related
- Deployments — every contract address, per chain, per version
- V2 & V3 DEX — protocol mechanics
- Partners & Integrations — ecosystem routing and integrations