Skip to content

feat: check the whole wiring before setup closes - #275

Open
hpmaxi wants to merge 8 commits into
fix/single-admin-signaturefrom
feat/full-wiring-check
Open

hpmaxi wants to merge 8 commits into
fix/single-admin-signaturefrom
feat/full-wiring-check

Conversation

@hpmaxi

@hpmaxi hpmaxi commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #326

A vault could finish setup with part of its wiring wrong. finish_setup checked the bindings, the token's manager role, the share supply, the allowlist and the freeze. It did not check that the asset has the share token's decimals, that the asset, the token, the oracle and the vault are four different addresses, that the oracle answers the feed's views, or that a custodian is set. The vault's own address was also accepted as its custodian.

Now finish_setup refuses with SetupIncomplete until each of these holds, and health() reports each one as its own field. Every path that sets the custodian refuses the vault's own address with a new error, InvalidCustodian.

Changes

  • finish_setup also needs the asset to have the share token's decimals, four different addresses for the asset, the token, the oracle and the vault, an oracle that answers state and freshness_duration, and a custodian that is not the vault
  • Each new check calls out with try_ and counts an error as false; a new feed with no report still passes
  • health() gains decimals_match, addresses_distinct, custodian_set and feed_answers
  • set_custodian (during setup and after) and a Custodian change (when proposed and when applied) refuse the vault's own address with InvalidCustodian (6075), through a new custodian_not_the_vault guard
  • An Oracle change refuses, with FeedNotBound, a contract that does not answer state and freshness_duration, when proposed and again when applied. It used to pass the proposal and trap at apply. The setup check and the swap use one helper
  • The test fixture sets the custodian on every wired vault; five new Miswire rows cover the new checks in the setup and health tables
  • The harness deploy stops if a new health() field is false; a new guards check refuses the vault as its own custodian on a real network
  • The kit spec, the design doc, the authorization matrix, the testing guide and the README follow

Deviations

  • set_custodian checks the address before the setup state, so after setup the vault's own address fails with InvalidCustodian, not UseTimelock.
  • New harness check: governance cannot make the vault its own custodian.
  • Besides the Health table and runbook, the kit spec's other setup and custodian lines, the design doc, the authorization matrix, the testing guide and the README's sample output list the new checks.
  • The oracle swap now makes setup's feed check; the issue covered setup only.
  • The health table test now names the row that fails.

Acceptance criteria

  • One setup test per new check: each miswiring is refused with SetupIncomplete, and its health() field is false
  • A custodian equal to the vault is refused everywhere it can be set
  • The harness deploy checks the new health() fields and passes on a local network
  • The kit spec's Health table and setup runbook list the new checks

Test plan

Automated tests

  1. cargo test --locked: 681 passed (435 vault, 98 oracle, 80 token, 36 compliance, 7 identity verifier, 19 timelock, 6 storage). Four tests are new, and the setup and health tables have five new rows each. Each of the five commits passes on its own.
  2. cargo clippy --locked --all-targets --workspace -- -D warnings, and on the vault with --features short_wind_down: clean.
  3. The Wasm upgrade tests (cargo test -p async-vault -p nav-oracle --features upgrade_wasm with_wasm, after the builds in docs/TESTING.md): vault 5 and oracle 2 pass.
  4. CI runs the harness deploy, happy path and guards on a local network.

Manual verification

  1. Start a local network: docker run --rm -d -p 8000:8000 --name stellar-local stellar/quickstart --local --enable core,rpc.
  2. Run stellar contract build, then CARGO_TARGET_DIR=target/short-wind-down stellar contract build --package async-vault --features short_wind_down.
  3. Run npm run build:clients, then HARNESS_NETWORK=local npm run deploy -w scripts/harness. The health line shows decimals_match, addresses_distinct, custodian_set and feed_answers true.
  4. Run test:happy-path, test:guards and test:wind-down with npm run <script> -w scripts/harness.
  5. You should see 24, 50 and 6 tests pass. The vault Wasm is 73,041 bytes (was 71,319); the other contracts are unchanged.

Breaking changes

Fresh deployments only.

  • async_vault's Health gains four fields: decimals_match, addresses_distinct, custodian_set and feed_answers. A client decoding health() needs the regenerated type.
  • New error VaultError::InvalidCustodian = 6075. set_custodian(vault), propose_change(Custodian(vault)) and applying such a change refuse the vault's own address. After setup, set_custodian(vault) fails with InvalidCustodian instead of UseTimelock.
  • finish_setup refuses with SetupIncomplete until a custodian is set, so a deploy calls set_custodian first (the harness already does). The asset must have the share token's decimals (7 today).
  • propose_change(Oracle(address)) and its apply_change refuse an address that does not answer state() and freshness_duration() with FeedNotBound (6072). Applying one used to fail with a host error instead. FeedNotBound's doc says so.
  • No entrypoint signature, argument or event changes.

Checklist

  • Self-reviewed my own diff
  • Tests added or updated
  • Docs updated (if applicable)
  • No unrelated changes bundled in

Screenshots

None.

set_custodian and a Custodian change accepted the vault's own address.
Capital deployed there never leaves, and the books count it as both held
and deployed. Every path that sets the custodian now refuses it with
InvalidCustodian: the direct setter during setup and after, and a
timelocked change when it is proposed and again when it applies.
finish_setup checked the bindings, the token's manager role, the share
supply, the allowlist and the freeze, but not the rest of the wiring.
It now also needs the asset to have the share token's decimals, four
different addresses for the asset, the token, the oracle and the vault,
an oracle that answers state and freshness_duration, and a custodian
that is not the vault. Each check calls out with try_ and counts an
error as false, and health() reports each one as a new field. The
harness deploy stops if any of them is false.
The kit spec's Health table, invariant, custodian rows, runbook and
adopter interfaces, the authorization matrix's deploy steps and the
testing guide list the new wiring checks and the refusal of the vault
as its own custodian. The README shows a deploy run that prints the new
health() fields.
@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
strata-vault-kit-app Ready Ready Preview Oct 23, 2026 3:32pm UTC

Request Review

An Oracle change checked only that the new address names this vault
with its setup closed. Another kit contract bound to the vault, such as
the compliance contract, passed at proposal and then trapped at apply
on a raw state() call. feed_bound_here now also needs state() and
freshness_duration() to answer, so such a swap is refused with
FeedNotBound when it is proposed and again when it applies. The setup
check and the swap guard share one helper.
The design doc's governance row lists the conditions setup closes on,
and the authorization matrix lists each vault entrypoint's guards. Both
now include matching decimals, four distinct addresses, an oracle that
answers the feed interface and a custodian other than the vault. The
matrix rows for set_custodian, propose_change and apply_change name
InvalidCustodian and its tests.
@hpmaxi
hpmaxi marked this pull request as ready for review October 20, 2026 03:59
Brings main into this branch through feedback.

Conflicts:
- README.md: the sample deploy run takes main's key and funding lines
  and keeps this branch's run.
- docs/kit-spec.md: keep this branch's text, with formulas in GitHub's
  dollar-backtick delimiters, as main now writes them.

This branch was successfully deployed

1 active deployment
Preview — 9d9ffbb3 Deployed Oct 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants