ALP State Mutation Pipeline#16
Open
jordanschalm wants to merge 17 commits intomainfrom
Open
Conversation
Structure operations as validate → apply → check invariants. Validators construct resource-typed mutations (access(contract) init); Pool.applyMutations is the sole state-write choke point, gated by a new MutateState entitlement. Vault inputs are modeled as mutations (VaultDeposit); vault outputs remain direct effects bounded by the invariant check. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the validator / StateMutation pipeline with a simpler orchestrator shape: Pool API methods build plain-struct Intents (DepositIntent, WithdrawIntent), validators are `view` yes/no gates over &Pool, and mutators are contract-level functions that take auth(MutateState) &Pool + the intent + any input resources. Mutators apply all state changes, call checkInvariants, and return any output resources directly. Eliminates the StateMutation resource interface and the resource-in-resource VaultDeposit pattern. Resource I/O is explicit at the mutator signature: inputs alongside the intent, outputs as return value. Access convention: Pool methods are either `view` or entitlement-gated. MutateState gates the appliers (applyLedgerDelta, applyVaultDeposit, applyReserveWithdraw) and checkInvariants. The entitlement is minted only inside orchestrator methods and never escapes the operation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move all mutable protocol state (positions, tokenStates, reserves) into a new PoolState resource nested inside Pool. Pool becomes a thin orchestrator owning config, lifecycle, access control, and @PoolState. Pool has no state-writing methods of its own — it builds intents and forwards to PoolState's entry points. Rename the applier-gating entitlement MutateState → StateWrite (it now belongs to PoolState, not Pool). Validators and mutators operate on &PoolState / auth(StateWrite) &PoolState. PoolState exposes two layers: access(all) entry points (deposit, withdraw, registerPosition, registerToken) that run validate → apply → checkInvariants internally, and access(StateWrite) appliers reached only through mutator functions. Also tightens Reserves' mutating methods (deposit/withdraw/addSupportedToken) from access(contract) to access(StateWrite), mirroring PoolState's convention. The design consolidates state-writing code into one greppable place. It does NOT prevent Pool from reaching PoolState appliers via direct self.state.X access — Cadence allows a composite unrestricted access to its own nested resources, so enforcement remains discipline + convention + lint. The top- level doc block calls this caveat out explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tlement Move applyDeposit and applyWithdraw from contract-level functions onto PoolState as access(self) methods. Tighten all state-writing methods on PoolState (applyLedgerDelta, applyVaultDeposit, applyReserveWithdraw, applyDeposit, applyWithdraw, checkInvariants) from access(StateWrite) to access(self). The StateWrite entitlement is no longer used anywhere and is removed. Unlike entitlement gating — which Cadence bypasses for direct self.field.method() calls within an owning composite — access(self) is enforced by the compiler even across composite boundaries. Verified by probe: an access(all) method on Pool calling self.state.applyLedgerDelta fails to compile with "access denied: function requires `self` authorization". Pool genuinely cannot reach PoolState's state-writing methods; the only path is through PoolState's access(all) entry points, which run validation first. Also reverts Reserves' mutating methods (deposit, withdraw, addSupportedToken) from access(StateWrite) to access(contract), since StateWrite is gone and Reserves is only called from PoolState's access(self) appliers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Describes the three-component pipeline (Orchestrator → Validator → Mutator) and the two design goals driving it: minimum state-writing surface area and uniform invariant enforcement after every operation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends the Mutation Pipeline design with a pre-validation phase for time-dependent state (interest indices, utilization, etc.). Each intent Orchestrator now calls state.applyTimeBasedMutations() before invoking its Mutator so validation runs against time-advanced state. Also adds explicit guidance on check placement: what belongs in the Validator (per-request / operation-mode / cheap early-exit checks) vs what belongs in Invariants (universal post-state properties). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Compress PoolState's deposit/applyDeposit and withdraw/applyWithdraw pairs into single access(all) Mutators (applyDeposit, applyWithdraw). Each Mutator expresses its Validator as a pre condition (calling FlowALP.validateXxx, which now returns Bool) and the universal invariant check as a post condition (calling self.invariantsHold(), also Bool-returning). Apply the same pre/post pattern to registerPosition. Adds a matching validateRegisterPosition view function for consistency. Validators (validateDeposit, validateWithdraw, validateRegisterPosition) remain separate contract-level view functions — restructured to return Bool so they can be invoked from pre blocks. Vault cross-checks (type, balance match the intent) stay inline in applyDeposit's pre because they need the resource reference. PoolState gains an isSupportedToken view to expose supported-token lookup to external validators. Renames checkInvariants to invariantsHold (returns Bool) so it can be used as a post condition.
Update the design doc to reflect that Validators now return Bool (called from Mutator pre blocks) and that the universal post-state check is invariantsHold (called from Mutator post blocks). Replaces the old "validator panics; checkInvariants runs as the final phase" narrative with the actual pre/body/post structure. Updates: Summary pipeline diagram and bullets, Goal 2 phrasing, Validator section signature and semantics, Mutator section code example and phase descriptions, Invariants section, Access-control convention list.
Remove DepositIntent and WithdrawIntent — for simple operations the struct is redundant with the resource (vault carries tokenType and amount) or with the underlying parameters. Mutator signatures now take primitive arguments and resources directly. A multi-input op like liquidation may reintroduce a parameter-bundle struct when the operation lands. Remove validateDeposit / validateWithdraw / validateRegisterPosition. The validation rules now live inline as Cadence pre-conditions on each Mutator, which is more idiomatic, gives granular per-rule error messages, and removes the parallel Bool-returning function layer that existed only to be called from pre. Net result: Pool's orchestrators are pure pass-throughs; PoolState's Mutators are self-contained pre/body/post units. The full pipeline is Orchestrator -> applyTimeBasedMutations -> Mutator (pre, body, post). Update the design doc to match: removes the Intent subsection, rewrites the Validator subsection to describe pre-conditions as the Validator, updates Mutator examples and the access-control convention.
Now that there are no contract-level applyDeposit/applyWithdraw functions to disambiguate against, the shorter names read better. Pool's orchestrators forward as self.state.deposit(...) / self.state.withdraw(...). Updates the contract code, top-of-file design block, and design doc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds a proposal for how to applying state mutations and enforcing invariants in FlowALP. The ideas should be communicated in the PR diff, so I'll keep this description short :)