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
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[alias]
# `cargo xtask <cmd>` — see xtask/src/main.rs or `cargo xtask help`.
xtask = "run --quiet --package xtask --"
109 changes: 56 additions & 53 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ on:
branches: [main]
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

# Every job goes through `cargo xtask` (xtask/src/main.rs). Contracts that
# contractimport! other contracts' wasm declare them in
# `[package.metadata.zenith] wasm-deps`, and xtask builds that wasm in
# dependency order into the shared target/ — so there are no per-crate
# build chains here. `cargo xtask ci` runs all of it locally.
#
# options_market cross-contract-calls price_oracle, multisig, vault, AND
# params (its parameter registry) via
# soroban-sdk's contractimport! against their compiled wasm (not a normal
Expand All @@ -24,38 +37,18 @@ on:
#
crate: [options_market, price_oracle, vault, multisig, streams, staking, timelock, governor, lp_token, lp_pool, integration_tests]
jobs:
test:
fmt:
runs-on: ubuntu-latest
strategy:
matrix:
crate: [options_market, price_oracle, vault, multisig, streams, staking, timelock, governor, lp_token, lp_pool, integration_tests]
crate: [options_market, price_oracle, vault, multisig, common, streams, staking, timelock, governor, params, grants_escrow, lp_token, lp_pool, integration_tests, tools/spec-diff, tools/resource-sna
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
components: rustfmt
- uses: Swatinem/rust-cache@v2
- if: matrix.crate == 'options_market' || matrix.crate == 'price_oracle' || matrix.crate == 'vault' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: multisig
- if: matrix.crate == 'options_market' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: price_oracle
- if: matrix.crate == 'options_market' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: vault
- if: matrix.crate == 'options_market'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: params
- run: cargo build --all-targets
working-directory: ${{ matrix.crate }}
- run: cargo test
working-directory: ${{ matrix.crate }}
# Ledger invariant post-conditions compiled in, plus the slow
# 10,000-op fuzz test that's #[ignore]d in the default run.
- if: matrix.crate == 'vault'
run: cargo test --features invariants -- --include-ignored
working-directory: vault
- run: cargo xtask fmt --check

clippy:
runs-on: ubuntu-latest
Expand All @@ -69,22 +62,10 @@ jobs:
components: clippy
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- if: matrix.crate == 'options_market' || matrix.crate == 'price_oracle' || matrix.crate == 'vault' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: multisig
- if: matrix.crate == 'options_market' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: price_oracle
- if: matrix.crate == 'options_market' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: vault
- if: matrix.crate == 'options_market'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: params
- run: cargo clippy --all-targets -- -D warnings
working-directory: ${{ matrix.crate }}
- run: cargo xtask check-imports
- run: cargo xtask clippy

fmt:
test:
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -108,20 +89,42 @@ jobs:
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- if: matrix.crate == 'options_market' || matrix.crate == 'price_oracle' || matrix.crate == 'vault' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: multisig
- if: matrix.crate == 'options_market' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: price_oracle
- if: matrix.crate == 'options_market' || matrix.crate == 'integration_tests'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: vault
- if: matrix.crate == 'options_market'
run: cargo build --target wasm32-unknown-unknown --release
working-directory: params
- run: cargo build --target wasm32-unknown-unknown --release
working-directory: ${{ matrix.crate }}
- run: cargo xtask test

wasm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- run: cargo xtask build
# Fails if soroban-sdk/testutils
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- run: cargo xtask test

wasm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- run: cargo xtask build
# Fails if soroban-sdk/testutils (or the host) leaked into any
# release wasm through workspace feature unification.
- run: cargo xtask check-wasm
- uses: actions/upload-artifact@v4
with:
name: wasm
path: target/wasm32-unknown-unknown/release/*.wasm

# Public-ABI guard: every existing function, type and error in each
# contract's spec must survive unchanged (additions are fine) unless
Expand Down
56 changes: 56 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[workspace]
# resolver 2 keeps dev-dependency features (every `features = ["testutils"]`
# below) out of plain `cargo build` graphs, and xtask builds each contract's
# wasm with its own `-p` invocation so no other member's features are
# unified into it. `cargo xtask check-wasm` verifies the result.
resolver = "2"
members = [
"common",
"governor",
"grants_escrow",
"integration_tests",
"multisig",
"options_market",
"params",
"price_oracle",
"staking",
"streams",
"timelock",
"vault",
"tools/spec-diff",
"xtask",
]

[workspace.package]
version = "0.1.0"
edition = "2021"
publish = false

[workspace.dependencies]
soroban-sdk = { version = "21.7.7" }
ed25519-dalek = "2"

# Internal crates. Contracts that call each other on-chain use
# `contractimport!` against the compiled wasm (see
# `[package.metadata.zenith] wasm-deps` in each manifest); these entries are
# only for dev-dependencies that register a real instance in a test Env,
# plus the zenith-common rlib.
zenith-common = { path = "common" }
governor = { package = "zenith-governor", path = "governor" }
multisig = { package = "zenith-multisig", path = "multisig" }
options_market = { package = "zenith-options-market", path = "options_market" }
params = { package = "zenith-params", path = "params" }
price_oracle = { package = "zenith-price-oracle", path = "price_oracle" }
timelock = { package = "zenith-timelock", path = "timelock" }
vault = { package = "zenith-vault", path = "vault" }

# Shared by every contract (profiles in member manifests are ignored inside
# a workspace).
[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
60 changes: 20 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,50 +77,30 @@ Soroban (Stellar smart contract) crates for the Zenith options protocol.

## Building and testing

Each crate is standalone (no workspace `Cargo.toml`), but **build order
matters for options_market**: it cross-calls price_oracle and vault via
soroban-sdk's `contractimport!` against their *compiled wasm* (not a
normal source dependency — that would link the other contract's own
functions into the caller's wasm and collide with functions of the same
name, like `pause`/`transfer_admin`). So those two wasm files have to
exist before options_market can be compiled at all, even natively.
Multisig is cross-called through `zenith-common` with `invoke_contract`
instead, so nothing needs multisig's wasm to compile:
The repo is one Cargo workspace (root `Cargo.toml`: shared
`[profile.release]` and `soroban-sdk` version). Build order is handled by
`cargo xtask` — you need Rust stable plus the wasm target:

```sh
cd price_oracle
cargo build --target wasm32-unknown-unknown --release # options_market needs it

cd ../vault
cargo build --target wasm32-unknown-unknown --release # options_market needs it too

cd ../params
cargo build --target wasm32-unknown-unknown --release # options_market needs it too (no dependencies of its own)

cd ../options_market # now this crate can build/test/etc.
cargo build # native build, fast iteration
cargo test # unit tests (soroban-sdk testutils)
cargo clippy --all-targets -- -D warnings # matches CI
cargo fmt --check # matches CI
cargo build --target wasm32-unknown-unknown --release # the real deploy artifact
rustup target add wasm32-unknown-unknown
cargo xtask build # every contract's wasm, dependencies first
cargo xtask test # builds the wasm tests need, then cargo test
cargo xtask ci # fmt --check, clippy, wasm checks, tests — same as CI
```

`params` and `grants_escrow` have no wasm dependencies of their own.
`vault` only needs multisig's wasm built first, no other dependency of
its own (options_market depending on vault's wasm doesn't run the other
way). `multisig` itself has no dependency on anything else and can be
built/tested independently, in any order relative to the others.

CI (`.github/workflows/ci.yml`) builds the required dependency wasm(s)
first whenever a job is about to touch options_market, price_oracle,
or vault, then runs the same four checks against every push and PR,
for every crate. On PRs a
`spec-diff` job (`tools/spec-diff/check.sh <base-ref>`) builds every
contract at the base branch and at the PR, fails if any existing
function, type or error in a contract's spec was removed or changed
(additions are fine; intentional signature changes must be listed in
`tools/spec-diff/allow.txt`), and prints each contract's wasm size
delta.
Add `-p <crate>` (e.g. `-p options_market`) to scope any command. Release
wasm lands in `target/wasm32-unknown-unknown/release/`. `cargo xtask help`
lists the rest (`clippy`, `fmt`, `graph`, `check-wasm`, `check-imports`).

Why an xtask: options_market calls price_oracle, vault and params through
`contractimport!` against their *compiled wasm* (a source dependency
would link their `pause`/`transfer_admin`/... into options_market's wasm
and collide), so that wasm must exist before options_market compiles at
all. A crate that imports wasm lists it in `[package.metadata.zenith]
wasm-deps`; xtask builds those first, and `check-imports` keeps the list
in sync with the `contractimport!` paths. Each contract's wasm is built on
its own so dev-dependency `testutils` never reaches a release build —
`check-wasm` verifies that on the artifacts.

### Wasm size budgets

Expand Down
Loading