Skip to content

chore: merge feedback into main - #359

Open
luchobonatti wants to merge 540 commits into
mainfrom
release/feedback
Open

luchobonatti wants to merge 540 commits into
mainfrom
release/feedback

Conversation

@luchobonatti

@luchobonatti luchobonatti commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

Summary

Merges feedback into main: the reviewed work on the contracts, the harness, the app and the docs.

GitHub closes an issue from Closes #N only when a pull request merges into the default branch. The pull requests below merged into feedback, so their issues are listed here, each with the pull request that did the work. All of them were closed by hand except #315, which this merge closes.

Issues

Test plan

CI on this pull request builds and tests feedback's head against main.

@vercel

vercel Bot commented Oct 27, 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 Nov 4, 2026 12:38pm UTC

Request Review

luchobonatti and others added 11 commits October 27, 2026 15:58
feat(app): collect a proof reference when attesting
feat(app): run the share token's admin changes through its timelock
feat(app): run the vault's timelocked changes through the console
…ments

feat(app): follow the renamed vault arguments
With a pinned tag, stellar-scaffold no longer sees the action's container as its local network, starts its own and redeploys, so the app is built against this branch's contracts and fails on the type errors the app stack fixes. Pin the image again once the app compiles against them.
feat(app): name the NAV pause and follow the oracle's bounds
The previous commit blamed the pin for the app build failure. That was wrong: the same tree without the pin fails the same way. The app does not compile against this branch's contracts yet, which the app stack fixes; the pin is unrelated.
…contracts

ci: build the app against the contracts the branch deploys
chore: run CI on protocol 30 and read its ledgers
luchobonatti and others added 5 commits October 27, 2026 18:52
Withdrawal cancels and claims explain 6068 and 6073 instead of the
token's old 304, and the token pause copy says it stops holder
transfers only.

Closes #340
After the oracle's setup closes, Set the oracle limits, attester and
guardian grants, the admin hand-over and the oracle upgrade go through
propose_change. Governance lists the oracle's pending changes with
Apply and Cancel.

Closes #337
Claims and cancels now look up the holder's request right after the
epoch, before they check whether the epoch is priced. A holder with no
request in the epoch gets RequestNotFound, not an error about the
epoch's price.

Closes #363
The README said the script restores and extends all of a deployment.
It covers each contract instance, its Wasm and each pending timelock
change. Any other entry is restored by the next call that needs it,
and that call's sender pays the restore fee.

Closes #362
Subscribe, redeem, cancel and claim explain NotAllowlisted and Frozen,
a frozen holder's requests show as held, Cancel follows the contract's
cancel window, and a converted or surrendered request says where it
went.

Refs #304
hpmaxi and others added 26 commits November 2, 2026 15:41
…t helpers

This reverts commit df89ed1. The TransactionModal split moves to its own
issue.

Refs #420
This reverts commit 908a78b. The shared typed fake moves to its own issue.

Refs #420
This reverts commit d1d371b, and removes its render helpers at the path they
were later moved to. The split moves to its own issue.

Refs #420
This reverts commit 80acc8e. The split moves to its own issue.

Refs #420
This reverts commit c9c75ec. The shared typed fake moves to its own issue.

Refs #420
Request, cancel, price, a claim the reserve cannot cover, fund and claim, on a seeded deployment. The treasury deploys the free reserve first and the redemption is sized above what is left, because a claim is refused only when the liquid reserve is below that claim. send takes a label and prints the transaction hash, so a run can be linked to an explorer.
List one testnet transaction per queue step, from the harness scenario, and the command that repeats it. Closes #368
`npm run test` imports only each client's `src/`, which the app's own client
generator also writes, so its guard checks only that. The guard before
`npm run typecheck` also requires each contract client's `dist/`, which the
harness imports. Both errors name `npm run build:clients`.

Refs #420
Add the console URL and one testnet transaction per privileged operation, with the signing account, signers and threshold. Say which operations were left out and why. Closes #370
chore(harness): set the minimum epoch duration at deploy
test(contracts): name the refusals added since the last pass
fix(contracts): small consistency fixes in the vault and oracle
chore(ci): generate the contract clients before the JS checks
Contract errors are resolved at runtime from the error cases the
generated clients carry, so failures expose the error's name next to
its code. Tests derive codes from each client's error table by name.

Refs #415
Per-action copy moves to transactionCopy/ as data read by one
describeStatus, and contract errors match by name. The modal tests are
split by section. Fixes AdminTransferPending for the vault and oracle,
and pause errors no longer show on oracle actions.

Closes #415
refactor(app): split the transaction modal's copy and map errors by name
…tions

docs: record the admin panel transactions
docs: record the redemption queue on testnet
fix(app): ledger-time expiry, sequential epoch reads, strict network config
Comment on lines +632 to +634
expires_at: input
.expires_at
.min(now.saturating_add(cfg.freshness_duration)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, there’s one interaction between the reporter’s expiry and the cooldown that I wanted to bring up.

For example, with a one-hour freshness window and a 60-second cooldown.

  • At time 1000, a report is accepted with an expiry of 1010.
  • At 1011, the report is stale.
  • A fresh report submitted then would be refused because the cooldown is still active.
  • The next report can only be accepted at 1060.

This creates a period where the feed is stale and the cooldown prevents its replacement, even if the attester already has an updated report ready. Deposits and epoch pricing would wait during that gap.

One option could be to reject a report if it would expire before the cooldown ends. That would prevent this combination, although I can see how it might be too restrictive for some use cases.

Keeping the current flexibility could also make sense. In that case, it would help to document this interaction explicitly, so operators understand that the configured freshness window alone doesn’t prevent the gap. They also need to account for the reporter’s expiry and how much validity remains when the report is accepted, including any submission delays.

Comment on lines +104 to +106
if s.reconciled || s.deposits_open > 0 || s.redeems_open > 0 {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, there’s one interaction between rounding reconciliation and zero-share refunds that I wanted to bring up.

From following these paths, it looks like a refund can wait for reconciliation while reconciliation waits for that same refund.

For example, all amounts below are in the smallest token units.

  • The vault holds 11 asset units, including Carol’s deposit of 1.
  • Alice and Bob each redeem 5 share units in the same epoch, priced at 1.1 asset units per share unit.
  • Pricing commits floor(10 × 1.1) = 11 asset units, but Alice and Bob each receive floor(5 × 1.1) = 5.
  • Carol’s deposit rounds to zero shares, so she needs her 1 asset unit refunded.

After both redemptions settle, the vault still holds that unit, but it remains marked as committed rounding dust. Carol’s refund fails because there is no free reserve. Reconciliation could release it, but the open deposit request prevents reconciliation from running.

Perhaps one way to address this would be to release the cash rounding remainder once all redemptions for that epoch have settled, independently of any outstanding deposit claims or refunds. At that point, the actual redemption payouts are known.

That would make the remaining unit available for Carol’s refund. The cash reconciliation would need to be tracked separately so completing the epoch doesn’t release the same remainder again.

@rcostadresch rcostadresch left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this round of fixes! The shared timelock, the per-epoch settlement, the wiring checks in finish_setup with health() and the oracle expiry and proof changes cover the findings really well, and it's good to see the Wasm upgrade tests running in CI too.

We also saw that some of the feedback was kept as a design decision and written up in the threat model and the contract docs, which makes the trade-offs easy to follow. My two notes are suggestions and nothing blocking. Before merging, please take a look at @fazzatti's comments on the oracle cooldown and the settlement reconciliation.

Comment on lines +48 to +58
/// The vault's clock with its wind-down ignored, so a `compliance` grant still
/// lands after the normal delay: without a holder no freeze can be lifted.
struct ComplianceGrantHost;

impl TimelockHost for ComplianceGrantHost {
fn clock(e: &Env) -> Result<Clock, Refusal> {
let mut clock = <ShareToken as TimelockHost>::clock(e)?;
clock.winding_down = false;
Ok(clock)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bug since the threat model’s X6 already covers it, more of a thought for #238. Once a wind-down is active it can’t be cancelled, so a module that fails on burn would block every claim for good. Letting RemoveModule through a wind-down with the normal delay, the same way this compliance grant goes through, could keep that case recoverable.

Comment on lines +32 to +34
/// A report outlives the notice, so an epoch's cancellation window does
/// not reopen on its own while the notice runs.
pub freshness_covers_notice: bool,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit, not blocking. The deploy guide covers min_epoch_duration, but a vault set up by hand could still close setup without it, leaving the manager as the only one who can seal. A flag here would make that easy to spot.

This branch was successfully deployed

1 active deployment
Preview — 56b22d13 Deployed Nov 4, 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

6 participants