Skip to content

Commit 4d7c71d

Browse files
authored
Require deployment manifest for trading Docker (#714)
* Require deployment manifest for trading Docker * Add wallet trading deployment flow * Tighten trading deployment client tests * Polish trading deployment setup * Harden trading deployment handoff * Validate stored trading deployments * Verify stored deployment RPC chain * Lock trading deployment inputs and route * Hydrate resolved trading deployment setup * Harden trading deployment setup state handling - Prevent stale registry data and actions during refresh failures - Preserve pending deployment state while configuration hydrates - Improve recovery errors, validation messages, and QA coverage
1 parent e997dc2 commit 4d7c71d

31 files changed

Lines changed: 1604 additions & 68 deletions

scripts/lint-no-bare-catch.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type CatchFinding = {
1313
const repositoryRoot = path.dirname(url.fileURLToPath(import.meta.url))
1414
const projectRoot = path.join(repositoryRoot, '..')
1515
const sourceFileExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts'])
16-
const ignoredPathPrefixes = ['.git', 'node_modules', 'ui/node_modules', 'ui/dist', 'ui/vendor', 'ui/js', 'shared/js', 'solidity/artifacts', 'solidity/js', 'solidity/node_modules', 'coverage']
16+
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']
1717
const ignoredFiles = new Set(['solidity/ts/testSupport/simulator/types/wire-types.js'])
1818

1919
function shouldIgnore(relativePath: string): boolean {

trading/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ COPY package.json bun.lock ./
99
COPY scripts ./scripts
1010
COPY shared ./shared
1111
COPY solidity/contracts ./solidity/contracts
12+
COPY docs/mainnet-deployment-addresses.json docs/sepolia-deployment-addresses.json ./docs/
1213
COPY trading ./trading
1314
RUN bun install --frozen-lockfile
1415
RUN bun run shared:build

trading/Dockerfile.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
!solidity/
1313
!solidity/contracts/
1414
!solidity/contracts/**
15+
!docs/
16+
!docs/mainnet-deployment-addresses.json
17+
!docs/sepolia-deployment-addresses.json
1518
!trading/
1619
!trading/package.json
1720
!trading/bun.lock

trading/README.md

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,49 @@ The pair trades only YES and NO. Every ETH entry creates a complete set, swaps t
1616

1717
## Quick setup
1818

19+
From this directory, build and start the live UI:
20+
1921
```bash
20-
cd trading
21-
bun install --frozen-lockfile
22-
bun run compile
23-
bun run test
24-
bun run ui:build
25-
bun run ui:serve
22+
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
23+
docker compose up --build --force-recreate
2624
```
2725

28-
Open `http://localhost:4163/?demo=1#/markets`. Demo mode is prominently labeled and makes no live-chain claims.
26+
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.
2927

30-
### Docker
28+
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 `/`.
3129

32-
Build and serve the standalone demo UI from this directory:
30+
### Local development and demo
3331

3432
```bash
35-
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
36-
docker compose up --build --force-recreate
33+
bun install --frozen-lockfile
34+
bun run compile
35+
bun run test
36+
bun run ui:build
37+
bun run ui:serve
3738
```
3839

39-
On Windows, run `start.bat` from this directory to start the same Compose command.
40-
41-
Then open `http://localhost:4163/?demo=1#/markets`. The final image runs as an unprivileged user and exposes a health check at `/`.
40+
Open `http://localhost:4163/?demo=1#/markets`. Demo mode is prominently labeled and makes no live-chain claims.
4241

43-
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.
42+
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.
4443

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

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

5252
### Live deployment
5353

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

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

6060
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.
6161

62-
For a local deployment, first deploy Zoltar core to Anvil, then:
63-
64-
```bash
65-
cp .env.example .env
66-
ZOLTAR_DEPLOYMENT_MANIFEST=/absolute/path/to/core.json bun run deploy:local
67-
```
68-
69-
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`.
70-
7162
## Commands
7263

7364
| Command | Purpose |

trading/deployments/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
`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.
44

5-
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.
5+
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.

trading/docs/explanation/limitations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
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.
44

5-
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.
5+
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.
66

77
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.
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# Configure the standalone UI
22

3-
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.
3+
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.
44

5-
`deploy:local` emits the authoritative nested manifest under `network`, `core`, and `trading`. Copy it into the built application with:
5+
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.
6+
7+
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:
68

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

11-
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.
13+
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.
1214

1315
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.

trading/docs/how-to/deploy.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,46 @@
11
# Deploy the trading contracts
22

3-
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`.
3+
## Use an installed public-network core
44

5-
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.
5+
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.
6+
7+
From `trading/`, start the standalone UI:
8+
9+
```bash
10+
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
11+
docker compose up --build --force-recreate
12+
```
13+
14+
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.
15+
16+
## Use local Anvil
17+
18+
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:
19+
20+
```bash
21+
bun run anvil -- --chain-id 1 --block-base-fee-per-gas 0 --gas-price 0 --no-priority-fee
22+
```
23+
24+
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.
25+
26+
From the repository root, start the trading Docker UI:
27+
28+
```bash
29+
cd trading
30+
docker network inspect zoltar >/dev/null 2>&1 || docker network create zoltar
31+
docker compose up --build --force-recreate
32+
```
33+
34+
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.
35+
36+
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:
37+
38+
```bash
39+
cd trading
40+
bun run compile
41+
ZOLTAR_DEPLOYMENT_MANIFEST=../docs/mainnet-deployment-addresses.json \
42+
TRADING_RPC_URL=http://127.0.0.1:8545 \
43+
bun run deploy:local
44+
```
45+
46+
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.

trading/docs/reference/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ Environment variables used by local deployment:
1010
| `TRADING_FEE_BPS` | Immutable AMM fee | `30` |
1111

1212
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`.
13+
14+
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.

0 commit comments

Comments
 (0)