Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# GitHub Copilot Instructions

This file provides guidance to GitHub Copilot when working with code in this repository.
Keep this file in sync with `CLAUDE.md` — changes to one must be reflected in the other.

## Lenses

Apply all lenses before proposing any solution. Each lens constrains acceptable answers.

- Hummingbot lens: Gateway is consumed by Hummingbot Python strategies via typed connector classes. API response shapes are parsed directly into Python dicts — breaking changes to field types or names silently corrupt live trading bots. Prefer additive changes (new optional fields) over mutations. `walletAddresses` must remain `string[]`. Use Tolerant Reader pattern for all response extensions.
- Blockchain lens: The `chain` field is the technology substrate (ethereum = all EVM, solana = SVM). `network` is the L1/L2 brand discriminator (mainnet, bsc, arbitrum, base, polygon, avalanche). A wallet address is chain-scoped, not network-scoped — the same keypair works across all EVM networks. Wallet files are stored under `conf/wallets/<chain>/<address>.json` as `{encryptedKey, network}` JSON; legacy files contain a raw encrypted string and must be handled transparently.
- System Architect lens: Routes follow `/{resource}/{operation}` REST conventions. Schemas are TypeBox objects auto-published to Swagger — every new field must be typed. Backwards compatibility is enforced via optional fields, never field removal or type mutation. Singleton pattern governs chain/connector instances (`getInstance(network)`). Error responses must use Fastify `httpErrors` — never throw raw errors from route handlers.
- Bitcoin lens: Not directly supported, but cryptographic primitives (key derivation, encryption, signing) must remain chain-agnostic. Wallet encryption uses a passphrase-derived key stored outside source control. Never log or expose private keys or passphrases in any code path.
- Jest lens: Mock external deps (fs, RPC, chains) — never write real files during tests. Test both happy paths and regressions. 100% coverage on utils, 75%+ on routes. Use `jest.mock()` for file/crypto ops. Parallel tests should not share state. Validate schema contracts before business logic. Every test suite must cover three categories: (1) **Happy paths** — expected utilization with valid inputs for all parameter combinations; (2) **Edge cases** — boundary values, legacy file formats, same-address-multi-network, empty arrays, zero amounts; (3) **Missing/invalid parameters** — each required field omitted independently, unrecognized chain/network values, neither `chain` nor `chainNetwork` provided, malformed `chainNetwork` strings. Route handler tests must assert the HTTP status code, not just the response body shape.
- QA lens: Validate backwards compatibility at every response boundary. Legacy wallet files must parse identically. New optional fields should not break old consumers. Test migration scenarios: old wallets → new system, new fields with old clients. Regression suite covers all breaking-change-adjacent code paths.
- Security lens: Never log or expose private keys, passphrases, mnemonic seeds, or decrypted values. All file I/O must use `getSafeWalletFilePath()` with sanitized inputs. Wallet encryption keys derive from passphrase outside source control. Validate address formats to prevent injection. All secrets must be stored in `conf/` outside repo.

- Markdown lens: All `.md` files must render cleanly — headings surrounded by blank lines, lists surrounded by blank lines, fenced code blocks surrounded by blank lines, no bare URLs (wrap in angle brackets or `[text](url)`), no trailing spaces, consistent ATX-style headings (`##` not underline). PR descriptions, README sections, and CLAUDE.md must follow these rules. Use `<!--` comments only for meta-notes, never for hiding required content.
- Documentation lens: Every public API change needs three things in the same commit — (1) updated TypeBox schema `description` fields visible in Swagger, (2) at minimum one Swagger `examples` entry showing BSC or the most common real-world usage, (3) a matching update to any relevant section in CLAUDE.md or copilot-instructions.md. Explanations must be concise (1–2 sentences), concrete (show the actual value, not a placeholder), and target the operator/bot-developer persona — not implementers. Avoid restating the field name; explain *why* it matters and what values are valid.
- OpenAPI lens: Every route schema must produce a valid, self-contained OpenAPI 3.0 object — `operationId` derived from method + path, `summary` one sentence ≤ 10 words, `description` operator-facing (no internal implementation details), `tags` matching the module folder name. `servers[0]` must be `{url: '/'}` so Swagger UI "Try it out" works at any mapped port. All enum fields (`chain`, `network`, `chainNetwork`) must carry `examples` arrays including BSC. Response schemas must exactly mirror handler return types — divergence silently breaks Python client parsing. Never use `Type.Any()` in a route schema.
- GitHub lens: Every commit on a feature branch must be atomic and conventional (`feat:`, `fix:`, `docs:`, `test:`, `chore:`). PR titles follow the same convention. Breaking changes append `!` to the type (`feat!:`). `pull_request.md` must be kept current — update it in the same commit as any functional change. Branch names use kebab-case prefixed by type (`feat-`, `fix-`). Never force-push to `development` or `main`. Squash-merge only; no merge commits on protected branches.

- Build: `pnpm build`
- Start server: `pnpm start --passphrase=<PASSPHRASE>`
- Start in dev mode: `pnpm start --passphrase=<PASSPHRASE> --dev` (HTTP mode, no SSL)
- Run all tests: `pnpm test`
- Run specific test file: `GATEWAY_TEST_MODE=dev jest --runInBand path/to/file.test.ts`
- Run tests with coverage: `pnpm test:cov`
- Lint: `pnpm lint` / Format: `pnpm format` / Type check: `pnpm typecheck`

## Architecture Overview

- RESTful API gateway built with Fastify + TypeBox schemas (auto-generates Swagger at `/docs`)
- Chain routes: `/chains/{chain}/{operation}` — e.g. `/chains/ethereum/balances`
- Connector routes: `/connectors/{dex}/{type}/{operation}` — type is `router`, `amm`, or `clmm`
- Wallet routes: `/wallet/*`
- Config routes: `/config/*`
- Chains are singletons: `Ethereum.getInstance(network)`, `Solana.getInstance(network)`
- Connectors are singletons: `Pancakeswap.getInstance(network)`, `Uniswap.getInstance(network)`
- `chain` = substrate (`ethereum` covers all EVM networks, `solana` covers all SVM networks)
- `network` = specific network (`mainnet`, `bsc`, `arbitrum`, `base`, `mainnet-beta`, etc.)
- `chainNetwork` = combined shorthand (`ethereum-bsc`, `ethereum-arbitrum`) parsed as `chain-network`

## Coding Style

- TypeScript, ESNext, CommonJS modules, 2-space indent, single quotes, semicolons required
- TypeBox for all request/response schemas — no untyped `any` in route handlers
- `logger` for all logging — never `console.log`
- `fastify.httpErrors.*` for all API error responses — never throw raw `Error` from handlers
- Unused variables prefixed with `_`
- Tests required for all new functionality (min 75% coverage for PRs)
- Test files mirror `src/` structure under `test/`; mocks live in `test/mocks/`

## Swagger / OpenAPI Documentation

Gateway auto-generates Swagger UI from TypeBox schemas via `@fastify/swagger` + `@fastify/swagger-ui`.

- **Live UI**: `http://localhost:15888/docs` (dev mode) or `https://localhost:15888/docs` (production)
- **JSON spec**: `GET /docs/json` — used to regenerate `openapi.json` at root
- **Schema location**: All TypeBox schemas live in `src/schemas/`, `src/{module}/schemas.ts`, or inline in route files
- Every route **must** declare `schema: { tags, summary, description, body/querystring, response }` — undecorated routes are invisible in Swagger
- Use `description` on every `Type.Object` field to explain purpose, valid values, and format
- `examples` arrays must include **BSC** alongside mainnet for every EVM `network` field: `['mainnet', 'bsc', 'arbitrum', 'base', 'polygon', 'avalanche']`
- `chainNetwork` examples must put `ethereum-bsc` first after `ethereum-mainnet`: `['ethereum-mainnet', 'ethereum-bsc', 'ethereum-arbitrum', 'solana-mainnet-beta']`
- `chain` field description must read: `'Blockchain substrate — use "ethereum" for all EVM networks (mainnet, BSC, Arbitrum, Base, Polygon, Avalanche), "solana" for all SVM networks'`
- Response schemas must match actual handler return types exactly — mismatches silently break Hummingbot Python parsing
- Tag groupings: `chains`, `connectors`, `wallet`, `config`, `pools`, `tokens` — use the tag matching the module folder
- Never use `Type.Any()` in a route schema; prefer `Type.Unknown()` with a description if shape varies
- After adding/changing schemas, run `pnpm build` and verify the route appears correctly in Swagger UI

## Key Patterns

- New wallet files: `JSON.stringify({ encryptedKey, network })` — always read with fallback to legacy raw string
- `chainNetwork` parsing: `parts = val.split('-'); chain = parts[0]; network = parts.slice(1).join('-')`
- Response extension: add optional fields alongside existing ones — never mutate existing field types
- Route files live in `{module}/routes/{operation}.ts`, registered in `{module}.routes.ts`
- Pool configs: `src/templates/pools/{connector}.json` — format: `{ type, network, baseSymbol, quoteSymbol, baseTokenAddress, quoteTokenAddress, feePct, address }`
37 changes: 37 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
# AI Agent Instructions

This file provides guidance to AI coding assistants when working with code in this repository.
Keep this file in sync with `.github/copilot-instructions.md` — changes to one must be reflected in the other.

## Lenses

Apply all lenses before proposing any solution. Each lens constrains acceptable answers.

- Hummingbot lens: Gateway is consumed by Hummingbot Python strategies via typed connector classes. API response shapes are parsed directly into Python dicts — breaking changes to field types or names silently corrupt live trading bots. Prefer additive changes (new optional fields) over mutations. `walletAddresses` must remain `string[]`. Use Tolerant Reader pattern for all response extensions.
- Blockchain lens: The `chain` field is the technology substrate (ethereum = all EVM, solana = SVM). `network` is the L1/L2 brand discriminator (mainnet, bsc, arbitrum, base, polygon, avalanche). A wallet address is chain-scoped, not network-scoped — the same keypair works across all EVM networks. Wallet files are stored under `conf/wallets/<chain>/<address>.json` as `{encryptedKey, network}` JSON; legacy files contain a raw encrypted string and must be handled transparently.
- System Architect lens: Routes follow `/{resource}/{operation}` REST conventions. Schemas are TypeBox objects auto-published to Swagger — every new field must be typed. Backwards compatibility is enforced via optional fields, never field removal or type mutation. Singleton pattern governs chain/connector instances (`getInstance(network)`). Error responses must use Fastify `httpErrors` — never throw raw errors from route handlers.
- Bitcoin lens: Not directly supported, but cryptographic primitives (key derivation, encryption, signing) must remain chain-agnostic. Wallet encryption uses a passphrase-derived key stored outside source control. Never log or expose private keys or passphrases in any code path.
- Jest lens: Mock external deps (fs, RPC, chains) — never write real files during tests. Test both happy paths and regressions. 100% coverage on utils, 75%+ on routes. Use `jest.mock()` for file/crypto ops. Parallel tests should not share state. Validate schema contracts before business logic. Every test suite must cover three categories: (1) **Happy paths** — expected utilization with valid inputs for all parameter combinations; (2) **Edge cases** — boundary values, legacy file formats, same-address-multi-network, empty arrays, zero amounts; (3) **Missing/invalid parameters** — each required field omitted independently, unrecognized chain/network values, neither `chain` nor `chainNetwork` provided, malformed `chainNetwork` strings. Route handler tests must assert the HTTP status code, not just the response body shape.
- QA lens: Validate backwards compatibility at every response boundary. Legacy wallet files must parse identically. New optional fields should not break old consumers. Test migration scenarios: old wallets → new system, new fields with old clients. Regression suite covers all breaking-change-adjacent code paths.
- Security lens: Never log or expose private keys, passphrases, mnemonic seeds, or decrypted values. All file I/O must use `getSafeWalletFilePath()` with sanitized inputs. Wallet encryption keys derive from passphrase outside source control. Validate address formats to prevent injection. All secrets must be stored in `conf/` outside repo.

- Markdown lens: All `.md` files must render cleanly — headings surrounded by blank lines, lists surrounded by blank lines, fenced code blocks surrounded by blank lines, no bare URLs (wrap in angle brackets or `[text](url)`), no trailing spaces, consistent ATX-style headings (`##` not underline). PR descriptions, README sections, and CLAUDE.md must follow these rules. Use `<!--` comments only for meta-notes, never for hiding required content.
- Documentation lens: Every public API change needs three things in the same commit — (1) updated TypeBox schema `description` fields visible in Swagger, (2) at minimum one Swagger `examples` entry showing BSC or the most common real-world usage, (3) a matching update to any relevant section in CLAUDE.md or copilot-instructions.md. Explanations must be concise (1–2 sentences), concrete (show the actual value, not a placeholder), and target the operator/bot-developer persona — not implementers. Avoid restating the field name; explain *why* it matters and what values are valid.
- OpenAPI lens: Every route schema must produce a valid, self-contained OpenAPI 3.0 object — `operationId` derived from method + path, `summary` one sentence ≤ 10 words, `description` operator-facing (no internal implementation details), `tags` matching the module folder name. `servers[0]` must be `{url: '/'}` so Swagger UI "Try it out" works at any mapped port. All enum fields (`chain`, `network`, `chainNetwork`) must carry `examples` arrays including BSC. Response schemas must exactly mirror handler return types — divergence silently breaks Python client parsing. Never use `Type.Any()` in a route schema.
- GitHub lens: Every commit on a feature branch must be atomic and conventional (`feat:`, `fix:`, `docs:`, `test:`, `chore:`). PR titles follow the same convention. Breaking changes append `!` to the type (`feat!:`). `pull_request.md` must be kept current — update it in the same commit as any functional change. Branch names use kebab-case prefixed by type (`feat-`, `fix-`). Never force-push to `development` or `main`. Squash-merge only; no merge commits on protected branches.

## Build & Command Reference

- Build: `pnpm build`
- Start server: `pnpm start --passphrase=<PASSPHRASE>`
- Start in dev mode: `pnpm start --passphrase=<PASSPHRASE> --dev` (HTTP mode, no SSL)
Expand All @@ -19,6 +38,7 @@ This file provides guidance to AI coding assistants when working with code in th
## Architecture Overview

### Gateway Pattern

- RESTful API gateway providing standardized endpoints for blockchain and DEX interactions
- Built with Fastify framework using TypeBox for schema validation
- Supports both HTTP (dev mode) and HTTPS (production) protocols
Expand Down Expand Up @@ -100,6 +120,23 @@ This file provides guidance to AI coding assistants when working with code in th
- `conf/`: Runtime configuration (created by setup)
- `tokens/`: Token lists for each network

## Swagger / OpenAPI Documentation

Gateway auto-generates Swagger UI from TypeBox schemas via `@fastify/swagger` + `@fastify/swagger-ui`.

- **Live UI**: `http://localhost:15888/docs` (dev mode) or `https://localhost:15888/docs` (production)
- **JSON spec**: `GET /docs/json` — used to regenerate `openapi.json` at root
- **Schema location**: All TypeBox schemas live in `src/schemas/`, `src/{module}/schemas.ts`, or inline in route files
- Every route **must** declare `schema: { tags, summary, description, body/querystring, response }` — undecorated routes are invisible in Swagger
- Use `description` on every `Type.Object` field to explain purpose, valid values, and format
- `examples` arrays must include **BSC** alongside mainnet for every EVM `network` field: `['mainnet', 'bsc', 'arbitrum', 'base', 'polygon', 'avalanche']`
- `chainNetwork` examples must put `ethereum-bsc` first after `ethereum-mainnet`: `['ethereum-mainnet', 'ethereum-bsc', 'ethereum-arbitrum', 'solana-mainnet-beta']`
- `chain` field description must read: `'Blockchain substrate — use "ethereum" for all EVM networks (mainnet, BSC, Arbitrum, Base, Polygon, Avalanche), "solana" for all SVM networks'`
- Response schemas must match actual handler return types exactly — mismatches silently break Hummingbot Python parsing
- Tag groupings: `chains`, `connectors`, `wallet`, `config`, `pools`, `tokens` — use the tag matching the module folder
- Never use `Type.Any()` in a route schema; prefer `Type.Unknown()` with a description if shape varies
- After adding/changing schemas, run `pnpm build` and verify the route appears correctly in Swagger UI

## Best Practices
- Create tests for all new functionality (minimum 75% coverage for PRs)
- Use the logger for debug/errors (not console.log)
Expand Down
Loading