diff --git a/README.md b/README.md index b794bab..f5d3805 100755 --- a/README.md +++ b/README.md @@ -1,51 +1,80 @@ -# QuickSwap SDK +

+ QuickSwap SDK
+ TypeScript building blocks for QuickSwap V2 trading and multi-chain protocol metadata. +

-This repository has been forked from [UniswapV2](https://github.com/Uniswap/uniswap-sdk) +

+ Overview • + Packages • + Flows • + Diagrams • + Glossary • + ADRs +

-## Running tests +--- -To run the tests, follow these steps. You must have at least node v10 and [yarn](https://yarnpkg.com/) installed. +> Trade entities, pricing math, and chain configuration — published as two focused npm packages. -First clone the repository: +A pnpm monorepo with two packages: `@quickswap-defi/protocol-core` (chain registry, versions, +stablecoins, fees) and `@quickswap-defi/sdk` (Uniswap V2-fork entities, fetcher, router). +Consumers pick metadata only, or the full trading toolkit on top of it. -```sh -git clone https://github.com/QuickSwap/QuickSwap-sdk.git -``` +## Packages -Move into the quickswap-sdk working directory +| Package | Role | Status | +|---|---|---| +| [`@quickswap-defi/protocol-core`](docs/packages/protocol-core.md) | Chain configs, protocol versions, fees, native + stablecoin metadata | Published | +| [`@quickswap-defi/sdk`](docs/packages/sdk.md) | V2 entities (`Token`, `Pair`, `Route`, `Trade`), fetcher, router | Published | -```sh -cd QuickSwap-sdk/ -``` +## How It Works -Install dependencies +1. **Resolve chain metadata** — read chain config and addresses from `protocol-core`. +2. **Build entities** — instantiate `Token`s, fetch `Pair` reserves, construct a `Route`. +3. **Compute the trade** — derive `Trade` outputs, slippage bounds, and execution params. -```sh -yarn install -``` +Full sequence → [docs/flows/trade-execution.md](docs/flows/trade-execution.md). -Run tests +## Install -```sh -yarn test +```bash +pnpm add @quickswap-defi/sdk @quickswap-defi/protocol-core +pnpm add @ethersproject/address @ethersproject/contracts @ethersproject/providers @ethersproject/networks @ethersproject/solidity ``` -You should see output like the following: - -```sh -yarn run v1.22.4 -$ tsdx test - PASS test/constants.test.ts - PASS test/pair.test.ts - PASS test/fraction.test.ts - PASS test/miscellaneous.test.ts - PASS test/entities.test.ts - PASS test/trade.test.ts - -Test Suites: 1 skipped, 6 passed, 6 of 7 total -Tests: 3 skipped, 82 passed, 85 total -Snapshots: 0 total -Time: 5.091s -Ran all test suites. -✨ Done in 6.61s. +## Stack + +| Component | Purpose | +|---|---| +| TypeScript 5 (strict) | Source language | +| tsup | CJS + ESM dual build with `.d.ts` | +| Vitest 2 | Test runner | +| ESLint 9 | Lint (flat config) | +| JSBI | BigInt math | +| ethers v5 | Peer dependency for on-chain reads | + +## Documentation + +Documentation follows [Diátaxis](https://diataxis.fr/) — each doc stays in its lane. + +| Doc | Type | Description | +|-----|------|-------------| +| [Overview](docs/overview.md) | Explanation | What the SDK is and why the split exists | +| [Packages](docs/packages/) | Reference | Per-package role, exports, dependencies | +| [Flows](docs/flows/) | Explanation | Trade execution and chain onboarding | +| [Diagrams](docs/diagrams/) | Reference | C4 + sequence mermaid sources | +| [Glossary](docs/glossary.md) | Reference | Domain terms | +| [ADRs](docs/adr/) | Decisions | Architecture decision records | + +## Development + +```bash +pnpm install +pnpm -r build +pnpm -r test +pnpm -r lint ``` + +## License + +MIT. diff --git a/docs/adr/001-protocol-core-split.md b/docs/adr/001-protocol-core-split.md new file mode 100644 index 0000000..0ba5a4c --- /dev/null +++ b/docs/adr/001-protocol-core-split.md @@ -0,0 +1,37 @@ +# ADR 001 — Split protocol-core from the trading SDK + +- **Status:** Accepted +- **Date:** 2026-05-04 + +## Context + +The original SDK was a single package: V2 entities, pricing math, on-chain fetcher, and chain +configuration all shipped together. Two different consumer profiles emerged: + +- Trading consumers (swap UIs, aggregators) need everything — entities, math, fetcher. +- Metadata consumers (frontends listing chains, indexers, analytics) only need the chain + registry, fees, and stablecoin lists. They have no use for JSBI, ethers, or V2 math. + +Bundling everything together meant the metadata consumers paid for the entire trading layer. +Releases were also coupled: a chain registry update forced an SDK release, and an SDK math fix +forced a registry release. + +## Decision + +Split the monorepo into two packages: + +- `@quickswap-defi/protocol-core` — chains, fees, token primitives, version helpers. Zero + runtime dependencies. +- `@quickswap-defi/sdk` — V2 entities, fetcher, router. Depends on `protocol-core` and the + ethers peer set. + +Dependency direction is one-way: `sdk` depends on `protocol-core`, never the reverse. + +## Consequences + +- Metadata consumers install a tiny package with no runtime dependencies. +- Trading consumers get the same surface they had before, plus an explicit dependency edge. +- Each package versions independently. A chain config change does not bump the SDK. +- The boundary needs to be enforced in code review — V2 math must not leak into `protocol-core`. +- New chains are added in `protocol-core` first, then surfaced in `sdk` (see + `docs/flows/chain-onboarding.md`). diff --git a/docs/adr/002-jsbi-over-bigint.md b/docs/adr/002-jsbi-over-bigint.md new file mode 100644 index 0000000..d2b10c1 --- /dev/null +++ b/docs/adr/002-jsbi-over-bigint.md @@ -0,0 +1,35 @@ +# ADR 002 — Use JSBI instead of native BigInt + +- **Status:** Accepted +- **Date:** 2026-05-04 + +## Context + +V2 pricing math operates on token reserves and amounts that exceed `Number.MAX_SAFE_INTEGER`. +The SDK needs an integer type with arbitrary precision. + +Two options: + +- Native `BigInt` — built into modern engines, fastest, no dependency. +- `JSBI` — userland BigInt polyfill, uniform API across runtimes. + +The SDK was originally forked from Uniswap's V2 SDK, which standardized on JSBI when native +`BigInt` had inconsistent support across browsers and Node versions targeted by dApps. JSBI's +API mirrors the eventual native one, so a future migration is mostly mechanical. + +## Decision + +Use JSBI throughout the SDK. Re-export `JSBI` from `@quickswap-defi/sdk` so consumers can build +amounts (`JSBI.BigInt('1000000000000000000')`) without adding their own dependency. + +Continue accepting `BigintIsh = JSBI | bigint | string` at public API boundaries so consumers +who prefer native `BigInt` can pass it in. + +## Consequences + +- Consistent math API across every runtime the SDK targets. +- A small bundle cost from the JSBI dependency. +- Slight performance gap versus native `BigInt`, acceptable for typical swap-sized math. +- A future ADR may revisit this once native `BigInt` is the universal default and the + migration cost is justified. +- Consumers can mix JSBI and native `BigInt` at the boundary via `BigintIsh`. diff --git a/docs/diagrams/c1-context.mmd b/docs/diagrams/c1-context.mmd new file mode 100644 index 0000000..99dc1c1 --- /dev/null +++ b/docs/diagrams/c1-context.mmd @@ -0,0 +1,29 @@ +%% C4 Level 1 — System Context +%% QuickSwap SDK as a black box in its ecosystem. +%% Edit this file; GitHub renders the diagram natively in markdown previews. + +graph TB + subgraph PEOPLE["Consumers"] + ACTOR_1["dApp / Frontend
Swap UIs, aggregators"] + ACTOR_2["Backend / Indexer
Analytics, off-chain services"] + end + + SYSTEM[["QuickSwap SDK
V2 entities + multi-chain protocol metadata"]] + + subgraph EXT["External Systems"] + EXT_1["EVM Chains
Polygon, Base, zkEVM, others"] + EXT_2["ethers v5
RPC provider library"] + end + + ACTOR_1 -->|"imports entities, builds trades"| SYSTEM + ACTOR_2 -->|"imports chain registry"| SYSTEM + SYSTEM <-->|"reads pair reserves and token metadata"| EXT_1 + SYSTEM -->|"uses provider for on-chain reads"| EXT_2 + + classDef system fill:#4a1f6e,stroke:#9b59b6,color:#f0e6ff,font-weight:bold + classDef external fill:#1e3a2f,stroke:#27ae60,color:#d5f5e3 + classDef person fill:#3d2b1f,stroke:#e67e22,color:#fdebd0 + + class SYSTEM system + class EXT_1,EXT_2 external + class ACTOR_1,ACTOR_2 person diff --git a/docs/diagrams/c2-containers.mmd b/docs/diagrams/c2-containers.mmd new file mode 100644 index 0000000..9ece2f3 --- /dev/null +++ b/docs/diagrams/c2-containers.mmd @@ -0,0 +1,33 @@ +%% C4 Level 2 — Containers +%% Inside QuickSwap SDK: packages and how they relate to consumers and libraries. +%% Edit this file; GitHub renders the diagram natively in markdown previews. + +graph TB + subgraph EXTERNAL["External"] + CONSUMER["dApp / Consumer
Imports the SDK"] + ETHERS["ethers v5
Peer dependency"] + JSBI_LIB["JSBI
BigInt polyfill"] + CHAIN["EVM Chain RPC
On-chain reads"] + end + + subgraph SYSTEM["QuickSwap SDK monorepo"] + subgraph PACKAGES["Packages"] + CORE["@quickswap-defi/protocol-core
Chains, fees, tokens, versions"] + SDK["@quickswap-defi/sdk
V2 entities, fetcher, router"] + end + end + + CONSUMER -->|"imports"| SDK + CONSUMER -->|"imports (metadata only)"| CORE + SDK -->|"depends on"| CORE + SDK -->|"peer dep"| ETHERS + SDK -->|"depends on"| JSBI_LIB + SDK -->|"reads via provider"| CHAIN + + classDef entity fill:#4a1f6e,stroke:#9b59b6,color:#f0e6ff,font-weight:bold + classDef infra fill:#1a3a5c,stroke:#2e86c1,color:#d6eaf8 + classDef external fill:#1e3a2f,stroke:#27ae60,color:#d5f5e3 + + class CORE,SDK entity + class ETHERS,JSBI_LIB infra + class CONSUMER,CHAIN external diff --git a/docs/diagrams/flow-trade.mmd b/docs/diagrams/flow-trade.mmd new file mode 100644 index 0000000..23a9c76 --- /dev/null +++ b/docs/diagrams/flow-trade.mmd @@ -0,0 +1,30 @@ +%% Sequence — Trade execution +%% From input amounts to a Trade ready to send on-chain. +%% Edit this file; GitHub renders the diagram natively in markdown previews. + +sequenceDiagram + autonumber + actor C as Consumer + participant F as Pair Fetcher + participant R as Route + participant T as Trade + + C->>C: build Token instances (in, out) + C->>F: fetchPairData(tokenA, tokenB, provider) + F->>F: derive pair address (factory + init code hash) + F-->>C: Pair (with reserves) + + C->>R: new Route([pair, ...], input, output) + R-->>C: Route + + alt EXACT_INPUT + C->>T: Trade.exactIn(route, inputAmount) + else EXACT_OUTPUT + C->>T: Trade.exactOut(route, outputAmount) + end + T-->>C: Trade (inputAmount, outputAmount, priceImpact) + + C->>T: minimumAmountOut(slippage) / maximumAmountIn(slippage) + T-->>C: bound CurrencyAmount + + Note over C: Format router calldata, sign, and submit diff --git a/docs/flows/chain-onboarding.md b/docs/flows/chain-onboarding.md new file mode 100644 index 0000000..294c310 --- /dev/null +++ b/docs/flows/chain-onboarding.md @@ -0,0 +1,37 @@ +# Flow — Chain onboarding + +How to add a new EVM chain to the SDK so consumers can use it. + +## Diagram + +No dedicated sequence diagram. The container view in +[../diagrams/c2-containers.mmd](../diagrams/c2-containers.mmd) shows where each artifact lives. + +## Steps + +1. **protocol-core — chain config.** Create `packages/protocol-core/src/chains/.ts` + exporting a `ChainConfig` with chain id, name, native token, wrapped native, supported + protocol versions, schema variant, and stablecoin list. +2. **protocol-core — registry.** Import and register the new config in + `packages/protocol-core/src/chains/registry.ts` (both `_registry` and `CHAIN_ID`). +3. **protocol-core — barrel.** Re-export the new chain config from + `packages/protocol-core/src/index.ts`. +4. **sdk — ChainId enum.** Add an entry to the `ChainId` enum in + `packages/sdk/src/constants.ts`. +5. **sdk — factory address.** Add the chain's V2 factory address to the `FACTORY_ADDRESS` map. + Until the address is finalized, leave the entry under onboarding rather than publishing. +6. **Tests.** Add or update tests in `packages/protocol-core/test/` and `packages/sdk/test/` to + cover the new chain. +7. **Build and lint.** Run `pnpm -r build` and `pnpm -r lint` to confirm both packages compile. + +## Inputs + +- Chain id, factory deployment address, native + wrapped native metadata, stablecoin list. + +## Outputs + +- A new chain that is enumerable via `getSupportedChainIds()` and usable from `Token` / `Pair`. + +## Audience + +SDK maintainers onboarding a new deployment. diff --git a/docs/flows/trade-execution.md b/docs/flows/trade-execution.md new file mode 100644 index 0000000..556a7fd --- /dev/null +++ b/docs/flows/trade-execution.md @@ -0,0 +1,40 @@ +# Flow — Trade execution + +How a consumer turns input amounts into a `Trade` with the params needed to execute on-chain. + +## Diagram + +See [../diagrams/flow-trade.mmd](../diagrams/flow-trade.mmd). + +## Steps + +1. **Consumer** builds `Token` instances for the input and output currencies. Each `Token` + carries its chain id, address, and decimals. +2. **Consumer** asks the **Fetcher** for the `Pair` between the two tokens. The fetcher derives + the pair address (factory + init code hash) and reads reserves from chain via the ethers + provider. +3. **Consumer** constructs a **Route** from the input currency through one or more pairs to the + output currency. Single-hop routes use one pair; multi-hop routes chain pairs together. +4. **Consumer** instantiates a **Trade**: + - `Trade.exactIn(route, inputAmount)` — fix the input, derive the output. + - `Trade.exactOut(route, outputAmount)` — fix the output, derive the input. +5. **Consumer** picks a slippage `Percent` and reads the bounds: + - `trade.minimumAmountOut(slippage)` for `EXACT_INPUT`. + - `trade.maximumAmountIn(slippage)` for `EXACT_OUTPUT`. +6. **Consumer** uses the `Router` helpers to format calldata for the swap, then signs and sends + it through the ethers signer. + +## Inputs + +- `Token` instances on the same chain. +- An ethers v5 provider. +- A user-chosen slippage tolerance. + +## Outputs + +- A `Trade` with `inputAmount`, `outputAmount`, `route`, `priceImpact`, and `executionPrice`. +- Router calldata for on-chain execution. + +## Audience + +SDK consumers building swap UIs, aggregators, or back-office tools. diff --git a/docs/glossary.md b/docs/glossary.md new file mode 100644 index 0000000..b6b2ead --- /dev/null +++ b/docs/glossary.md @@ -0,0 +1,68 @@ +# Glossary + +One term per `##` heading. Two-to-four sentences max per term. + +## ChainId + +Numeric identifier of an EVM network (e.g. Polygon = 137, Base = 8453). The SDK exposes a +`ChainId` enum and `protocol-core` exposes a `CHAIN_ID` map keyed by name. + +## Token + +ERC20 representation: chain id, address, decimals, optional symbol and name. Used as the basic +unit for pairs, routes, and trades. + +## Currency + +Base type that abstracts over a `Token` and a chain's native asset. Anything you can hold or +trade is a `Currency`. + +## Pair + +A liquidity pool between two `Token`s. Holds reserves and exposes pricing helpers and the math +to derive output amounts from inputs. + +## Route + +An ordered list of `Pair`s connecting an input `Currency` to an output `Currency`. Routes can be +multi-hop. + +## Trade + +A computed swap: input amount, output amount, route, price impact, and the params needed to +execute on-chain. + +## CurrencyAmount + +A `Currency` paired with a quantity, expressed as a fraction over the currency's decimals. + +## TokenAmount + +A `CurrencyAmount` whose currency is specifically a `Token` (not a native asset). + +## Fraction + +Lossless rational number (`numerator / denominator`) used for all internal pricing math to avoid +floating-point error. + +## Percent + +A `Fraction` interpreted as a percentage. Used for slippage tolerance and price impact. + +## Price + +A `Fraction` of one `Currency` denominated in another. Derived from `Pair` reserves. + +## Factory + +The on-chain V2 factory contract address per chain. Combined with `INIT_CODE_HASH` to +deterministically compute pair addresses. + +## Init Code Hash + +The keccak256 hash of the pair contract's init code. Used in `CREATE2` to derive the address of +a pair without an on-chain call. + +## JSBI + +JavaScript BigInt polyfill. The SDK uses JSBI throughout for cross-runtime BigInt math. diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..2a9c9cd --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,44 @@ +# QuickSwap SDK — Overview + +## What + +The QuickSwap SDK is a pnpm monorepo that publishes two TypeScript packages for working with +QuickSwap's V2 (Uniswap V2-fork) deployments across multiple EVM chains. `protocol-core` exposes +chain configuration, protocol versions, fees, and token primitives. `sdk` builds on top of it +with the trading entities, fetcher, and router. + +## Why + +Consumers have very different needs. A frontend that only renders chain names and stablecoin +addresses has no reason to pull in JSBI, ethers, and the V2 math. A swap UI does. Splitting the +project into a base metadata package and a trading SDK keeps each install minimal and lets the +two evolve on independent release cycles. + +## Core principle + +**One concern per package.** `protocol-core` is pure data and helpers — no on-chain calls, no +math libraries. `sdk` is the V2 trading layer — entities, pricing, and reserve fetching. The +boundary is enforced by the dependency direction: `sdk` depends on `protocol-core`, never the +other way around. + +## Packages + +| Package | Role | Human analogue | +|---|---|---| +| `@quickswap-defi/protocol-core` | Chain registry, fees, stablecoins, natives | The atlas — knows where things are | +| `@quickswap-defi/sdk` | V2 entities, pair math, trade construction | The calculator — knows what to do with them | + +## How they work together + +1. **Look up the chain** — pick a chain from the registry, read its protocol versions and tokens. +2. **Build the trade** — use the SDK's `Token`, `Pair`, `Route`, and `Trade` to compute outputs. + +Full sequence in [flows/trade-execution.md](./flows/trade-execution.md). Visual in +[diagrams/c2-containers.mmd](./diagrams/c2-containers.mmd). + +## Multi-chain support + +The chain registry is the single source of truth for which networks the SDK supports and what +their on-chain configuration looks like. See [packages/protocol-core.md](./packages/protocol-core.md) +for the export surface and [glossary.md](./glossary.md) for the underlying concepts (`ChainId`, +`Factory`, `Init Code Hash`). diff --git a/docs/packages/protocol-core.md b/docs/packages/protocol-core.md new file mode 100644 index 0000000..53c222b --- /dev/null +++ b/docs/packages/protocol-core.md @@ -0,0 +1,40 @@ +# protocol-core — Reference + +## Role + +Pure protocol metadata for QuickSwap. Exposes the chain registry, protocol version helpers, fee +constants, and token primitives (natives, stablecoins). No on-chain calls, no math libraries. + +## Package + +`@quickswap-defi/protocol-core` — published with provenance. + +## Public exports + +- **Registry**: `CHAIN_REGISTRY`, `CHAIN_ID`, `getChain`, `getChainOrThrow`, `getSupportedChainIds` +- **Per-chain configs**: `POLYGON`, `BASE`, `MANTRA`, `MANTA`, `SONEIUM`, `SOMNIA`, `IMX`, `XLAYER`, `ZKEVM`, `DOGECHAIN`, `ETHEREUM` +- **Types**: `ChainConfig`, `ChainProtocolEntry`, `ProtocolVersion`, `SchemaVariant`, `TokenInfo`; constants `PROTOCOL_VERSIONS`, `SCHEMA_VARIANTS` +- **Protocol helpers**: `getSchemaVariant`, `getSupportedVersions`, `getProtocolVersionLabel` +- **Fees**: `V2_FEE_BPS`, `V2_FEE_RATE`, `computeV2Fee` +- **Tokens**: `getNativeToken`, `getWrappedNative`, `getStablecoins`, `getStablecoinAddresses`, `isStablecoin` + +## Inputs + +- A `chainId: number` for any lookup helper. + +## Outputs + +- Frozen `ChainConfig` objects, version labels, fee values, token metadata. + +## Depends on + +- None at runtime. TypeScript-only package. + +## Consumed by + +- `@quickswap-defi/sdk` (sibling package). +- External consumers (frontends, indexers) that need protocol metadata without trading logic. + +## Status + +Published. Source of truth for QuickSwap chain configuration. diff --git a/docs/packages/sdk.md b/docs/packages/sdk.md new file mode 100644 index 0000000..cd8e32b --- /dev/null +++ b/docs/packages/sdk.md @@ -0,0 +1,39 @@ +# sdk — Reference + +## Role + +Uniswap V2-fork trading SDK for QuickSwap. Provides the entity model (`Token`, `Pair`, `Route`, +`Trade`), fraction-based pricing math, an on-chain reserve fetcher, and router helpers for +building swap calldata. + +## Package + +`@quickswap-defi/sdk` — published with provenance. + +## Public exports + +- **Entities**: `Token`, `Currency`, `Pair`, `Route`, `Trade` +- **Fractions**: `Fraction`, `Percent`, `Price`, `CurrencyAmount`, `TokenAmount` +- **Constants**: `ChainId`, `TradeType`, `Rounding`, `FACTORY_ADDRESS`, `INIT_CODE_HASH`, `MINIMUM_LIQUIDITY`, `BigintIsh`, `JSBI` +- **Fetcher / Router / Errors**: on-chain reads, swap calldata builders, typed error classes + +## Inputs + +- `Token` definitions (chain id, address, decimals). +- An ethers v5 provider for on-chain fetcher calls. +- Trade parameters (input/output amounts, slippage, deadline). + +## Outputs + +- `Trade` instances with execution params, derived bounds, and price impact. +- Router calldata ready to be sent to the V2 router contract. + +## Depends on + +- `@quickswap-defi/protocol-core` (sibling package). +- `jsbi`, `big.js`, `decimal.js-light`, `tiny-invariant`, `tiny-warning`, `toformat`. +- ethers v5 (`@ethersproject/*`) — peer dependencies. + +## Status + +Published. Tested with Vitest in `packages/sdk/test/`.