# Local development (/developers/local-development)



## Toolchain [#toolchain]

| Tool    | Version |
| ------- | ------- |
| Node.js | 24.19.0 |
| pnpm    | 11.22.0 |
| Aiken   | 1.1.23  |

The root `package.json` pins the Node engine and the package manager, and `onchain/aiken.toml`
pins the Aiken compiler.

The repository keeps a local copy of these under `.toolchain/`, activated with:

```bash
source .toolchain/env.sh
```

That script puts the pinned Node and Aiken on `PATH` and sources `secrets/koios.env` when it
exists, so a Koios bearer token is picked up without living in your shell profile. Both
directories are excluded from version control.

## Install and build [#install-and-build]

```bash
pnpm install
pnpm build
pnpm test
node apps/cli/dist/main.js help
```

`pnpm build` compiles `packages/**` first, then `apps/**`.

## Root scripts [#root-scripts]

| Script            | What it does                                                     |
| ----------------- | ---------------------------------------------------------------- |
| `pnpm build`      | Compiles every package, then every application.                  |
| `pnpm typecheck`  | Runs `tsc` across every workspace package.                       |
| `pnpm lint`       | ESLint across the repository.                                    |
| `pnpm test`       | The full Vitest suite, including tests that read the live chain. |
| `pnpm test:unit`  | The suite without `live-*.test.ts` files.                        |
| `pnpm test:watch` | Vitest in watch mode.                                            |
| `pnpm clean`      | Removes `dist` and build info from every package.                |

## Two TypeScript versions, on purpose [#two-typescript-versions-on-purpose]

Source is compiled by **TypeScript 7.0.2**, the native port.

`typescript-eslint` 8.67.0 declares a peer range that excludes TypeScript 7 and refuses to load
against it, because the TypeScript 7 package exports only a version string rather than the classic
compiler API.

So `tools/lint` is a workspace package whose only dependencies are `typescript-eslint` 8.67.0 and
`typescript` 6.0.3. Under pnpm's isolated `node_modules` the linter resolves the classic API from
there, while every source package is compiled by TypeScript 7.0.2. `eslint.config.js` at the root
loads the linter from that path.

TypeScript 6.0.3 exists only as the linter's own peer and compiles nothing.

TypeScript 7 changed three defaults, and the project sets all three explicitly rather than
inheriting them: `strict`, `module` and `types`.

## Testing [#testing]

The suite is Vitest 4.1.11.

`vitest.config.ts` includes `packages/**/src/**/*.test.ts` and `apps/**/src/**/*.test.ts`, and
excludes `e2e`. It aliases each `@cardano-swap/*` package to its source, so tests run against
source rather than build output.

### Test files run one at a time [#test-files-run-one-at-a-time]

`fileParallelism` is off. Almost every suite reads the live chain, and several build real
transactions from the one operator wallet. Run in parallel they contend for the same wallet UTxO
and hammer the provider hard enough to be rate limited, which makes tests fail for reasons that
have nothing to do with the code under test.

A slower suite that means what it says is worth more than a fast one that fails at random.

### Live tests [#live-tests]

Files named `live-*.test.ts` read the live chain. `pnpm test:unit` excludes them.

A Koios bearer token raises the rate limit well above the anonymous tier. Reads work without one,
and a full pool discovery across several venues is rate limited without it. Put it in
`secrets/koios.env`, which `.toolchain/env.sh` sources.

Tests that would spend or emit a transaction are gated behind their own environment variables and
do nothing by default. See [Configuration
reference](/developers/reference/configuration#test-only).

### Two emulators, chosen for a measured reason [#two-emulators-chosen-for-a-measured-reason]

The Lucid Evolution emulator imports no UPLC evaluator at all. It performs ledger bookkeeping
only, so it cannot catch a validator logic bug and its execution unit figures are whatever it was
given.

`@blaze-cardano/emulator` imports a real evaluator through `@blaze-cardano/vm` and checks budgets,
raising on an underestimated budget.

Use `@blaze-cardano/emulator` for any test where script correctness or execution unit accuracy
matters. Use `@lucid-evolution/lucid` for building transactions. This is a deliberate two library
split.

## Running the web application [#running-the-web-application]

```bash
cp apps/web/.env.example apps/web/.env
pnpm --filter @cardano-swap/web dev
```

`VITE_CARDANO_SWAP_NETWORK` must be set. There is no default network, and the app throws a named
error when it is missing.

The build replaces three Node modules with browser implementations, so the same configuration
loader runs in both places. `src/shims/node-fs.ts` serves the bundled `config/<network>.json`
files from a fixed virtual directory, and any other path throws. That keeps the browser and the
command line from drifting apart on the asset set.

### End to end tests [#end-to-end-tests]

```bash
pnpm --filter @cardano-swap/web test:e2e
```

Playwright starts a Vite server on `127.0.0.1:5273` and runs against live preprod, so the quote
the test asserts on is a real one at the chain tip of the moment. Set
`CARDANO_SWAP_E2E_NETWORK=mainnet` for the read only run that proves nothing in the interface is
tied to one network.

The signing step is skipped rather than faked: a headless browser has no CIP-30 extension, and a
faked one would test the fake.

## Running this documentation site [#running-this-documentation-site]

```bash
pnpm --filter @cardano-swap/docs dev
```

Serves on port 3100.

```bash
pnpm --filter @cardano-swap/docs build
```

Prerenders every page to static HTML in `docs/.output/public`. `docs/serve.json` carries the
rewrite rule a static host needs, sending anything outside `/api/` to the shell.

Content lives in `docs/content/docs`, and navigation order comes from the `meta.json` file in each
directory. A directory whose `meta.json` sets `"root": true` becomes a top level tab.

## The on-chain project [#the-on-chain-project]

```bash
cd onchain
aiken check
aiken build
```

`aiken build` writes the blueprint to `onchain/plutus.json`, which is what
`cardano-swap deploy show` and `deploy settlement` read.

`aiken check` runs the validator's own tests, which are in `validators/settlement.ak` alongside
the validator.

## Repository conventions [#repository-conventions]

Internal artifacts, working notes and scratch work live in `resources/` and `scratch/`, both
excluded from version control. Anything worth keeping becomes a command or a document.

`footprint.*.json` is a published artifact and **is** committed, which the `.gitignore` states
explicitly.

## Related [#related]

<Cards>
  <Card title="Architecture" href="/developers" />

  <Card title="Command line" href="/developers/reference/command-line" />

  <Card title="Configuration reference" href="/developers/reference/configuration" />
</Cards>
