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
2 changes: 1 addition & 1 deletion scripts/lint-no-bare-catch.mts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type CatchFinding = {
const repositoryRoot = path.dirname(url.fileURLToPath(import.meta.url))
const projectRoot = path.join(repositoryRoot, '..')
const sourceFileExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts'])
const ignoredPathPrefixes = ['.git', 'node_modules', 'ui/node_modules', 'ui/dist', 'ui/vendor', 'ui/js', 'shared/js', 'solidity/artifacts', 'solidity/js', 'solidity/node_modules', 'coverage']
const ignoredPathPrefixes = ['.git', 'node_modules', 'ui/node_modules', 'ui/dist', 'ui/vendor', 'ui/js', 'shared/js', 'solidity/artifacts', 'solidity/js', 'solidity/node_modules', 'trading/artifacts', 'trading/ts/artifacts', 'trading/ui/dist', 'trading/ui/ts/generated', 'coverage']
const ignoredFiles = new Set(['solidity/ts/testSupport/simulator/types/wire-types.js'])

function shouldIgnore(relativePath: string): boolean {
Expand Down
1 change: 1 addition & 0 deletions trading/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ COPY package.json bun.lock ./
COPY scripts ./scripts
COPY shared ./shared
COPY solidity/contracts ./solidity/contracts
COPY docs/mainnet-deployment-addresses.json docs/sepolia-deployment-addresses.json ./docs/
COPY trading ./trading
RUN bun install --frozen-lockfile
RUN bun run shared:build
Expand Down
3 changes: 3 additions & 0 deletions trading/Dockerfile.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
!solidity/
!solidity/contracts/
!solidity/contracts/**
!docs/
!docs/mainnet-deployment-addresses.json
!docs/sepolia-deployment-addresses.json
!trading/
!trading/package.json
!trading/bun.lock
Expand Down
45 changes: 18 additions & 27 deletions trading/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,49 @@ The pair trades only YES and NO. Every ETH entry creates a complete set, swaps t

## Quick setup

From this directory, build and start the live UI:

```bash
cd trading
bun install --frozen-lockfile
bun run compile
bun run test
bun run ui:build
bun run ui:serve
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
docker compose up --build --force-recreate
```

Open `http://localhost:4163/?demo=1#/markets`. Demo mode is prominently labeled and makes no live-chain claims.
Open `http://localhost:4163/#/markets`. Select a network whose canonical Zoltar core deployment is installed, enter its RPC URL, and connect a wallet. The repository's public-network manifests contain planned deterministic addresses; the UI verifies the required code before it offers a trading deployment transaction.

### Docker
On Windows, run `start.bat` from this directory to start the same Compose command. The final image runs as an unprivileged user and exposes a health check at `/`.

Build and serve the standalone demo UI from this directory:
### Local development and demo

```bash
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
docker compose up --build --force-recreate
bun install --frozen-lockfile
bun run compile
bun run test
bun run ui:build
bun run ui:serve
```

On Windows, run `start.bat` from this directory to start the same Compose command.

Then open `http://localhost:4163/?demo=1#/markets`. The final image runs as an unprivileged user and exposes a health check at `/`.
Open `http://localhost:4163/?demo=1#/markets`. Demo mode is prominently labeled and makes no live-chain claims.

Without a deployment build argument, the image contains `deployment.json` set to `null` and supports demo mode only. Live use requires a build with a reviewed manifest.
The Docker image copies the canonical mainnet and Sepolia core deployment addresses from the root documentation manifests. The live UI uses the installed core deployment's deterministic proxy to deploy the two-way factory and router in two wallet transactions. It verifies the RPC chain, core contracts, deterministic addresses, immutable fee, and router-to-factory link before enabling trading.

For live use, include a reviewed project-local deployment manifest at build time. The path is relative to `trading/` inside the build context:
To use an existing reviewed trading deployment instead, first copy its manifest into `trading/deployments/`. Then set its project-local path at build time:

```bash
cp /absolute/path/to/reviewed.json deployments/reviewed.json
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
TRADING_UI_DEPLOYMENT=deployments/local.json docker compose up --build --force-recreate
TRADING_UI_DEPLOYMENT=deployments/reviewed.json docker compose up --build --force-recreate
```

### Live deployment

Without Docker, build with a reviewed deployment manifest and open the same routes without `?demo=1`:
Without Docker, `bun run ui:build` includes the same wallet deployment setup. To use an existing reviewed deployment manifest instead:

```bash
TRADING_UI_DEPLOYMENT=/absolute/path/to/trading/deployments/local.json bun run ui:build
```

The live client validates the manifest, discovers canonical SecurityPools in bounded pages, displays their exact pairs, settings, and status, and obtains authoritative simulations before entry, exit, liquidity, settlement, and explicit fork-migration transactions. Fork migration loads the fork question and supports labeled categorical branches or arbitrary scalar ticks, including multi-branch migration for each INVALID, YES, or NO source balance. Each simulation is pinned to a canonical block hash; the client rejects a quote when either its block number or hash changes, including a same-height block replacement, and re-simulates immediately before wallet submission.

For a local deployment, first deploy Zoltar core to Anvil, then:

```bash
cp .env.example .env
ZOLTAR_DEPLOYMENT_MANIFEST=/absolute/path/to/core.json bun run deploy:local
```

The script verifies that the configured core `SecurityPoolFactory` has bytecode on the selected chain, deploys a factory with an immutable fee, deploys the router, and writes `deployments/local.json`.

## Commands

| Command | Purpose |
Expand Down
2 changes: 1 addition & 1 deletion trading/deployments/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

`deploy:local` writes `local.json` here. It records the chain, input core `SecurityPoolFactory`, immutable fee, deployed trading factory and router, compiler settings, bytecode hashes, and transaction hashes. `local.json` is ignored because local addresses are ephemeral.

No public-network address is bundled. Build the live standalone UI with `TRADING_UI_DEPLOYMENT=/absolute/path/to/local.json bun run ui:build`; the build copies the manifest to the untracked UI output, and the live client validates it at startup. The client then discovers canonical pools and pairs, simulates router calls, and submits through the connected wallet. See [UI configuration](../docs/how-to/configure-ui.md) for the complete schema and runtime requirements.
No completed public trading deployment manifest is bundled. The default build includes planned mainnet and Sepolia core addresses from the root manifests, then lets the wallet deploy and verify the trading factory and router when that core code is installed. To use an existing trading manifest instead, build with `TRADING_UI_DEPLOYMENT=/absolute/path/to/local.json bun run ui:build`; the build copies it to the untracked UI output, and the live client validates it at startup. See [Deploy the trading contracts](../docs/how-to/deploy.md) for the wallet path and [UI configuration](../docs/how-to/configure-ui.md) for the complete schema and runtime requirements.
2 changes: 1 addition & 1 deletion trading/docs/explanation/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

This MVP intentionally does not implement a three-way invariant, INVALID trading, an invalidity-probability oracle, weighted reserves, quadratic solvers, flash swaps, a protocol fee, governance controls, upgradeable proxies, automatic branch selection, automatic LP migration, an insured-position NFT, or per-user on-chain position accounting.

It also has no TWAP, routing across markets, guaranteed deep liquidity, or mechanism to withdraw more early ETH than complete-set insurance and reserves permit. The UI’s local demo states are visual fixtures, not live-chain evidence; live mode requires an explicit deployment manifest. Public deployments require an independently reviewed manifest, gas benchmarks against a real-core funded lifecycle fixture, adversarial integration testing, and an external audit.
It also has no TWAP, routing across markets, guaranteed deep liquidity, or mechanism to withdraw more early ETH than complete-set insurance and reserves permit. The UI’s local demo states are visual fixtures, not live-chain evidence. Live mode requires either a verified wallet deployment on a supported canonical core network or an explicit reviewed trading manifest. Public deployments require gas benchmarks against a real-core funded lifecycle fixture, adversarial integration testing, and an external audit.

Potential future work may add separate INVALID markets, safer oracle observations, routing, or bounded convenience flows. Those are distinct designs and must not weaken the invariant that this pair never accepts or holds INVALID.
8 changes: 5 additions & 3 deletions trading/docs/how-to/configure-ui.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Configure the standalone UI

The standalone UI has two explicitly separated modes. `?demo=1` provides walletless visual fixtures and is always labeled simulated; do not use demo screens as evidence of live chain state. Without that query parameter, the application requires a deployment manifest and reads live RPC and wallet state.
The standalone UI has two explicitly separated modes. `?demo=1` provides walletless visual fixtures and is always labeled simulated; do not use demo screens as evidence of live chain state. Without that query parameter, the application reads live RPC and wallet state.

`deploy:local` emits the authoritative nested manifest under `network`, `core`, and `trading`. Copy it into the built application with:
The build copies the canonical mainnet and Sepolia core factory and deterministic proxy addresses from the root deployment manifests. When no complete trading deployment is bundled or saved, open the live UI, select a core network, enter its RPC URL, and keep or change the immutable fee. The UI verifies the RPC chain and core bytecode automatically. Connect a wallet to deploy the deterministic trading factory, then the router. After verifying both contracts, the browser saves the completed configuration locally and opens live trading. A different fee selects a different deterministic factory and router.

For an existing local or reviewed trading deployment, `deploy:local` emits the authoritative nested manifest under `network`, `core`, and `trading`. Copy it into the built application with:

```bash
TRADING_UI_DEPLOYMENT=/absolute/path/to/trading/deployments/local.json bun run ui:build
```

The build copies it as untracked `ui/dist/deployment.json`. The parser also accepts the documented flat schema for deliberate hand-authored configurations. The live client validates required addresses and values, discovers pools from `SecurityPoolFactory` in bounded pages, isolates individual market-read failures, and never hard-codes market addresses. The wallet must report the manifest chain before any submission. Entry, exit, liquidity, settlement, and fork-migration calls are simulated through the actual contracts, rejected after a block change, and simulated again immediately before submission with explicit bounds where the call accepts them.
The build copies it as untracked `ui/dist/deployment.json`, which takes precedence over browser-saved configuration. The parser also accepts the documented flat schema for deliberate hand-authored configurations. The live client validates required addresses and values, discovers pools from `SecurityPoolFactory` in bounded pages, isolates individual market-read failures, and never hard-codes market addresses. The wallet must report the configured chain before any submission. Entry, exit, liquidity, settlement, and fork-migration calls are simulated through the actual contracts, rejected after a block change, and simulated again immediately before submission with explicit bounds where the call accepts them.

Serve built assets from the same origin. Production code may connect only to the configured RPC, the wallet provider, and explicit explorer links. Demo mode is unmistakably labeled and must never be presented as live state.
45 changes: 43 additions & 2 deletions trading/docs/how-to/deploy.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,46 @@
# Deploy the trading contracts

Compile first with `bun run compile`. Set `ZOLTAR_DEPLOYMENT_MANIFEST` to a reviewed manifest for the same Anvil chain, optionally set `TRADING_RPC_URL`, `TRADING_DEPLOYER`, and `TRADING_FEE_BPS`, then run `bun run deploy:local`.
## Use an installed public-network core

The script verifies code at the configured core `SecurityPoolFactory`, deploys `TwoWayConstantProductFactory(coreFactory, feeBps)`, deploys `TwoWayConstantProductRouter(factory)`, and records chain ID, inputs, outputs, transaction hashes, compiler settings, and bytecode hashes. The fee is immutable and no economically optimal value is claimed. No mainnet or Sepolia address is assumed.
The root mainnet and Sepolia manifests describe planned deterministic addresses; they do not prove that the contracts are live. Continue only after the selected network has reviewed core code at those addresses. See the root [deployment-status reference](../../../docs/reference/deployment-status.html) for that distinction.

From `trading/`, start the standalone UI:

```bash
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
docker compose up --build --force-recreate
```

Open `http://localhost:4163/#/deploy`, select the network, enter an HTTPS RPC URL and the immutable fee, then connect a wallet. The UI checks the RPC chain, canonical proxy, and core factory before enabling the first transaction. Submit the factory transaction, then the router transaction. Progress reaches `2 / 2` after both deterministic contracts and their immutable links have been verified. The flow resumes at the first missing contract if you return later.

## Use local Anvil

Return to the repository root (`cd ..` if you are still in `trading/`) and complete the root setup. Start Anvil as chain ID 1 so it uses the mainnet deterministic-address profile:

```bash
bun run anvil -- --chain-id 1 --block-base-fee-per-gas 0 --gas-price 0 --no-priority-fee
```

In another terminal, run `bun run app:serve`, open `http://localhost:12345/?rpcUrl=http://127.0.0.1:8545#/deploy`, connect an Anvil account, and use the root Zoltar deployment screen to install the core contracts. Wait until its deployment plan is complete.

From the repository root, start the trading Docker UI:

```bash
cd trading
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
docker compose up --build --force-recreate
```

Open `http://localhost:4163/#/deploy` and follow the public-network steps above with **Ethereum Mainnet · chain 1** and `http://127.0.0.1:8545`. The browser will deploy the trading factory and router through the same deterministic proxy.

For a generated trading manifest instead of browser storage, open another terminal in the repository root, enter `trading/`, and run the local deployment script against the matching root manifest:

```bash
cd trading
bun run compile
ZOLTAR_DEPLOYMENT_MANIFEST=../docs/mainnet-deployment-addresses.json \
TRADING_RPC_URL=http://127.0.0.1:8545 \
bun run deploy:local
```

The script verifies code at the configured core `SecurityPoolFactory`, deploys `TwoWayConstantProductFactory(coreFactory, feeBps)`, deploys `TwoWayConstantProductRouter(factory)`, and records chain ID, inputs, outputs, transaction hashes, compiler settings, and bytecode hashes. The fee is immutable and no economically optimal value is claimed.
2 changes: 2 additions & 0 deletions trading/docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ Environment variables used by local deployment:
| `TRADING_FEE_BPS` | Immutable AMM fee | `30` |

The live UI directly accepts the nested `deploy:local` manifest. It also accepts a flat JSON schema with numeric `chainId` and `feeBps`, string `chainName` and `rpcUrl`, and addresses `securityPoolFactory`, `factory`, and `router`. Set `TRADING_UI_DEPLOYMENT` while building to copy a reviewed manifest to untracked `ui/dist/deployment.json`; see [UI configuration](../how-to/configure-ui.md). Secrets do not belong in manifests or `.env.example`.

Without `TRADING_UI_DEPLOYMENT`, the build writes `deployment.json` as `null` and writes `core-deployments.json` from the root mainnet and Sepolia deployment manifests. The live setup screen accepts a supported chain, an HTTPS or loopback HTTP RPC URL, and an immutable fee from 0 to 9999 basis points. It computes the trading factory and router through the core deployment's canonical CREATE2 proxy, verifies or deploys each contract through the wallet, and stores the completed configuration under `zoltar.trading.deployment.v1` in browser local storage.
Loading
Loading