test(registry): verify registry.deploy delivers full_access_keys (ENG-404)#490
test(registry): verify registry.deploy delivers full_access_keys (ENG-404)#490peer2f00l wants to merge 42 commits into
Conversation
Replace integration tests that booted a NEAR sandbox and deployed ~6 contracts merely to exercise pure logic with direct `#[test]` coverage in `templar-common`, the first workstream of the test-suite speedup. - Delete `contract/market/tests/configuration_validation.rs` (11 sandbox tests) and re-add the cases as pure tests calling `MarketConfiguration::validate()` directly. Also drops the fragile `Smart contract panicked: ...` string assertions. - Add `DepositMsg` wire-format round-trip tests covering the exact `msg` shapes passed to `*_transfer_call` (the regression `message_regression` guarded on-chain). - Add `Fee`/`TimeBasedFee::of` math tests (Flat/Proportional, Fixed/Linear, expiry boundary, rounding) — previously uncovered. - Add `borrow_status` MCR/duration tests. These run in milliseconds with no node; the stateful accrual tests (interest_rate, accumulations, static_yield, snapshot) remain node-backed and will be ported in a later workstream. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…(ENG-388) Second workstream of the test-suite speedup. Builds the additive layer of the new gateway-client-based test harness so market tests can move off the near-workspaces controllers, and fills a gap in the gateway op catalog. - Add the `market.collateralize` write operation (there was a `Supply` but no collateralize): `market::Collateralize` spec + `PlanWrite` dispatch mirroring `Supply` with the collateral asset and `DepositMsg::Collateralize`, registered in `for_each_write_method!`. - Unify the `SandboxHarness` signer registry into one growable `Mutex<HashMap<_, ManagedSigner>>` (operators seeded at start + on-demand test accounts); expose it via a `gateway_signers()` accessor. - Add `gateway/testing/src/ops.rs`: terse domain wrappers over the in-process `templar-gateway-client::Client` (create_user, fund_user, supply, collateralize, borrow, repay, harvest_yield, supply_and_harvest_until_activation, position/snapshot reads). Writes go through `Client::execute_as`, reads through `Client::read` — the same dispatch the RPC service uses, no sign/submit boilerplate. - Add `deploy_market_with(customize)` to tweak the `MarketConfiguration` before deploy (deploy_market keeps its signature). - Spike: port `fast_borrow.rs` onto the harness as `gateway/testing/tests/`, `#[ignore]`-gated. Passes against a real sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…xture (ENG-388)
Externalize neard so the test harness can either launch its own node (owned
mode) or attach to an out-of-band one over RPC (attach mode, when
NEAR_SANDBOX_RPC_URL is set) — the basis for sharing one node across many
tests without booting one per test.
- Unify account creation onto a single path: every account is a uniquely-named
`{label}-{seq}-{pid}.sandbox` sub-account created via near-api against the
genesis `sandbox` root (replacing the patch_state `*.near` path, which only
works for an owned node). The per-process+seq id keeps parallel harnesses
that share an attached node from colliding.
- `SandboxHarness.sandbox` is now `Option<Sandbox>` (an RAII handle held only
in owned mode; attach mode must not own it, since dropping kills neard).
- Caller-supplied contract ids (the gateway service tests) are transparently
namespaced via `label_of`, so those tests compile unchanged.
- Add an `rstest` `harness` fixture so tests keep the
`#[rstest] ... #[future(awt)] harness: SandboxHarness` shape; the spike uses it.
Owned-mode spike passes against a real sandbox.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…G-388) Make the node-backed suite share a single externally-managed `neard` instead of booting one per test, via a nextest setup script. - Add `sandbox-host` bin (gateway/testing): boots one sandbox neard, reports its RPC url, stays alive until SIGTERM/SIGINT (then drops the Sandbox, killing neard). - Add `script/sandbox-up.sh` (prebuild wasms + launch the host detached + export NEAR_SANDBOX_RPC_URL/TEST_CONTRACTS_PREBUILT via $NEXTEST_ENV), `script/sandbox-down.sh`, and `script/test-sandbox.sh` (runs the profile and guarantees teardown on exit). - Add a `sandbox` nextest profile (serial for now — every account-create signs as the genesis root, so parallel processes would contend on its nonce) wired to the setup script, and enable the experimental setup-scripts feature. Validated end-to-end: SETUP starts the node, the #[ignore]-gated spike attaches over RPC and passes, teardown stops the node. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Standardize the root task runner on `just` (already used in several subdir justfiles), and fold the test-sandbox wrapper script into a recipe. - Port the root Makefile recipes to `justfile` (fmt, sql-fmt, test, coverage, docs) and add `test-sandbox`/`sandbox-up`/`sandbox-down`. - `just test-sandbox` absorbs script/test-sandbox.sh: it runs the sandbox nextest profile with guaranteed teardown, and passes args through cleanly (which Make could not). - Repoint references: `.config/nextest.toml` and the relayer README. Service-level Makefiles (funding-bridge, liquidator, market-monitor) are left as-is — out of scope here. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…-388) First WS3 batch: port FT-only market integration tests off the near-workspaces controllers onto the gateway harness, using rstest + the `harness` fixture. - Port `repay.rs` (5 amounts x 3 payer modes) and `empty_positions.rs`. - Add the ops wrappers they need: withdraw_collateral, create/execute_next_supply_withdrawal_request, ft_balance_of, set_asset_prices, storage_deposit_min. - Use set_asset_prices in the fast_borrow spike too. Note: empty_positions re-supplies after a full withdrawal, which refunds only the position's storage (not its snapshots'), so it tops up storage via storage_deposit_min first — mirroring the old test's manual storage_deposit. Validated against the shared sandbox: empty_positions and a representative repay subset (partial / boundary / overpay x implicit / self / other) pass. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
When a write operation's step fails on-chain, the gateway recorded only a `Failed` status and tx hash — not *why* it failed. Capture the failure reason so consumers (and tests) can see the contract panic message. - Add an optional `failure: Option<String>` to `StepStatus::Failed` (`#[serde(default, skip_serializing_if)]`, so the wire format stays backwards-compatible) and an `OperationRecord::failure_message()` convenience. - The `OperationDriver` extracts the message from the failed execution outcome (`ExecutionFinalResult::into_result`) and threads it through `fail(...)`; the reconcile path captures its error too. - The Postgres store keeps its schema: the reason is best-effort in-process (present in the returned operation record), and a step resumed from the store reports `failure: None`. Purely additive; existing callers are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… (ENG-388) Continue WS3: port more FT-only market tests onto the gateway harness. - Port untracked_funds.rs (cannot borrow / cannot withdraw untracked funds) and the excessive-collateral-withdrawal case of collateral.rs (the concurrent race test is deferred). - Add ops wrappers: ft_transfer, try_borrow / try_withdraw_collateral (return the result without asserting success, for expected-rejection tests), and supply_withdrawal_queue_status / supply_withdrawal_request_status reads. - Strengthen the write path: `execute` now asserts the operation succeeded (a contract panic surfaces as Ok(Failed), not Err, so this turns an unexpected on-chain failure into a test failure); `try_execute` is the non-asserting variant. - The former `#[should_panic = "..."]` cases now assert both the observable effect and the failure reason via OperationRecord::failure_message(). All validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… (ENG-388) Continue WS3 with two more parametrized FT-only files. - Port maximum_borrow_asset_usage_ratio.rs (borrow within / beyond the usage ratio) and supply_maximum_amount.rs (supply within / beyond the max, plus the harvest-after-repay regression). - Add a `try_supply` wrapper. - supply-beyond-maximum asserts the *effect* (the over-max deposit is refunded, not recorded) rather than a failed status: a panic inside `ft_on_transfer` is caught by the FT, which refunds, so the `ft_transfer_call` operation itself succeeds. (Direct calls like borrow/withdraw still fail outright — those keep asserting status + failure message.) All validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Port contract/market/tests/borrow_limits.rs: borrow within bounds, below minimum, above maximum, and the repay-to-minimum cap. The original `borrow_above_maximum` borrowed concurrently via a JoinSet; here it borrows sequentially and asserts at least one is rejected — borrows are direct market calls that fail outright (so the rejection surfaces as a failed operation with the "outside of allowable range" message), unlike ft_transfer_call deposits which refund. Validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Port the full deposit-message / market-op smoke: supply, collateralize, borrow, repay, repay-for-another-account, withdraw collateral, apply interest, and liquidate after a price drop. Each step asserts success. Adds `apply_interest` and `liquidate` harness wrappers (the latter also unblocks the liquidation batch). Validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…NG-388) Recreate the same failure conditions deterministically (rather than replicating the exact mechanics), per review: - collateral.rs: a collateral withdrawal is blocked once the account is unregistered from the collateral token (and the collateral is not lost) — replaces the original 30-way JoinSet race with a deterministic unregister. - storage_management.rs: an unregistered account's raw ft_transfer_call(Supply) is rejected and refunded, leaving no position — bypasses the gateway supply op's auto-registration to exercise the contract's own requirement. - supply_activation.rs: a fresh deposit activates in the next snapshot; uses long snapshots + fast_forward instead of brittle 1ms-snapshot wall-clock timing. - maximum_borrow_duration_ms.rs: a borrow past its max duration reports Liquidation(Expiration); advances time via fast_forward (logic also covered by a pure unit test). Harness additions: fast_forward (sandbox_fast_forward over RPC), raw ft_transfer_call (non-asserting), storage_unregister, get_oracle_prices, and get_borrow_status. All validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Raise the max borrow duration to 10s (from 1s) so the pre-expiration "healthy" check can't flake on slow reads and `fast_forward` — not wall-clock block production — is what drives expiration. Mutation-tested all four finicky ports to confirm they actually fail when their critical condition breaks: removing the storage unregister (collateral), supplying via the registering gateway op (storage_management), and removing fast_forward (supply_activation, maximum_borrow_duration) each make the corresponding test fail. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
While a position is liquidatable, only curing actions are allowed: an insufficient collateral top-up and a repayment are rejected (no effect), while a sufficient top-up that cures it is allowed (collateral increases). Adds try_collateralize / try_repay wrappers (the rejected paths go through ft_transfer_call, which refunds rather than failing the op). Validated against the shared sandbox; the allow/deny contrast makes the no-effect assertions meaningful. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Port the maintenance-MCR tests: borrowing succeeds above the maintenance ratio, fails below it (direct call → failed op with the expected message), a position below maintenance but above liquidation is not liquidatable, and withdrawing collateral that would drop below maintenance is rejected. Validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ers (ENG-388) Port the comprehensive happy-path lifecycle (NEP-141 variant): supply + activate, collateralize, borrow (with origination fee), repay, then the 8/1/1 yield split across supply (80) / protocol (10) / insurance (10), supply principal + collateral withdrawal, and position closure. Adds harness wrappers for ops that already existed in the gateway: accumulate_static_yield, withdraw_static_yield, static_yield_total, list_finalized_snapshots. The MT (NEP-245) variants of the original are deferred (need MT-contract deploy support in the harness; the gateway ops already handle MT assets generically). Validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Port the time-based supply-withdrawal-fee tests: the flat fee is charged when withdrawing within the window (and credited to the protocol account's static yield), and is not charged after the window (advanced via fast_forward instead of wall-clock sleep). The protocol account is configured as both the fee recipient and a yield_weights static member (so it "earns static yield"), and is registered on the market for its yield record. Also surface the on-chain failure reason in the harness `execute` error message (via OperationRecord::failure_message), which is what made diagnosing the above straightforward. Validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The new market.collateralize write method was registered in the spec macros but the committed catalog was not regenerated, so the methods_md_is_up_to_date test in templar-gateway-catalog would fail. Regenerated via the catalog's regenerate_methods_md helper. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- sandbox-down.sh: verify the PID is still a sandbox-host process (ps args match) before killing, to avoid terminating an unrelated process after PID reuse. - sandbox-up.sh: on startup failure (no RPC url reported), kill the half-started host and remove the stale PID/URL files so subsequent runs don't inherit orphaned metadata. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Address CodeRabbit review comments left in the harness/test code: - fast_forward: give the RPC client a request timeout, and decode a JSON-RPC error body (HTTP 200 + `error`) before indexing `result`. - supply_and_harvest_until_activation: bound the harvest loop (fail after 1000 iterations) instead of looping unbounded. - storage_management: assert the specific failure (Failed op + "is not registered" message), not just the post-state. Validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…rt accumulations/static_yield/supply_withdrawal_queue (ENG-388)
Harness account-creation rework (fixes shared-node flakiness):
- The default genesis `sandbox` account holds only 10_000 NEAR. On the shared
attach-mode node, each test locks funds in accounts that outlive it, so a run
drains genesis and later tests fail to create accounts (surfacing as
`InvalidTransaction({})` at harness start). Seed a high-balance `funder`
genesis account (shared launch config for owned mode and the out-of-band host)
and fund everything from it, so the node never runs dry.
- Each test process now creates one intermediate "tenant root" account from the
funder, then creates all its working accounts as sub-accounts of that, signed
by the tenant root's own key. The heavily-shared funder key is thus touched
once per process instead of ~9 times, removing the bulk of cross-process nonce
contention and laying the groundwork for parallel test-threads. Only that one
funder-signed creation keeps a bounded retry (a nonce-rejected tx is refused at
submission, so it never piles up as pending).
Ports (all validated against the shared sandbox):
- accumulations: third party can drive interest accrual + yield harvest.
- static_yield: accrue/accumulate/withdraw static yield; withdrawal blocked when
the recipient unregistered from the token (record preserved).
- supply_withdrawal_queue: full/partial fulfilment, batch, range/over-deposit
rejections, failed-transfer-still-dequeues, deposit-during-withdrawal.
measure_gas is omitted (needs per-call gas, not exposed by the harness).
New harness wrappers: harvest_yield_with_mode, static_yield_record,
try_withdraw_static_yield, try_create_supply_withdrawal_request,
list_supply_positions, list_borrow_positions. fast_forward's RPC timeout raised
to 120s (it only bounds an infinite hang).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Port the core liquidation flows: fully-underwater and exact-to-zero liquidations, the maximum-spread cases, both rejection cases (too little attached / healthy borrow — refunded via ft_transfer_call, asserted as no effect), interest-only liquidation (fast_forward instead of a wall-clock sleep), and the liquidators race (only one of two identical attempts succeeds). Adds harness helpers mirroring the retired controller: liquidatable_collateral_fmv, liquidatable_collateral_with_spread, and try_liquidate. The yield-distribution (good_debt_under_mcr), partial-liquidation, and extreme-price (raw pyth exponents) cases are deferred — they need the 8/1/1 yield config, multi-liquidator partial mechanics, and a raw price setter respectively. Validated against the shared sandbox (11/11). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Add the remaining liquidation cases, completing the file: - extreme_prices: raw pyth prices with explicit (incl. negative) exponents, via new set_borrow/collateral_asset_price_exact harness helpers; exercises the offer-too-low-then-enough path. - partial_liquidation: two liquidators race for an eligible slice; the first succeeds, the second is refunded once the position is healthy again. - partial_liquidation_fail_offer_too_little: an underpriced offer is refunded. - many_little_partial_liquidations: repeated fractional liquidations sum to the whole, across three split patterns. - successful_liquidation_good_debt_under_mcr: the 8/1/1 yield distribution (supply / protocol / insurance) of the liquidation proceeds. Validated against the shared sandbox (all cases pass). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…NG-388) A node-free near-sdk unit test that builds a market with deterministic finalized snapshots (chosen end timestamps + interest rates) and a borrow position with a chosen principal, then asserts calculate_interest returns the exact accrued interest. The expected value is both independently recomputed (Decimal, same op order) and pinned as a literal, so the assertion isn't a tautological echo of the implementation. This is the deterministic-times/exact-result companion to the fast_forward-based interest_rate integration test. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…on (ENG-388) Add operation_gas_burnt to the harness — it sums total_gas_burnt (transaction + receipts) across an operation's step transactions via the JSON-RPC tx-status method, since the gateway result carries only tx hashes. With it: - supply_withdrawal_queue::measure_gas — base/per-request gas stay under budget. - many_snapshots — per-borrow gas does not grow as snapshots accumulate (256 borrows; ~250s, gated #[ignore]). interest_rate integration test (the node-requiring half; the exact arithmetic is the pure templar-common test): interest accrues as fast-forwarded time passes, and applying it more frequently cannot reduce the total owed. All validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…counts (ENG-388) - snapshot (8 tests): snapshot data-capture mechanics, reusing the test-utils partial::check / states! DSL. The original's wall-clock sleeps become fast_forward; generous advances are safe because check skips snapshots matching the previous state. many_users_same_snapshot runs each collateralize/borrow group concurrently so all land in one time chunk. - supply_within_snapshot::funds_activate_in_the_next_snapshot: deposits defer to exactly the next snapshot (asserted via the deterministic activate_at index, not the fragile finalization count). The original partial_snapshot_no_earnings asserts exact per-snapshot yield from timestamps — that exact arithmetic is the pure templar-common interest test's job, so it is not reproduced as a flaky timestamp-bounded integration test. - many_accounts: state-consistency across many accounts (reduced from the original's 100-account JoinSet/concurrent-queue stress to a deterministic set of suppliers + borrowers), asserting the position listings reflect exactly them. All validated against the shared sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…G-388) Port contract/market/tests/with_proxy_oracle.rs to the gateway harness as gateway/testing/tests/with_proxy_oracle.rs: a market whose oracle is a proxy oracle aggregating a pyth mock and a redstone mock via Proxy::median_low, one proxy per asset. Parametrized over source ordering, it iterates all 16 availability combinations and asserts a collateralize succeeds iff both proxied prices are available (no effect otherwise). Harness additions (minimal, localized): - sandbox: deploy_market_with_oracle (deploy_market_with now delegates to it after deploying a fresh mock) points a market at an existing oracle account. - sandbox: update_proxy_prices wraps the proxy's update_prices cross-contract fan-out (admin_set_proxy already existed). - ops: deploy_full_market_with_oracle + a shared resolve_deployed_market helper. Verified in owned mode: all 4 cases pass (~92s each). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…388, WS4) Run the node-backed `sandbox` profile in parallel instead of serial. The setup script now boots a pool of neard nodes (SANDBOX_NODE_COUNT, default 4) and exports NEAR_SANDBOX_RPC_URL_<i> per node; each test attaches to the node for its NEXTEST_TEST_GLOBAL_SLOT. Because nextest runs at most one test per slot at a time, each test has *exclusive* use of its node — so `fast_forward` and chain state stay isolated between concurrent tests (a single shared node could not guarantee this, since `fast_forward` advances one global clock) — while reusing a node across the tests that pass through its slot avoids per-test boot. - sandbox-up.sh: boot N nodes (node 0 first to warm the neard binary, then the rest concurrently); export per-slot urls. sandbox-down.sh tears down all. - sandbox.rs: `attach_rpc_url` picks the slot's node, errs loudly if the pool is smaller than test-threads, falls back to the single url for manual runs. - nextest.toml: sandbox profile test-threads 1 -> 4 (must stay <= node count). - test.sh: drop the post-run `*.rmeta` sweep — it ran too late to relieve in-run disk pressure and stripped dependency metadata from the rust-cache snapshot, forcing deps to recompile next run. Validated: 10 fast_forward-heavy, time-sensitive tests pass 4-wide in parallel (~92s vs ~300s serial), with no cross-test time interference. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Debug symbols are the bulk of both target/ and the rust-cache snapshot. line-tables-only keeps panic backtraces (file:line) while dropping the rest, shrinking peak disk and cache up/download with no recompilation penalty — the right disk lever, vs the after-the-fact .rmeta deletion that relieved no in-run disk pressure and degraded the cache. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… pooled neard (ENG-388, WS4) All 27 market integration files are ported (the parse_configurations.rs pure config-parse check stays), so remove the old near-workspaces market tests that booted a neard per test — the dominant cost of the 40-minute integration job. CI: the near-integration job's `test.sh --workspace` no longer runs them; add a step running the ported tests on the `sandbox` profile's pooled neard nodes (SANDBOX_NODE_COUNT=2, --test-threads 2), and tear the pool down on cleanup. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…-388) Move the ported market tests from gateway/testing/tests to contract/market/tests so they live with the market contract (where market tests belong), leaving templar-gateway-testing as a pure, reusable harness library. No dependency cycle: the gateway depends on templar-common types + method-name strings, not on the market contract crate, so contract/market can dev-depend on templar-gateway-testing. - contract/market dev-deps: + anyhow, near-token, serde_json, templar-gateway-testing, templar-gateway-types. - CI: the pooled sandbox gate now targets -p templar-market-contract. Validated: market crate tests compile and run on the pooled sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Port contract/registry's near-workspaces deployment tests onto the SandboxHarness (the non-market pattern-setter): deploy markets from a registry version via the gateway registry.deploy op and verify the deployed configuration, the empty access-key set, and the id-collision panic. Drops registry's near-workspaces dev-dep. Reusable non-market harness surface added: deploy_ft, view_access_keys, get_configuration, registry add_version/deploy. Known gap (TODO in deploy_with_access_key): the gateway registry.deploy op yields a market with zero full-access keys when keys are requested — the contract adds them, so the typed full_access_keys appears dropped in the gateway path (likely a near_api<->near_sdk PublicKey serialization round-trip). The original near-workspaces test never exercised this (it called the contract directly). Filed for a focused fix. Validated on the pooled sandbox (3/3). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…oxHarness (ENG-388) Re-author the universal-account near-workspaces integration tests (migration.rs, universal_account.rs) onto the gateway `templar-gateway-testing::SandboxHarness`, in place. Tests now provision a real contract on a near-sandbox node via an `rstest` `harness` fixture and drive it with raw `near_api` calls (the same tx::FunctionCall-style path the harness uses internally), since the gateway `Client` universal-account read/write surface is still being built out on this branch. No edits to gateway/testing were required. - Shared helpers live in tests/common/mod.rs (deploy, patch_state, views, execute relay, migrate, FT registration). - `#[should_panic = "...: <msg>"]` assertions converted to non-asserting calls that capture the on-chain failure and assert the inner substring via CallOutcome::assert_failure_contains. - All tests keep granular names and are `#[ignore = "requires NEAR sandbox"]`. - dev-deps: add anyhow, base64, near-api, near-token, serde, serde_json, templar-gateway-testing; drop near-workspaces (no longer used by the crate). Validated on the pooled sandbox: migration: 24/24 passed universal_account: 63/63 passed Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ess (ENG-388, WS5) The migration was authored against a pre-WS2 harness (owned-mode `harness.sandbox` account creation + patch_state). Adapt it to the current attach-mode harness: - Add `SandboxHarness::patch_state` (mode-agnostic, via the `sandbox_patch_state` RPC; works without an owned `Sandbox`), used by the UA migration tests. - `create_account` now provisions a harness account via `create_user` (same shared test key → `test_signer` still signs for it) and returns its id; drop the owned-mode `harness.sandbox.create_account` path. 87/87 pass on the pooled sandbox. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…xHarness (ENG-388)
Re-author the proxy-oracle + lst-oracle near-workspaces integration tests onto
the gateway `SandboxHarness`, driving the contracts via typed `near_api` view
calls (reads) and signed `near_api` transactions (writes), matching the
harness's own pattern. Pure-unit (`testing_env!`) tests are left in place.
- contract/.../contract/tests:
- proxy_oracle.rs: sandbox `proxy_oracle` (median_low/priority) and
`proxy_oracle_enforces_freshness_filter` ported to the harness
(deploy_mock_oracle/deploy_proxy_oracle/admin_set_proxy/set_mock_oracle_*);
update_prices driven as a raw signed call, prices read back via the
pyth view. Unit tests unchanged.
- migrate_v0.rs / migrate_mainnet.rs: deploy legacy v0 wasm, patch raw state
via `harness.sandbox.patch_state(...)`, redeploy current wasm, migrate.
`migrate_is_private` converts `#[should_panic]` to a non-asserting call +
failure-message substring check. mainnet fixture generator re-implemented
over near_api.
- tests/common/mod.rs: shared signer / view / call / failure-assert / price
helpers + `deploy_from_patch` (test-local, no harness edits).
- lst-contract/tests/lst_oracle.rs: LST-oracle behavior ported end-to-end
(oracle id, transformer list/get, price-feed existence, transformed price
resolution against a 2:1 redemption rate). Market borrow-health assertion
deferred with a TODO(ENG-388 follow-up) pending market-domain harness flows.
- dev-deps: add anyhow/near-api/near-sandbox/near-token/serde_json/base64/
templar-gateway-testing; drop near-workspaces from both crates.
- .config/nextest.toml: add a `sandbox` profile.
Validated: SANDBOX_NODE_COUNT=2 TEST_CONTRACTS_PREBUILT=1 cargo nextest run
--profile sandbox --run-ignored all, both crates -> 21 passed, 0 skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…ENG-388, WS5) Like the universal-account migration, the proxy-oracle tests were authored against a pre-WS2 harness. Adapt to the current attach-mode harness: - `create_account` provisions via `create_user` (shared test key → global `signer()` still signs) and returns the generated id. - `deploy_from_patch` uses the new `SandboxHarness::patch_state` instead of the owned-mode `harness.sandbox.patch_state`. - Drop now-unused `near-sandbox` dev-dep + `Sandbox`/`AccountId`/`NearToken` imports. 21/21 pass on the pooled sandbox (proxy-oracle contract + lst-oracle). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…eration model (ENG-388) Merge origin/dev to pick up the vault gateway methods-spec/Client ops (ENG-392, unblocks the vault test migration) and the relayer operation-model rework (ENG-370). Reconciles two divergent evolutions of the gateway: - Operation model: adopt dev's ExecutionOutcome model (Succeeded/Reverted/ Rejected with tokens_burnt + per-receipt logs) wholesale, and re-home this branch's panic-message capture as ExecutionOutcome.failure (populated via into_result() on the failure path; per-receipt status stays private, ENG-407). Restores OperationRecord::failure_message() so the ported market tests that assert on contract panic substrings keep working. Fix an auto-merge artifact that leaked `failure` onto CurrentStep::Rejected; add the field to the 5 ExecutionOutcome construction sites. - Harness: rewrite dev's auto-merged deploy_vault into this branch's harness idiom (create_account + deploy_contract); ClientBuilder::signer -> with_signer; amounts use templar_primitives::SU128. - universal_account.rs: port dev's 5 new tests (nonce rollback/replay, key-index uniqueness, last-key protection, removed-key execution) onto the SandboxHarness (add_key/remove_key common helpers; new() is positional, no InitArgs). - Regenerate METHODS.md (171 methods). cargo check --workspace --tests, cargo fmt, and the pure unit suite all pass. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… smoke (ENG-388) Add `SandboxHarness::deploy_vault_with_market[_with]`, the harness analogue of the retired `setup_everything`: deploys a market, then a vault whose underlying is the market's borrow asset, with distinct signable owner/curator/sentinel accounts, then caps + enqueues the market — all through the gateway `Client` (vault::SubmitCap/AcceptCap/SetSupplyQueue), never raw tx. Returns `DeployedVault`. Plus the minimal domain wrappers to prove it end-to-end: `vault_supply` (vault::Deposit), `vault_allocate` (vault::Allocate), `vault_init_account`, `vault_market_id_of`, and total-assets/idle/total-supply reads. A `#[ignore]` smoke test (`tests/vault_fixture.rs`) exercises supply -> allocate and asserts the vault accounts the assets (idle drains, total preserved, shares minted) — validating the storage/caps choreography the ported vault tests need. Two storage-registration details this surfaced, both handled via `storage.deposit`: vault participants need more than the reported registration min to hold shares, and the market must be registered on its own assets to receive allocations. `deploy_contract` is now `pub(crate)` so the fixture can live beside the market fixtures in ops.rs; `test_secret_key`/`DeployedVault` re-exported from the crate root. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…-404) The gateway registry.deploy path was suspected of dropping `full_access_keys` (deployed market ending up with 0 keys), with a relaxed assertion + TODO left on the ported harness test. Tracing op → contract shows the path is already correct: near_api::PublicKey and near_sdk::PublicKey serialize to the same "ed25519:<bs58>" string, so the gateway emits byte-identical JSON to the direct near-workspaces path, and the op stays fully typed to PlanWrite::plan. Restore the strict key-count assertion in the ported `deploy_with_access_key` test (passes against both fresh-compiled and prebuilt wasm), and add two serde regression guards in gateway-core: DeployArgs serializes full_access_keys as the curve-prefixed string, and the near_api/near_sdk PublicKey JSON match. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8cf837d518
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| operation.current_step = Some(CurrentStep::Submitted { | ||
| transaction, | ||
| tx_hash, | ||
| failure: Some(error.to_string()), | ||
| submitted_at, | ||
| }); |
There was a problem hiding this comment.
Reject expired unknown submitted transactions
When query_transaction keeps returning TransactionNotFound for a step that was marked Submitted but never actually reached the chain (for example, an RPC error before broadcast after submit() persisted the state), this branch restores Submitted forever. submitted_at is preserved but never checked, so recovery/broom never transitions the operation to Rejected, leaving the gateway operation in progress and any relayer pending charge locked indefinitely.
Useful? React with 👍 / 👎.
| failed_status => { | ||
| // Operation did not succeed (reverted receipt, or did not | ||
| // reach finality) - release reserved inventory. | ||
| self.inventory | ||
| .write() | ||
| .await | ||
| .release(borrow_asset, liquidation_amount); |
There was a problem hiding this comment.
Keep inventory reserved for in-flight liquidations
When the gateway returns OperationStatus::InProgress (a transaction was submitted but no final outcome was captured yet), this catch-all arm releases the reserved borrow inventory immediately. That transaction may still finalize successfully and spend the reserved tokens, while the service can reuse the same inventory for another liquidation, causing the bot to overcommit its actual balance; handle InProgress separately and keep the reservation until final reconciliation or a fresh balance refresh.
Useful? React with 👍 / 👎.
| $$ | ||
| ; | ||
|
|
||
| CREATE TABLE IF NOT EXISTS gateway_operations ( |
There was a problem hiding this comment.
Add a new migration instead of rewriting the old one
This rewrites the already-versioned 20260421120000 migration in place. Any database that has applied the previous migration has that version/checksum recorded by SQLx, so PostgresStore::migrate() will fail validation before the new lifecycle migration can run, blocking upgrades for existing gateway-store deployments (especially the documented public legacy schema path). Restore this migration as it was and move the schema transformation into a later migration.
Useful? React with 👍 / 👎.
Resolves ENG-404. Stacked on #480 (base
feature/eng-388-ws2-harness); GitHub auto-retargets todevonce #480 merges.TL;DR
The gateway
registry.deploypath was suspected of droppingfull_access_keys(deployed market → 0 keys), with a relaxed assertion +TODOleft on the ported harness test. It is not a bug — the path was already correct. This PR restores the strict assertion and adds serde regression guards. No production code changed.Why it is not a bug
Traced op → contract:
registry::Deploy.full_access_keys: Option<Vec<primitive::PublicKey>>→.map(Into::into)→DeployArgs.full_access_keys: Option<Vec<near_api::PublicKey>>(transparent newtype unwrap).near_api_types::PublicKeyserializes viacollect_str→"ed25519:<bs58>"; the contract paramnear_sdk::PublicKeydeserializes from exactly that. The direct near-workspaces path encodesnear_sdk::PublicKey, which emits the same string — so both paths produce byte-identical JSON.PlanWrite::plan; the stored request payload is for idempotency/audit and is not replayed. The harnessregistry_deployforwarded the keys even at the TODO commit.Reproduction
Ran the ported
deploy_with_access_keywith the strictkeys.len() == 1assertion restored — passes against fresh-compiled contracts and the prebuiltv1.2.1wasm. Could not reproduce 0 keys against current code.Changes
contract/registry/tests/deployment.rs— remove the TODO block; restore strict key-count assertions (len==1, key matchesTEST_PUBLIC_KEY, full access).gateway/core/src/client/registry.rs— two serde regression guards: (1)DeployArgsserializesfull_access_keysas["ed25519:<bs58>"]; (2)near_api/near_sdkPublicKeyJSON match.deploy_from_registry(no-key → empty) still passes;cargo fmt+cargo checkclean.🤖 Generated with Claude Code
This change is