Skip to content
Merged
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
103 changes: 66 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,80 @@
# QuickSwap SDK
<p align="center">
<strong>QuickSwap SDK</strong><br>
<em>TypeScript building blocks for QuickSwap V2 trading and multi-chain protocol metadata.</em>
</p>

This repository has been forked from [UniswapV2](https://github.com/Uniswap/uniswap-sdk)
<p align="center">
<a href="docs/overview.md">Overview</a> &bull;
<a href="docs/packages/">Packages</a> &bull;
<a href="docs/flows/">Flows</a> &bull;
<a href="docs/diagrams/">Diagrams</a> &bull;
<a href="docs/glossary.md">Glossary</a> &bull;
<a href="docs/adr/">ADRs</a>
</p>

## 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.
37 changes: 37 additions & 0 deletions docs/adr/001-protocol-core-split.md
Original file line number Diff line number Diff line change
@@ -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`).
35 changes: 35 additions & 0 deletions docs/adr/002-jsbi-over-bigint.md
Original file line number Diff line number Diff line change
@@ -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`.
29 changes: 29 additions & 0 deletions docs/diagrams/c1-context.mmd
Original file line number Diff line number Diff line change
@@ -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<br/><i>Swap UIs, aggregators</i>"]
ACTOR_2["Backend / Indexer<br/><i>Analytics, off-chain services</i>"]
end

SYSTEM[["QuickSwap SDK<br/><b>V2 entities + multi-chain protocol metadata</b>"]]

subgraph EXT["External Systems"]
EXT_1["EVM Chains<br/><i>Polygon, Base, zkEVM, others</i>"]
EXT_2["ethers v5<br/><i>RPC provider library</i>"]
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
33 changes: 33 additions & 0 deletions docs/diagrams/c2-containers.mmd
Original file line number Diff line number Diff line change
@@ -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<br/><i>Imports the SDK</i>"]
ETHERS["ethers v5<br/><i>Peer dependency</i>"]
JSBI_LIB["JSBI<br/><i>BigInt polyfill</i>"]
CHAIN["EVM Chain RPC<br/><i>On-chain reads</i>"]
end

subgraph SYSTEM["QuickSwap SDK monorepo"]
subgraph PACKAGES["Packages"]
CORE["@quickswap-defi/protocol-core<br/><i>Chains, fees, tokens, versions</i>"]
SDK["@quickswap-defi/sdk<br/><i>V2 entities, fetcher, router</i>"]
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
30 changes: 30 additions & 0 deletions docs/diagrams/flow-trade.mmd
Original file line number Diff line number Diff line change
@@ -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
37 changes: 37 additions & 0 deletions docs/flows/chain-onboarding.md
Original file line number Diff line number Diff line change
@@ -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/<chain>.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.
40 changes: 40 additions & 0 deletions docs/flows/trade-execution.md
Original file line number Diff line number Diff line change
@@ -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.
Loading