Skip to content

feat(contracts): IStrategy interface (#20)#143

Merged
ozpool merged 2 commits into
mainfrom
contracts/20-istrategy
May 4, 2026
Merged

feat(contracts): IStrategy interface (#20)#143
ozpool merged 2 commits into
mainfrom
contracts/20-istrategy

Conversation

@ozpool
Copy link
Copy Markdown
Owner

@ozpool ozpool commented Apr 29, 2026

Summary

packages/contracts/src/interfaces/IStrategy.sol — pluggable rebalance-shape API. Resolves #20.

  • Surface matches PRD §Day 3 (TargetPosition struct + computePositions + shouldRebalance).
  • NatSpec encodes the ADR-005 purity contract — deterministic, stateless, vault-agnostic; block.timestamp allowed only inside shouldRebalance; runtime SSTORE forbidden.
  • Vault is the trust boundary: it re-checks weight-sum and MAX_POSITIONS after every call.

Quality gates

  • forge build clean
  • forge test 4/4 pass on this file (selector snapshots + mock round-trip + weight-sum fuzz)
  • forge fmt --check clean

Test plan

  • Single-line NatSpec on the purity contract is the right scope (vs. exhaustive enumeration of forbidden patterns — those live in ADR-005)
  • block.timestamp carve-out wording in shouldRebalance is unambiguous
  • No need to express MAX_POSITIONS as an interface constant (vault enforces independently)

Closes #20. Blocks #32, #33, #39.

packages/contracts/src/interfaces/IStrategy.sol — pluggable rebalance
shape API. Strategies decide tick-range positions and rebalance
timing; vault remains the trust boundary.

Surface matches PRD §Day 3: TargetPosition struct + computePositions +
shouldRebalance. NatSpec captures the ADR-005 purity contract:
deterministic, stateless, vault-agnostic; block.timestamp permitted
only inside shouldRebalance for the 24h liveness fallback; runtime
SSTORE forbidden (CI bytecode scan).

The vault re-checks Σ weight == 10_000 (#2) and length <= MAX_POSITIONS
(#3) after every call, so the interface itself doesn't need to police
malformed strategies — the vault reverts via Errors.WeightsDoNotSum or
Errors.MaxPositionsExceeded.

Tests: 2 selectors snapshotted, mock returns a full-range single
position to exercise the round-trip, fuzz over inputs verifies the
weight-sum invariant on the mock. 4/4 pass.

forge build clean, forge fmt --check clean.

Closes #20. Blocks #32, #33, #39.
* feat(contracts): centralised custom error library (#17)

Adds packages/contracts/src/utils/Errors.sol — every PRISM contract
reverts through a 4-byte selector (~200 gas) instead of a string
(2,000+ gas). Errors are grouped by domain (access control, vault,
strategy, hook, oracle, callback, generic) with NatSpec describing
the call site of each.

Coverage matches PRD §Day 1 plus the three errors introduced by
ADR-004 (UnknownOp, DeltaUnsettled, Reentrancy) and the bound/value
helpers (MathOverflow, ValueOutOfBounds) used by FeeLib (#23) and
PositionLib (#22).

Tests in test/utils/Errors.t.sol snapshot every selector against its
canonical signature — 21 selectors plus 4 round-trip reverts. Selector
stability is a downstream-consumer contract; bumping a signature is a
breaking change and the test will catch it.

forge build clean, forge test 27/27 pass, forge fmt --check clean.

Closes #17. Blocks #26, #27, #28, #29, #32, #34, #35, #36, #37.

* feat(contracts): BellStrategy.computePositions — bell-curve weights (#32)

Default PRISM strategy. Returns 7 positions arranged as a symmetric
bell around the current tick, with hardcoded weights
[500, 1200, 2100, 2400, 2100, 1200, 500] bps (Σ = 10_000).

Per ADR-005 the strategy is pure / stateless / vault-agnostic:
- No SSTORE in runtime bytecode (constants only — W0..W6 are inline
  compile-time literals; CI bytecode scan in #61 verifies this)
- Deterministic across repeat calls with identical inputs
- amounts0/amounts1 are opaque to the strategy (vault-agnosticism)

Tick alignment: currentTick is floored to the nearest tickSpacing
boundary (negative-tick aware). The 7 positions tile contiguously
across [anchor - 3*ts, anchor + 4*ts] with each position spanning
exactly one tickSpacing.

shouldRebalance returns false on this PR — #33 implements the actual
gate (drift + time + 24h fallback).

16 tests:
- 7 unit tests for shape, symmetry, sum, alignment, contiguity,
  width, anchor centering
- 2 revert tests for zero / negative spacing
- 3 fuzz tests for determinism, weight invariant, vault-agnosticism
- 4 supporting tests for constants and ctor

Stacked on contracts/20-istrategy with cherry-picked Errors (#17).

Closes #32
@ozpool ozpool merged commit c8ffbd7 into main May 4, 2026
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.

[contracts] IStrategy interface: TargetPosition struct + computePositions + shouldRebalance

1 participant