Agent skill package for Topaz Dex on BNB Chain, Robinhood Chain, Base, Ethereum and Arc. Covers v2/Slipstream markets, chain-specific deployments and ABIs, swaps, liquidity, gauges, BNB veTOPAZ, xTOPAZ entry/redemption and bridging, spoke voting, rewards, analytics and website navigation.
Everything here targets the live networks only; there are no testnet deployments. Start with the xTOPAZ plain-language FAQ, multichain architecture, five-chain addresses and ABIs, builder examples and website navigation. Every read/write helper, builder and operator CLI takes an explicit chain (chainId / --chain); veTOPAZ locks, relays and rebases are BNB-only by protocol. Governance information is linked; privileged changes are not ordinary user actions.
Current version: 3.5.0 — see CHANGELOG.md. Machine-readable manifest: skill.json.
The Topaz website auto-mirrors this version: https://topazdex.com/agents, https://topazdex.com/skill.md, and https://topazdex.com/skill.json all pull from main on a 1-hour ISR cycle. Pushing a new version here propagates without any website-side changes — see docs/RELEASING.md for details.
- Website: https://topazdex.com
- Docs: https://www.topazdex.com/docs
- Agents page: https://topazdex.com/agents (canonical install / discovery page)
- X (Twitter): https://x.com/TopazDex
- Telegram: https://t.me/TopazDex
- GitHub: https://github.com/topazdex
- Brand assets: https://github.com/topazdex/assets — full catalog in
references/brand.md; typed constants inscripts/src/config/brand.ts. - Topaz ID: https://id.topazdex.com — the ecosystem account/identity layer.
- Topaz ID Connect (NPM): https://www.npmjs.com/package/@topazdex/id-connect — "Connect with Topaz ID" for partner dApps; guide in
developers/topaz-id-connect.md. - Topaz ID Connect demo: https://github.com/topazdex/topaz-id-connect-demo
This repo is the skill package. Any agent that can read a SKILL.md plus a directory of references and scripts can consume it — Claude Code, Codex, OpenCode, Hermes, custom in-house agents, or a human at a terminal.
The fastest path is to clone the repo into wherever your agent looks for skills, then run the validator and smoke check.
git clone https://github.com/topazdex/agent-skill.git <dest>
cd <dest>/scripts
cp .env.example .env # RPC overrides optional; PRIVATE_KEY only needed for writes
yarn install --immutable
yarn validate && yarn smokeReplace <dest> with whichever directory your agent reads skills from. Common conventions:
| Runtime | Conventional location |
|---|---|
| Claude Code (user-wide) | ~/.claude/skills/topaz |
| Claude Code (project-local) | <your-project>/.claude/skills/topaz |
| Hermes | ~/.hermes/skills/defi/topaz |
| OpenCode | ~/.config/opencode/skills/topaz |
| Codex / generic CLI agent | anywhere — point your agent at it explicitly |
| Standalone / inspecting from a terminal | anywhere |
If your agent has no notion of skills at all, just clone the repo somewhere and tell the agent to read SKILL.md plus the linked references/ and examples/ files when working on Topaz.
install.sh runs the clone + dependency install + validator for you. With no arguments it auto-detects an existing agent skills directory on the host and installs there. Detection order (alphabetical, no runtime favored):
~/.claude/skills/exists → installs to~/.claude/skills/topaz~/.config/opencode/skills/exists → installs to~/.config/opencode/skills/topaz~/.hermes/skills/exists → installs to~/.hermes/skills/defi/topaz- None of the above → falls back to
~/.local/share/topaz-skilland prints a notice asking you to point your agent at that path
The installer prints its chosen destination and the reason (e.g. auto-detected ~/.claude/skills/ or fallback (no recognized agent skill dir found)) before cloning, so the path is never a surprise.
# auto-detect destination
curl -fsSL https://raw.githubusercontent.com/topazdex/agent-skill/main/install.sh | bash
# pick your own
curl -fsSL https://raw.githubusercontent.com/topazdex/agent-skill/main/install.sh \
| bash -s -- ~/some/other/pathgit clone https://github.com/topazdex/agent-skill.git <dest>
git -C <dest> checkout v2.2.0Or fetch a release's frozen artifacts directly (auto-redirects to the latest tag — no need to know the version number):
curl -fsSL https://github.com/topazdex/agent-skill/releases/latest/download/skill.json
curl -fsSL https://github.com/topazdex/agent-skill/releases/latest/download/SKILL.md# convenience wrapper — runs git pull, refreshes deps, re-runs validate/smoke
bash <dest>/update.sh
# or just:
git -C <dest> pull --ff-onlyCheck whether an update is available without applying it:
cd <dest>
bash tools/check_update.sh # exit 0 = up to date, 10 = update availablecd <dest>/scripts
yarn validate # static checks: frontmatter, links, addresses, checksums, manifest parity, ...
yarn build # type-check (tsc --noEmit)
yarn test # 213 unit tests (vitest, no RPC)
yarn smoke # live read against BNB Chain (public RPC by default; BSC_RPC_URL overrides)CI runs validate + build + test on every PR. See .github/workflows/validate.yml.
Releases are one command:
cd scripts && yarn release patch --apply # or minor / major / x.y.zyarn release bumps the version in SKILL.md, skill.json, README.md, and CHANGELOG.md atomically, runs the full validation suite, commits, tags, and pushes. GitHub Actions (.github/workflows/release.yml) then re-validates, creates the GitHub Release with notes extracted from CHANGELOG.md, and attaches skill.json + SKILL.md as release assets (so releases/latest/download/... URLs always resolve to the newest tag).
The Topaz website auto-mirrors anything that lands on main via Next.js ISR with a 1-hour cache — no handshake step in this workflow. Full release flow + propagation details: docs/RELEASING.md.
- For agents: start at
SKILL.md, then drill intoreferences/*.mdandexamples/*.mdas needed. - For developers: start at
developers/DEVELOPERS.mdfor app, SDK, calldata, dashboard, subgraph, and frontend integration guidance. - For partner wallet/account integrations: start at
developers/topaz-id-connect.mdfor@topazdex/id-connecton all five chains, "Connect with Topaz ID", Topaz ID profile display, and demo-app guidance. - For humans doing ops: address tables below, deeper docs under
references/, runnable code underscripts/.
| Contract | Address |
|---|---|
| TOPAZ (governance token, ERC20) | 0xdf002282C1474C9592780618Adda7EaA99998Abd |
| WBNB | 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c |
| VotingEscrow (veTOPAZ, ERC721) | 0xe951aC65EFE86682311ab0d8995E7A58750c5eB3 |
| Voter | 0x2F80F810a114223AC69E34E84E735CaD515dAD67 |
| Minter | 0x606794d37991A426a189fD9FA8664D339A77f8ae |
| RewardsDistributor (rebase) | 0x85e15e7Ad4f20d5ca3A1104B1c2CcE72f5F683dB |
| PoolFactory (v2) | 0x65E6cD0eF5D3467030103cf3d433034E570b5784 |
| Pool implementation | 0xdC942D8e37cC20BCf9aD1Fe0111eE6c5908f3678 |
| Router (v2) | 0x1E98c8226e7d452e1888e3d3d2F929346321c6c3 |
| GaugeFactory | 0xFc080D1EcD7c332022cebf942AEb62d5E1d4Cb08 |
| VotingRewardsFactory | 0x4C303f7af7b8b05226440e4e12FF9a82F513716c |
| ManagedRewardsFactory | 0xe4b23F13b24232C1E68AD0575191216152AA9480 |
| FactoryRegistry | 0x268d1C8a538Ecf6628838C11d581e1EABD13D6A4 |
| Forwarder (ERC-2771) | 0xE79EB7c4D06ff38e6483921DE8e85A37eC7c731b |
| VeArtProxy | 0x9612305fe63DFb84Da8f6d6261169F6B85026601 |
| AirdropDistributor | 0x7B1d8745079C85af80Ff7A7eA7C2C4769Eab5348 |
| BalanceLogicLibrary (linked library) | 0xeF6724ad68Fd2f8526765e08afa6627850c8a589 |
| DelegationLogicLibrary (linked library) | 0xCb24e31896d7476EFB7B76A366566cfbcf375033 |
| Contract | Address |
|---|---|
| CLFactory | 0x73DC984D9490286E735548f61dfCCec67Af82ed9 |
| CLPool implementation | 0x18e68051d1b1fB44cb539cA4436F112D28577AF7 |
| NonfungiblePositionManager (NFT positions) | 0xf8c30c3C362941C23025f2eA30B066A73C982f63 |
| SwapRouter (v3) | 0x9B63CA87919617d042A89663492dB3c8686e0CaE |
| QuoterV2 | 0x7CCB89bB9BdEF68688F39a2c22d249fD1D9759f1 |
| MixedRouteQuoterV1 (v2+v3 routes) | 0x47c3570b90e7234FE695Ad5F1bE69E21fe1a9ee2 |
| CLGaugeFactory | 0xeD2ED418f104E18B1D11eA5C26236A1caa675839 |
| CLGauge implementation | 0xc2f777a2e9f54f195212a5a2d394399252958b97 |
| NonfungibleTokenPositionDescriptor | 0x239BD25E86e4A3B931B1C6Cf7849C27cA9f0498A |
| NonfungibleTokenPositionDescriptor_V1 (legacy) | 0x81aCc35240D19948a56b8b68BcC8706F90baBAb5 |
| NFTDescriptor (library) | 0x50f9756f631266686b9A7EBDF55998dB3dA5ca0a |
| NFTSVG (library) | 0x21C9257dFCdf04154D34dF5A2204B9402Ef31d9a |
| CustomSwapFeeModule | 0xA0462a52af4f8cbF7766Efbba75355B30b6BCCe2 |
| CustomUnstakedFeeModule | 0x3bad7F96cd1b51CE86e12C42541Ac7d559A78582 |
| DynamicSwapFeeModule | 0x656cf5d2f1A70177E011e2c27DeafBeE4C7B0541 |
| PositionBurnHelper | 0x2764db7bca0ccf98a1611f36879ebffd06ffc02b |
Automated reward managers for managed veTOPAZ (mveTOPAZ). See references/relays.md.
| Contract | Address |
|---|---|
| RelayFactoryRegistry | 0x987097eF2fBd740436166f49700a40ac5eD49FE4 |
| AutoCompounderFactory | 0x717bB82888F103A1Ff8E07A0f96aD6497744feeA |
| CompoundConverterFactory | 0x64FaeF44D4b9bF1AbeF56878D0188084355fd5Ad |
| Optimizer | 0x62B3cea3C6028029E56A880E71b659aF523F06B6 |
| OptimizerRegistry | 0x70008f088e60DE590ca63F93814692503e96Fcbd |
| KeeperRegistry | 0xDB93DCfd7a560fB0757857787b6B3c2dBF6E56aA |
| veTOPAZ Maxi (AutoCompounder, mTokenId 3083) | 0xC3b3d7037DA1216A1770b3aC5cB8e2D4241AF251 |
| Reward & Distribute (CompoundConverter, mTokenId 3087) | 0xb30d44B5E6Ab16494EA2B8455BB430926A935b84 |
In this skill, addresses are canonical in scripts/src/config/addresses.ts and mirrored to references/addresses.md and the table above. The validator (yarn validate in scripts/) enforces parity across the three.
The public multichain API at https://api.topazdex.com/v1 (contract: https://api.topazdex.com/openapi.json) is the primary source for pools, prices, gauges, votes, epochs, incentives, account portfolios, xTOPAZ and bridge data on all five chains — see references/analytics-multichain.md. yarn tsx src/cli/stats.ts v1 <path> and fetchV1 in scripts/src/lib/topazApi.ts call it. Every current endpoint begins with /v1/; the /api/stats routes are deprecated, BNB-only and kept for backward compatibility (see references/analytics-stats-api.md).
v2: https://api.goldsky.com/api/public/project_cmgzljqwl006c5np2gnao4li4/subgraphs/topaz-v2/prod/gn
v3: https://api.goldsky.com/api/public/project_cmgzljqwl006c5np2gnao4li4/subgraphs/topaz-v3/prod/gn
ve: https://api.goldsky.com/api/public/project_cmgzljqwl006c5np2gnao4li4/subgraphs/topaz-ve/prod/gn
BNB only; the ve graph indexes locks, votes, bribes, epochs, gauge stakes, relays and xTOPAZ hub state. Spoke chains are served through /v1. Entity catalogs and example queries: references/analytics-subgraph.md.
Topaz is two pool stacks (v2 and v3) sharing one ve(3,3) governance layer.
┌─────────────────────────────┐
│ TOPAZ ERC20 (emissions) │
└──────────────┬──────────────┘
│ mint weekly
┌──────────────▼──────────────┐
│ Minter │
└──────┬───────────────┬──────┘
60% to Voter │ │ 40% to RewardsDistributor (rebase)
┌──────▼──────┐ ┌──────▼──────┐
│ Voter │ │ RewardsDistributor │ → claimed by veTOPAZ holders
└──────┬──────┘ └─────────────┘
│ distribute() per-epoch (per pool weight)
┌──────────────────┴──────────────────┐
│ │
┌───────▼────────┐ ┌─────────▼────────┐
│ Gauge (v2) │ │ CLGauge (v3) │
│ stake LP ERC20│ │ stake NFT pos │
└───────┬────────┘ └─────────┬────────┘
│ │
┌───────▼────────┐ ┌─────────▼────────┐
│ Pool (v2) │ │ CLPool (v3) │
│ xy=k / stable │ │ concentrated liq │
└────────────────┘ └──────────────────┘
Voting:
veTOPAZ holders ─vote()─▶ Voter ─_deposit()─▶ FeesVotingReward[gauge]
└_deposit()─▶ BribeVotingReward[gauge]
Pool trading fees ─claimFees()─▶ FeesVotingReward (distributed to voters)
External bribers ─notifyRewardAmount(token, amt)─▶ BribeVotingReward (paid to voters)
- Pools generate trading fees. Fees that accrue to gauges flow into
FeesVotingReward(one per gauge, mapped viaVoter.gaugeToFees(gauge)). - Gauges receive TOPAZ emissions proportional to vote weight; LPs stake to earn emissions.
- Voters allocate veTOPAZ weight across gauges, earning a share of that gauge's trading fees + any bribes.
- Bribers add reward tokens to
BribeVotingReward(mapped viaVoter.gaugeToBribe(gauge)) to attract votes. - veTOPAZ holders also receive a weekly rebase (anti-dilution) via
RewardsDistributor.claim(tokenId).
Epochs are weekly, starting Thursday 00:00:00 UTC. Voting window opens at +1h and closes at the next epoch boundary -1h. See references/epoch-timing.md.
Relays (managed veTOPAZ). Depositors can hand a veTOPAZ lock to a Relay (Voter.depositManaged) that aggregates voting power into a managed veNFT and auto-claims/swaps/votes/compounds each epoch. Two are live: veTOPAZ Maxi (compounds into TOPAZ in-place) and Reward & Distribute (also streams USDT to depositors). See references/relays.md.
topaz-skill/
├── README.md # This file
├── SKILL.md # Agent entry (frontmatter + nav)
├── references/ # Topic docs (loaded on demand)
│ ├── addresses.md
│ ├── tokens.md
│ ├── epoch-timing.md
│ ├── swapping-{v2,v3,mixed}.md
│ ├── liquidity-{v2,v3}.md
│ ├── gauges.md
│ ├── ve-locks.md
│ ├── voting.md
│ ├── rewards-claiming.md
│ ├── bribes-deposit.md
│ ├── relays.md # managed veTOPAZ (mveTOPAZ) reward automation
│ ├── auto-manage.md # Auto Manage (ALM) vaults: discovery, vault reads, managed-cl-position
│ ├── analytics-{multichain,stats-api,subgraph,onchain}.md
│ ├── apr-calculations.md
│ ├── pitfalls.md
│ └── abis/ # JSON ABIs for ethers/web3
├── developers/ # Builder guides: app integration, calldata, subgraphs, dashboards
├── sdk/ # SDK layer notes; public exports currently live under scripts/src
├── examples/ # Narrative walkthroughs
└── scripts/ # TypeScript + ethers v6 helpers
├── package.json
└── src/
├── config/ # addresses, deployments (five-chain catalog), chain, tokens, relays
├── lib/ # client, contracts, chainOption, multichain, topazRouting, topazSwap, txBuilders, erc20, topazApi, subgraph, tickMath, path, pricing, epoch, relayBuilders
├── read/ # quotes, pools, gauges, locks, votes, spokePositions, claimable, apr, relays, ...
├── write/ # swap, liquidity, gauge, lock, vote, claim, bribe, relay, spokePosition
└── cli/ # `yarn tsx src/cli/<cmd>.ts ...` entry points
cd scripts
cp .env.example .env
# optional: BSC_RPC_URL / TOPAZ_RPC_<chainId> RPC overrides; PRIVATE_KEY only for write ops
yarn install
yarn tsx src/cli/stats.ts pool 0x<pool-address> # BNB (default)
yarn tsx src/cli/swap.ts quote --chain base --in ETH --out 0x<token> --amount 0.01swap, lp, vote, claim, bribe and the on-chain stats reads take --chain <id|name> for any of the five networks; position manages spoke xTOPAZ positions; lock and relay are BNB-only by protocol. Full env + per-CLI usage in scripts/README.md.
If you are building an app or SDK on top of Topaz, start with developers/DEVELOPERS.md. It links to focused guides for quote widgets, wallet-ready swap calldata, subgraph recipes, position dashboards, gauges/APR, and frontend integration.
For the Topaz account/identity layer — adding "Connect with Topaz ID" on BNB Chain, Robinhood Chain, Base, Ethereum or Arc, showing Topaz ID profiles, or signing through the Topaz ID consent flow — see developers/topaz-id-connect.md for the @topazdex/id-connect NPM package.
This section tracks the maturity of the skill. Priority 1 (validator, tests, smoke, goldens, evals, PR checklist) is complete; remaining work is feature-side (priority 2) and polish (priority 3).
Agent-facing operator layer (read + write):
-
SKILL.mdfrontmatter, trigger phrases, navigation map. -
references/topic docs for swaps (v2/v3/mixed), liquidity (v2/v3), gauges, ve-locks, voting, rewards, bribes, epoch timing, APR, addresses, tokens, pitfalls, analytics (public API + subgraph + on-chain). -
references/abis/*.jsonfor every contract the skill touches. -
examples/walkthroughs for the canonical workflows (swap-v2 stable/volatile, swap-v3 single, mixed route, v2 add-liquidity, v3 mint, CL stake, lock+vote, claim-all-rewards, deposit-bribe, query-pool-stats, spoke stake/vote/claim). -
scripts/CLIs:stats,swap,lp,lock,vote,claim,bribe,relay,position(with--chainfor the five networks where the protocol supports the action) — each backed by a typed library function inscripts/src/read/orscripts/src/write/. - Single canonical address table (
scripts/src/config/addresses.ts↔references/addresses.md↔ this README). - FS-loaded ABIs out of
references/abis/so docs and runtime stay in sync. -
yarn smokeend-to-end live read against BNB Chain.
Developer/builder layer (added on this branch):
-
developers/with builder-facing recipes (DEVELOPERS.md,quote-widget.md,swap-calldata.md,user-positions.md,subgraph-recipes.md,gauges-and-apr.md,frontend-integration.md,error-cookbook.md). - Public import surface via
scripts/src/index.ts(re-exportsADDR,TOKENS,ABIS,provider,bestQuote,bestQuoteBundle,bestV2Quote,bestV3Quote,topRoutes,buildBestSwapTx,buildV{2,3}SwapTx,buildV{2,3}{Route,Path}SwapTx,buildFromExecRoute,buildBribeDepositTx,getPoolV{2,3}, claimable/locks/votes/positions/apr/subgraph helpers, epoch math, tick math). - Wallet-ready swap builders in
scripts/src/lib/txBuilders.ts:buildBestSwapTxreturns a completeTopazSwapBatch; explicit legacy builders retain{ to, data, value, expectedOut, amountOutMin, route, quotedAt, deadline, approval? }. - Wallet-ready bribe calldata builder in
scripts/src/lib/actionBuilders.tsreturning approval +notifyRewardAmountcalldata after gauge/live/whitelist checks. - Default quotes and swap building use Topaz API split/mixed CL/v2 routing with signature-free Permit2 batches. Explicit on-chain quoters remain available. See API routing and migration.
- Broken-pool filter on every route search: candidates with > 50% USD price impact (subgraph spot prices) are dropped, with a relative-to-best fallback when subgraph prices are missing.
BestRoute.priceImpactPctexposed for UI. Tunable viamaxPriceImpactPct/minRelativeToBest/skipPriceFilteronBestQuoteOptions. NewtokenPricesUSD(addresses)helper inscripts/src/read/subgraphQueries.ts. -
bestQuotereturns the overall winner (max of v2 / v3),bestV2Quote/bestV3Quotereturn one stack at a time.topRoutes(...)returns the full sorted candidate list with an optionallimitfor UI alternatives.allowMixedonBestQuoteOptionsis now a deprecated no-op.
Builder-side input validation and safety (added on this branch):
- Every builder runs through a normalizer:
tokenIn/tokenOut/recipient/payerare checksummed viagetAddress(...),tokenIn !== tokenOut,recipient !== ZeroAddress,slippageBpsclamped to0..10000,amountIn > 0,deadlinestrictly in the future. Malformed input fails synchronously before any RPC call. - Optional
payer?: stringtriggers an on-chainallowance(tokenIn, payer, spender)read; theapprovalfield is omitted when existing allowance already coversamountIn, saving the user a redundant tx. -
BuiltSwapTxcarriesquotedAtanddeadline(unix seconds) for staleness UX. -
quoteV2and the v3 quoters alltry/catchreverts; one bad pool can't kill abestQuote. - Chain-bound multichain helpers explicitly read
eth_chainIdand reject wrong-chain RPCs; the CLIs callverifyChainfor the selected--chainbefore any read or write. A static network setting alone is not evidence of RPC chain identity. - Write helpers throw on missing
PRIVATE_KEY(no silent degradation); write CLIs broadcast only when explicitly invoked with a configured key, while no-broadcast wallet flows use builders.
Skill hygiene, validator, and brand surface (added on this branch):
- Static skill validator
scripts/src/cli/validate.ts(run viayarn validate) covers frontmatter, internal links, author-local paths, external-repo source pointers, secrets/vendored artifacts, address parity and checksums, subgraph URLs, manifest, brand, eval assertions and documented helper names (every function a doc names in ascripts/src/module must exist there). Git-aware: inspects tracked and nonignored untracked files. -
.claude/INTERNAL-SOURCE-POINTERS.md(gitignored) captures the developer-machine paths under~/topaz/topaz-{contracts,slipstream,interface,v2-subgraph,v3-subgraph}/. Those pointers were removed from all tracked public docs andscripts/src/config/addresses.ts; the validator now rejects any future leak of those paths. -
scripts/.yarn/install-state.gzuntracked +**/.yarn/{cache,unplugged,build-state.yml,install-state.gz}gitignored. - Doc-only addresses (
BalanceLogicLibrary,DelegationLogicLibrary,NFTDescriptor,NFTSVG, legacyNonfungibleTokenPositionDescriptor_V1) added toscripts/src/config/addresses.tsandREADME.mdto satisfy strict byte-for-byte parity withreferences/addresses.md. - Vitest harness + 240 unit tests across
path,epoch,tickMath,tokens,chainScope,spokeDispatch,liquidityV3,revertReason,txBuilders,actionBuilders,relayBuilders,topazRouting,topazSwap,multichain,topazApi,apr,quotes,gauges, andmulticall(incl. mockedbuildBestSwapTxcalldata-shape test, bribe approval/deposit calldata tests, the 1.D goldens, multicall3 enumerate/decode coverage,isStaleboundary/deadline cases, v3 native-BNB-out multicall/unwrap assertions, realized-fees APR goldens, and aggregate3 retry-policy coverage with injectable exec).yarn test/yarn test:watch. - Real bug fix surfaced by the tests:
getTickAtSqrtRatio's MSB binary search wrote(r > mask ? 1 : 0) << bitwherebit ∈ {128, 64, 32}— JS bitwise shift truncates to 32 bits, so1 << 128 = 1. Fixed insrc/lib/tickMath.ts. Smoke test still passes. - Brand surface:
scripts/src/config/brand.tstypedBRANDconstant (web, docs, X, Telegram, GitHub, assetsRepo, plusassets.{logoPng,logoSvg,tokenLogoPng,topaz100Png,previewJpg}pointing atraw.githubusercontent.com/topazdex/assets/main/*). Catalog pagereferences/brand.mdwith embedding examples. Links section inREADME.md, project-links section inSKILL.md. Validator enforces channel-URL parity across README/SKILL/brand.md and asset-URL presence in brand.md. - Live smoke test (
yarn smoke) extended from 5 to 9 checks (bytecode on everyADDR, TOPAZ symbol+decimals, v2/v3 TVL > 0, livebestQuote+ route-type assertion, fullbuildBestSwapTxshape, liveVoter.gauges+isAlive). Exits non-zero on any FAIL. - Golden tests (1.D) —
compareByAmountOutDescextracted fromquotes.ts;computeEmissionApr+computeFeeAprextracted fromapr.ts(poolApr behavior unchanged);src/read/{quotes,apr}.test.ts+ epoch window-state goldens are covered by the current 173-test vitest suite. - Agent eval prompts (1.E) —
evals/directory with 8 markdown checklists covering quote / build-swap / can-i-vote / claimable-bribes / quote-widget / deposit-bribe / explain-revert / safe-refusals. - PR checklist (1.F) —
docs/PR-CHECKLIST.mdmirroring validator + tests + smoke + golden + eval steps. Includes "bumping a golden" guidance. -
SKILL.mdOperating principles patched with an explicit broadcast-safety rule: "Build and quote by default; do not broadcast unless the user explicitly asks; label every output as one of {quote / built calldata / approval-needed / broadcast tx-hash}."
Analytics source of truth (2026-09-28):
- The public multichain API (
/v1) is the primary analytics source acrossSKILL.md, everyreferences/callout, thedevelopers/dashboard guides,examples/query-pool-stats.mdandscripts/README.md; the legacy/api/statsreports and their typed client are scoped to retained BNB history.references/analytics-multichain.mdcarries the full route catalog and response semantics. -
/v1/is stated as the only current API surface and/api/statsas deprecated everywhere an agent can land:SKILL.mdcallout, stats-reports banner, pitfall,skill.jsondeprecated: true,@deprecatedon thestatsApihelpers, and a stderr warning from every legacystats.tscommand. -
scripts/src/lib/topazApi.ts(fetchV1,fetchV1Pages,chainQualified,TopazApiRequestError) plus thestats.ts v1 <path> [--param value] [--all]passthrough;yarn smokechecks/v1/healthreports BNB ready. - The
topaz-ve/prodGoldsky graph is the third BNB subgraph (veClient,SUBGRAPH_VE_URL, entity catalog and queries inreferences/analytics-subgraph.md,subgraphsblock inskill.json). The validator's drift check covers v2, v3 and ve across eight files. Stale "votes/bribes/locks aren't indexed" and "v3Positionnot deployed toprod" claims were removed after verifying the live deployments. - Auto Manage (ALM) read-side coverage:
references/auto-manage.mdexplains what a vault is, where it is live (BNB, Robinhood, Arc), the/v1/auto-manage/*routes and their number-not-string / fraction-not-percent conventions, how a user's shares surface askind: managed-cl-positionin the account API, and the BNBalm*subgraph entities. Linked fromSKILL.md,developers/user-positions.md,references/liquidity-v3.mdandreferences/multichain.md.
Five-chain accounts and scripts (2026-09-28):
-
developers/topaz-id-connect.mdresynced with@topazdex/id-connect0.5.x: all five chains, per-chain gas and funding, chain switching, UserOperation receipts, per-chain signature verification, and submitting Topaz swap batches throughsendCalls({ calls, atomicRequired }). Stale "Topaz ID is BNB-only" claims removed fromSKILL.md,developers/DEVELOPERS.mdandskill.json. - Every
read//write/helper and builder takeschainId; contracts resolve fromreferences/deployments.jsonand fail closed where absent. Spoke votes and fee/bribe claims route throughXTopazVotingVault;position.tsmanages spoke positions;swap,lp,vote,claim,bribeandstatstake--chain;lock,relayand rebase refuse spokes. - Live-verified every read command on all five chains, and every write CLI on anvil forks of BNB (62 steps: native and ERC20 swaps, v2 and CL liquidity, both gauge types, bribes, veTOPAZ create/add/extend/merge/permanent/withdraw, votes, fee/bribe/rebase claims, relay deposit/claim/withdraw) and of Base, Robinhood and Ethereum (43 steps each: swaps, CL liquidity and gauges, bribes, spoke positions, votes, claims, merge, unstake). Arc's non-USDC steps pass; its USDC system contract cannot be emulated on a fork. Eval 13 and 27 new unit tests guard the behaviour.
Validator, unit tests, live smoke, goldens, agent evals, PR checklist. Land in order.
A. Static skill validation (scripts/src/cli/validate.ts, run via yarn validate):
-
SKILL.mdfrontmatter parses; required keys (name,description) present;descriptionlength within Hermes limits. - Every internal link in
SKILL.md,README.md, developers/, references/, examples/ resolves to an existing file. - No hardcoded author-local paths (
/Users/...,/home/<name>/...) outside of explicitly-noted "source pointers". - No committed secrets (
.env, private keys, API tokens) or vendored deps (node_modules,.pnp.*). - Address table in
README.mdmatchesscripts/src/config/addresses.tsmatchesreferences/addresses.mdbyte-for-byte (case-insensitive). - Subgraph URLs (v2, v3 and ve) in
README.md,SKILL.md,skill.json,scripts/.env.example,scripts/src/lib/subgraph.ts,developers/subgraph-recipes.md,developers/DEVELOPERS.md, andreferences/analytics-subgraph.mdall match.
B. TypeScript unit tests (vitest, no RPC, run via yarn test):
-
slip(amount, bps)rounding and 0/10000 boundary behavior. -
encodePath(tokens, spacings)↔decodePath(hex)round-trip for v3 paths. -
encodeMixedPathwithV2_VOLATILE/V2_STABLEsentinels. -
epochStart,epochNext,epochVoteStart,epochVoteEnd,canVoteNowagainst fixed timestamps spanning the Thu 00:00 → Wed 23:59 window and boundary hours. -
getSqrtRatioAtTick/getTickAtSqrtRatioround-trip and known Uniswap v3 fixtures. (Tests caught a real bug ingetTickAtSqrtRatio's MSB binary search — JS shift overflow atbit ≥ 32; fixed insrc/lib/tickMath.ts.) -
getAmountsForLiquidity/getLiquidityForAmountsfor representative tick ranges. -
findToken("topaz" | "0xdf...")case + address lookup. -
normalizeAndValidaterejects: self-swap, zero recipient, slippage > 10000, past deadline, malformed address. -
buildBestLegacySwapTxcalldata shape for a staticExecRoute(offline, by mocking quoters): correct selector, decoded args,valueset whenuseBnb && tokenIn === WBNB, approval skipped when payer allowance ≥ amountIn.
C. Live smoke tests (yarn smoke runs src/cli/stats.ts smoke; exits non-zero on any FAIL):
-
provider.getCode(addr) !== "0x"for every entry inADDR(every important address has deployed bytecode). -
ERC20(TOPAZ).symbol() === "TOPAZ"anddecimals() === 18. - v2 subgraph: top pair has
reserveUSD > 0. - v3 subgraph: top pool has
totalValueLockedUSD > 0. - WBNB→TOPAZ
bestQuotereturns nonzero, route type isv3-singleorv3-path(sanity). -
buildBestSwapTxreturns a complete Permit2/routerTopazSwapBatch, with quote minima, payer, chain, deadline and every ordered call. WBNB is ERC20 by default. -
Voter.gauges(<top live pool by TVL>) !== ZeroAddressandVoter.isAlive(gauge) === true.
D. Golden / regression tests:
- Route sort logic —
compareByAmountOutDescextracted fromquotes.tsand golden-tested insrc/read/quotes.test.ts(strict descending, stable on ties, wei-magnitude safe). Live smoke (1.C) covers route-family freeze for WBNB→TOPAZ. - Epoch window state at the three fixed timestamps the README calls out (Thu 00:30 UTC → distribute, Thu 01:30 UTC → vote-open, Wed 23:30 UTC → whitelist-only) —
src/lib/epoch.test.ts. - APR math against frozen samples —
computeEmissionAprandcomputeFeeAprextracted fromapr.tsand golden-tested insrc/read/apr.test.tswith hand-verifiable inputs. -
encodePathfor a known tokens/spacings pair against a frozen hex string — already covered bysrc/lib/path.test.ts"encodes the expected hex layout for a fixed input".
E. Agent eval prompts (evals/ — 1 file per prompt, manual review first, automation later):
-
01-quote.md— "Quote 0.5 WBNB → TOPAZ on Topaz." -
02-build-swap.md— "Build a swap tx but don't send it." -
03-can-i-vote.md— "Can I vote with veNFT #N this epoch?" -
04-claimable-bribes.md— "Show my claimable bribes for veNFT #N." -
05-quote-widget.md— "Build a frontend quote widget." -
06-deposit-bribe.md— "Deposit a bribe on pool X with USDC." -
07-explain-revert.md— "Explain why this swap reverted." (locks the retry-without-slippage anti-pattern.) -
08-safe-refusals.md— testnet ask / governance proposal / deploy-new-pool — skill refuses cleanly.
F. Regression checklist (docs/PR-CHECKLIST.md):
-
SKILL.mdvalidates. - No secrets /
node_modules/ vendored deps committed. -
cd scripts && yarn install --immutablesucceeds from the lockfile. -
cd scripts && yarn build(tsc --noEmit) clean. - Unit tests pass.
- Smoke tests pass against a live BSC RPC.
- Golden tests pass.
- Address tables across
README.md/references/addresses.md/scripts/src/config/addresses.tsagree byte-for-byte; SKILL.md's quick-reference subset is checksum-validated. -
developers/*.mdlinks resolve. - Eval prompts reviewed (manual until automation).
With 1.A–1.F complete and the SKILL.md broadcast/labeling rule patched, the foundational quality work is done. Remaining work is feature-side (priority 2), an automated eval harness (priority 1.G — closes the manual-review hole left by 1.E), and polish (priority 3).
G. Automated eval harness (scripts/src/cli/evals.ts, evals/, .github/workflows/evals.yml — run via yarn evals):
The 1.E checklists in evals/*.md are reviewed manually today. That leaves agent-behavior regressions invisible between releases. This slice lifts them into machine-readable assertions and an automated runner.
- YAML assertion schema documented in
evals/README.md. Six output kinds (quote/built calldata/approval-needed/broadcast tx-hash/refusal/explanation) plus four regex lists (expected_tool_calls,forbidden_tool_calls,must_include,must_not_include). Single-case and multi-case (cases:) shapes both supported. - Assertion blocks added to all 8 evals (
01-quote.mdthrough08-safe-refusals.md). The existing markdown prose stays as human-readable docs; the trailing YAML is canonical for the runner. - Spec parser (
scripts/src/lib/evalSpec.ts) — extracts the prompt from the first>blockquote (single-case) or per-section blockquote (multi-case), parses YAML viajs-yaml, validatesoutput_kindagainst the six allowed values, and returns typedEvalSpec[]. Shared by validator + runner. - Validator extension —
yarn validatenow parses everyevals/*.md, confirms the YAML block is well-formed, each regex compiles, andoutput_kindis recognized. Added as the 11th validator section. - RPC fixture system (
scripts/src/lib/evalFixtures.ts,evals/fixtures/<case-id>/responses.json) — frozen, shape-realistic JSON keyed by helper function name. Deterministic, free, replayable. Refusal evals get empty fixtures so any tool call surfaces asfixture-missing(and is graded againstforbidden_tool_calls). - Runner CLI (
scripts/src/cli/evals.ts) —yarn evalsmountsSKILL.mdplus a short eval-environment note as the system prompt, exposes two tools (topaz_read({function, args})served from fixtures;read_file({path})served from the tracked repo), runs the conversation loop untilend_turn, records everytool_useinput, and grades the trace + final answer against each case's assertions. Flags:--single,--dry-run,--model,--verbose,--max-turns. Default model:claude-haiku-4-5. WithoutANTHROPIC_API_KEY, prints a skip notice and exits 0. - Nightly CI (
.github/workflows/evals.yml) —cron: 0 7 * * *plusworkflow_dispatchwith optionalsingle/modelinputs. Skips cleanly when no API key is configured. On failure, opens a GitHub issue taggedeval-regressionrather than blocking PRs (LLM cost + nondeterminism make per-PR gating the wrong call). - README + evals/README.md updated to describe the schema, the runner, and the CI cadence.
The harness can be re-targeted: the YAML schema is runtime-agnostic, and any agent runtime that can record tool_use blocks + final text could grade the same assertions. Today's runner uses the Anthropic SDK; an external runner (Hermes, OpenCode, Codex) could consume the same evalSpec.ts parser.
- Auto Manage write flows. Shipped: ABIs, catalog entries and verify checks for factory / zap / lens on BNB, Robinhood and Arc;
read/autoManage.ts,lib/autoManageBuilders.ts(deposit, redeem, claim, zap in/out with validated 0x legs) andcli/autoManage.ts, fork-tested end to end. Original item: Vendor the frozen ABI v1 (TopazManagedCLVault,TopazManagedCLZap,TopazManagedCLLens, factory) and the factory / lens / zap addresses for BNB, Robinhood and Arc intoreferences/deployments.jsonandreferences/abis/deployed/, then add deposit / withdraw / claim builders with gate checks (paused,globalPause,live.depositsEnabled,live.isCalm, gauge alive) and a multichain test. Read-side coverage landed inreferences/auto-manage.md; until this ships the skill must not invent Auto Manage calldata. - Native-BNB-out for v3 swaps. Shipped: when
useBnbis true (default) and the v3 swap's terminal token is WBNB,buildV3SwapTxandbuildV3PathSwapTxemitSwapRouter.multicall([exactInputSingle|exactInput(recipient=Router, amountOutMinimum=0), unwrapWETH9(amountOutMin, recipient=user)]). Slippage is enforced at the unwrap boundary. PassuseBnb: falseto keep WBNB output.developers/frontend-integration.mdupdated. - Multicall3 aggregation for
bestQuote. Shipped: every candidate is packed intoMulticall3.aggregate3(allowFailure=true, ...)round trips (chunked viaaggregate3Chunkedso 3-hop v3 sweeps fit under the eth_call gas cap). One pool-existence probe runs first so the quoter sweep only sees routes through real pools.concurrencyoption is a deprecated no-op. HelpersenumerateV2Plans/enumerateV3Plans/detectPoolInventory/decodeCandidatesare unit-tested with synthetic results. - Bundler-safe ABI loading. Shipped:
scripts/src/lib/abis.tsnow uses staticimport … with { type: "json" }for every ABI. The module is statically resolvable by vite/esbuild/webpack/rollup and works in browser + edge runtimes. No FS access at runtime. ReturnsJsonFragment[](typed against ethers'Interface/Contractsignatures) so call-sites don't need any casts. The previousloadAbihelper is gone — replaced by named imports of each JSON wrapper. - APR signal cleanup. Shipped:
computeFeeAprnow takes(fees7d, tvlUsd)and computesfees7d * 52 / tvlUsd * 100. Realized fees handle Topaz'sDynamicSwapFeeModule/CustomSwapFeeModulecorrectly, whichvol7d * feeRatewould not.poolAprno longer reads the v2getFee()or the v3fee()for APR purposes (the fee-rate fetches are dropped, one fewer RPC per call). Breaking change tocomputeFeeAprsignature — documented in CHANGELOG. -
bestQuoteretry policy. Shipped:aggregate3now takes{ retries, retryBackoffMs, exec }and re-tries the whole multicall once (250ms backoff by default) on transient RPC errors. Default policy is 2 attempts total. Caller can tune via the options bag; tests inject a syntheticexecto drive the retry path deterministically. Since multicall3 collapses every candidate into one call, retrying the batch is more useful than per-candidate retries would have been. - Quote freshness helper. Shipped:
isStale(tx, maxAgeSeconds=30, now?)fromtxBuilders.ts. Returns true when quote is older thanmaxAgeSecondsOR the tx'sdeadlinehas passed. Documented indevelopers/frontend-integration.md.
-
sdk/folder is a single README pointing atscripts/. Either flesh it into a real publishable package (@topazdex/sdk,tsupbuild, types-only deps) or fold its README intodevelopers/DEVELOPERS.mdand delete the directory. -
developers/frontend-integration.mdBNB-vs-WBNB section updated now that v3 native-BNB-out is shipped. - Verify
developers/subgraph-recipes.md's "Goldsky rejects mixing column filters withor" claim against the live deployment. Verified 2026-09-28 againsttopaz-v3/prod: the endpoint returnsCannot mix column filters with 'or' operator at the same level, so the recipe's workaround is required. -
developers/error-cookbook.md— every revert across v2 Router, v3 SwapRouter / CLPool, NonfungiblePositionManager, Voter, VotingEscrow, gauges, ERC20, plus generic patterns (empty revert data, nonce / gas) — each entry has source pointer, UI string, and a next step. Wired fromSKILL.mdnav,developers/DEVELOPERS.md, and the priority-1evals/07-explain-revert.mddiagnostic.
In skill 3.0.0: the default swap builder returns a full batch. See the migration contract and release notes.
Universal Router: 0x691e6171e0a434FfE5C9f1759621D05b9efcF6A6 · Permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3. See API routing.