Agent skill package for Topaz Dex — a ve(3,3) DEX on BNB Chain Mainnet (chain id 56) combining Solidly-style v2 pools (volatile + stable) with Uniswap-v3-style concentrated liquidity (Slipstream). The skill teaches Claude how to swap, manage liquidity (both v2 LP and v3 NFT positions), stake in gauges, manage veTOPAZ locks, vote, claim rewards, deposit bribes, and query analytics via on-chain reads and the official subgraphs.
Everything here is mainnet-only. Testnet and governance contracts (EpochGovernor/ProtocolGovernor) are intentionally out of scope.
Current version: 2.4.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.
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 # set BSC_RPC_URL; 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 # 109 unit tests (vitest, no RPC)
yarn smoke # live read against BSC mainnet — requires BSC_RPC_URLCI 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 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 | 0xBa4C4f5Ca809C21286ff1a872b3c0CFb57AfE904 |
| NonfungibleTokenPositionDescriptor_V1 (legacy) | 0x81aCc35240D19948a56b8b68BcC8706F90baBAb5 |
| NFTDescriptor (library) | 0x50f9756f631266686b9A7EBDF55998dB3dA5ca0a |
| NFTSVG (library) | 0x21C9257dFCdf04154D34dF5A2204B9402Ef31d9a |
| CustomSwapFeeModule | 0xA0462a52af4f8cbF7766Efbba75355B30b6BCCe2 |
| CustomUnstakedFeeModule | 0x3bad7F96cd1b51CE86e12C42541Ac7d559A78582 |
| DynamicSwapFeeModule | 0x656cf5d2f1A70177E011e2c27DeafBeE4C7B0541 |
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.
v2: https://api.goldsky.com/api/public/project_cmgzljqwl006c5np2gnao4li4/subgraphs/topaz-v2/v0.0.3/gn
v3: https://api.goldsky.com/api/public/project_cmgzljqwl006c5np2gnao4li4/subgraphs/topaz-v3/v0.0.1/gn
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.
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
│ ├── analytics-{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, chain, tokens
├── lib/ # client, erc20, subgraph, tickMath, path, pricing, epoch
├── read/ # quotes, pools, gauges, locks, votes, claimable, apr, ...
├── write/ # swap, liquidity, gauge, lock, vote, claim, bribe
└── cli/ # `yarn tsx src/cli/<cmd>.ts ...` entry points
cd scripts
cp .env.example .env
# edit .env: BSC_RPC_URL (required), PRIVATE_KEY (required only for write ops)
yarn install
yarn tsx src/cli/stats.ts pool 0x<pool-address> # read-only exampleFull 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.
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 (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). -
scripts/CLIs:stats,swap,lp,lock,vote,claim,bribe— 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 mainnet.
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 calldata builders in
scripts/src/lib/txBuilders.tsreturning{ 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. - Route search is v2-only or v3-only — never mixed.
bestQuoteBundle(...)returns the best v2 (volatile + stable, up to 3 hops) and best v3 (every tick-spacing combination, up to 3 hops) side-by-side, plus the overall winner. Intermediaries swept:USDT, WBNB, BTCB, ETH, TOPAZ, USDC. A pool-existence probe (oneMulticall3.aggregate3) prunes routes through non-existent pools before the quoter sweep, and large quote batches are chunked across multicalls so the v3 3-hop layer fits inside the eth_call gas cap. - 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. - Provider is constructed with
staticNetwork: { chainId: 56 }so ethers rejects wrong-chain RPCs. - 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) covering 9 categories: frontmatter, internal links (markdown + backticked paths, fenced-code-aware), author-local paths, external-repo source pointers, secrets / vendored deps / yarn-cache artifacts, address-set parity (config ↔ README ↔ references), EIP-55 checksum validity (viaethers.getAddress), subgraph URL consistency, and brand URL parity. Git-aware: only inspects tracked 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 + 116 unit tests across
path,epoch,tickMath,tokens,txBuilders,actionBuilders,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 116-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}."
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 in
README.md,SKILL.md,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. -
buildBestSwapTxcalldata 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). -
buildBestSwapTx({ WBNB→TOPAZ, recipient=dead })returns{ to: ADDR.SwapRouter, data: 0x..., value: amountIn, expectedOut > 0, amountOutMin > 0, quotedAt > 0, deadline > now }. -
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) and polish (priority 3).
- 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 previousloadAbi(name)helper 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; the comment reads like it was written from a remembered failure rather than tested. -
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.