Skip to content

Repository files navigation

Cork Rollover

Cork Rollover is a Solidity/Foundry protocol for signed rollover orders between Cork Phoenix pools. Phoenix pools issue a dual share pair — cPT (principal) and cST (swap) — against a collateral asset and mature at a fixed expiry. Rollover lets a cPT holder move a cST position from an expiring source pool into a destination pool in a single signed flow: a filler pushes source cST into the cPT holder's rollover contract, the rollover contract performs the Phoenix unwindMint/deposit leg under a signed hook plan, and the filler later pays a premium to claim the produced destination cST — or the residual is reclaimed if the premium never arrives.

The protocol combines an ERC-7683-compatible settler pair (exact and partial fill modes), per-cPT-holder clones with immutable arguments (CWIA), ERC-7484 module attestation for hook targets, and filler-side orchestration through BaseFiller. Runtime order execution is single-chain: admission requires originChainId == block.chainid and destinationChainId == block.chainid.

Code in src/ is the source of truth. The protocol spec and invariant ledger explain the integration contracts. These documents do not establish a security-review level.

Release identity and status

0.2.0 identifies this Rollover source release. A review-branch snapshot does not establish publication. Published release records are listed in Rollover releases; current deployment information belongs in the Cork documentation.

CorkRolloverContract, CorkRolloverContractFactory, ExactSettler, and PartialSettler retain a version() return value of 1.0.0. The mismatch with 0.2.0 is intentional for this preparation and departs from the release policy's source/getter equality requirement; it is not dual-namespace compliance. The getter alone cannot distinguish this distribution from earlier source. Identify an installation by its exact source commit and verified deployment runtime-code hash. The EIP-712 signing domain remains CorkSettler/1.0.0.

Earlier on-chain addresses are not automatically 0.2.0 deployments. Use the explicit deployment records rather than inferring a version from an address or getter. Verification does not establish a security-review level, production authorization, or retirement of earlier deployments.

The deployment manifest records shadow deployment observations from 2026-09-11 on Base and Arbitrum: 13 canonical components plus BaseFiller, with deployed addresses, artifact ABIs, runtime-code hashes, transaction receipts, and binding checks at the stated finalized blocks. Its artifact evidence compares deployed ABIs and bytecode templates with the candidate source. These observations are preserved, not refreshed by this revision. source_release_version is 0.2.0; the embedded release manifest retains the deployment campaign identifier 0.1.0-rc.2 without relabeling the deployment. This file is not production or publication authorization.

The historical filename is retained, but consumers must dispatch on schema_version: this is rollover-shadow-release-bundle-v2, validated by the v2 structural schema. V2 replaces original build commit/tree pointers with source_identity_digest. The top-level source_identity lists SHA-256 hashes of 65 tracked production/build files and four root dependency gitlinks; recursive pins are inherited from those gitlinks. It excludes release prose and this manifest to avoid self-reference. JSON digests are SHA-256 of Python json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8"), prefixed sha256:. File hashes cover exact bytes. Consumers must verify these hashes and all nested digests in addition to the schema. The content identity does not claim an original deployment commit or an already-published tag. Release and Distribution records must bind the final public commit separately after it exists.

Compatibility in 0.2.0

This source distribution uses public Market Registry 0.5.0. The Registry dependency and its recursive dependencies are pinned by the repository's submodules; use those pins rather than substituting an older Registry interface.

  • BaseFiller.JITMarketParams includes oracleSalt. Adding this tuple field changes the executeWithMarket and hashJITMarketParams selectors. Rebuild calldata and bindings against this interface; do not reuse the older tuple.
  • The just-in-time (JIT) market commitment preimage includes oracleSalt. Off-chain builders must include it when computing RolloverParams.jitMarketHash; use hashJITMarketParams as the canonical commitment helper. Changing only calldata without rebuilding the committed hash is not sufficient.
  • Registry-backed recipe paths require registered assets. Recipe verification receives the expiry timestamp and creating context, and runs for both new and existing pools. Integrations must provide the creation/expiry-aware recipe interface, not the earlier verification signature.
  • Controller market creation and PoolManager market-return tuples use the ten-field, fee-bearing Market structure, including swapFeePercentage and unwindSwapFeePercentage. Both fees are immutable pool-identity inputs in 18-decimal percentage units (1e18 = 1%). Update encoders and decoders together.

See CHANGELOG.md for compatibility and support-status disclosures.

Glossary

Term Meaning
cPT / cST Cork Principal Token / Cork Swap Token — the dual shares a Phoenix pool mints 1:1 on deposit. Rollover orders move cST.
CA Collateral asset of a Phoenix pool (e.g. USDC). unwindMint burns cPT+cST for CA; deposit mints them from CA.
cPT holder Owner of a CorkRolloverContract clone; signs OrderData that commits the hook plan through OrderData.rolloverIntentHash.
Rollover contract Per-cPT-holder CWIA clone that holds positions and executes the Phoenix unwind/deposit leg. Must be whitelisted on Phoenix.
Filler Party that supplies source cST in the ROLLOVER fill and pays the premium in the PREMIUM fill.
Hook plan Sequence of hook modules committed by the cPT-holder-signed OrderData.rolloverIntentHash, each attested in the ERC-7484 registry.
Latch Transient (rolloverContract, orderDigest, settler) record the factory holds during a fill frame.
Residual Destination cST measured after the rollover leg, held until the premium fires or it is reclaimed.

Full terminology: protocol glossary.

How a Rollover Works

sequenceDiagram
    participant User
    participant Filler
    participant Settler
    participant Factory as CorkRolloverContractFactory
    participant Rollover as CorkRolloverContract
    participant Phoenix as Phoenix PoolManager

    User->>Settler: open / openFor (signed ERC-7683 order)
    Filler->>Settler: fill(..., ROLLOVER) with srcCST
    Settler->>Factory: executeIntentHooks (approved-settler gate, latch)
    Factory->>Rollover: forward intent
    Rollover->>Rollover: verify cPT-holder OrderData signature,<br/>check committed hooks vs ERC-7484 registry
    Rollover->>Phoenix: unwindMint (source pool, burn cPT+cST → CA)
    Rollover->>Phoenix: deposit (destination pool, CA → cPT+cST)
    Rollover-->>Settler: measured dstCST residual
    Filler->>Settler: fill(..., PREMIUM) — premium routed to rollover contract
    Settler-->>Filler: release dstCST residual to the recorded destination
Loading
  1. A user signs an ERC-7683 GaslessCrossChainOrder carrying OrderData (EIP-712 domain CorkSettler/1.0.0, ERC-1271 supported).
  2. A relayer or filler calls Settler.open / openFor, or a filler fills atomically without a prior open where the state path allows it.
  3. fill(..., ROLLOVER) pulls srcCST from the filler, pushes it to the user's rollover contract, and dispatches through CorkRolloverContractFactory.executeIntentHooks. The factory checks the caller against its default-deny settler allowlist and latches the in-flight (rolloverContract, orderDigest, settler) triple.
  4. The rollover contract verifies the cPT holder's OrderData signature for the dispatch, checks the committed RolloverIntent hook targets against the ERC-7484 registry by hook bucket, unwinds the source Phoenix pool, optionally runs mid-rollover hooks, deposits into the destination pool, and returns measured dstCST to the settler.
  5. fill(..., PREMIUM) collects the filler's premium, records the premium-fired latch, and releases the recorded dstCST residual in-frame.
  6. markExpired, cancel, and async-only reclaim finalize stopped orders or route unpaid residuals.

Order Lifecycle

Order status is one of None, Opened, Settled, Expired, Cancelled, Closing (src/types/RolloverTypes.sol). Settled, Expired, and Cancelled are hard terminals. The gates in src/types/SettlerTypes.sol define what each action admits: cancel is blocked from the terminals and from Closing; markExpired admits Opened and Closing after the fill deadline; reclaim is blocked only from Settled and Cancelled. The exact settler keeps one order-level fill record; the partial settler keeps per-filler records, premium latches, and residuals, so multiple fillers can each roll a portion and settle independently.

Premium Payer vs Rollover Filler

fillerData is a canonical 11-field payload with premiumFor after destination. In ROLLOVER, premiumFor must be zero and the record is keyed by msg.sender. In PREMIUM, premiumFor is mandatory and must identify the recorded rollover filler being paid for. The payer supplies premium tokens but never controls the settlement destination: settlement always uses the destination recorded during ROLLOVER, and PREMIUM payloads that try to change it revert. No legacy fillerData shape is accepted.

Contracts

Contract Role
BaseSettler Shared ERC-7683 lifecycle, admission, signatures, token movement, premium accounting, cancellation, expiry and reclaim.
ExactSettler One order-level fill record and residual.
PartialSettler Per-filler records, premium latches and residuals.
CorkRolloverContractFactory Per-cPT-holder clones, default-deny settler allowlist, transient dispatch latch and role-gated trust configuration.
CorkRolloverContract Signed hook-plan execution, Phoenix unwind/deposit and timelocked trust changes.
BaseFiller Atomic filler orchestration, optional destination-market creation and premium-cap refunds.
EvcRolloverAdapter Euler Vault Connector (EVC) adapter with Permit2 funding; documented separately as adapter context.
src/modules/ Stateless delegatecall modules for committed hook plans.

Phoenix and the ERC-7484 attestation registry are trusted external dependencies. Rollover contracts must be whitelisted on Phoenix to call unwindMint/deposit. Hook targets must be attested for their committed module-type buckets. Market Registry recipe approval and asset registration are separate integration requirements from ERC-7484 hook attestation.

Getting started

Requirements: Foundry (the consumer CI workflow pins v1.7.1), Solidity 0.8.34, Python 3.12 for consistency checks, and recursive git submodules.

Only after the public v0.2.0 tag and release are published, a pinned Foundry dependency installation will be:

forge install Cork-Technology/rollover@v0.2.0

That example is conditional, not a claim that the tag is available now. For an already obtained checkout of this candidate, initialize its pinned dependencies and build/test locally:

git submodule update --init --recursive
forge build
forge test

The repository uses Cancun transient storage. Build-profile settings are in foundry.toml; use the deploy profile for production-bytecode size checks, not the default test profile:

FOUNDRY_PROFILE=deploy forge build --sizes src

These commands describe how to validate the checkout; they are not a statement that this candidate has passed those checks or that its bytecode is deployed.

Testing and consumer CI

FOUNDRY_PROFILE=ci forge test --match-path "test/unit/**/*.t.sol" -vvv
FOUNDRY_PROFILE=ci forge test --match-path "test/integration/**/*.t.sol" -vvv
FOUNDRY_PROFILE=ci forge test --match-path "test/invariant/**/*.t.sol" -vvv

The retained consumer workflow covers formatting, compilation, contract size, unit/integration/invariant tests, NatSpec, static analysis and consistency checks. See the workflow for exact commands and tool pins. Static analysis and passing tests do not establish a security-review level.

The invariant ledger and both files under docs/agent-context/spec/ are consumer test inputs, not disposable documentation. Keep them in source checkouts. The citation checkers verify reference structure and line bounds; they do not prove that prose is semantically equivalent to the cited code. Some inherited source-line references have weak alignment with their named symbols; semantic citation review remains outstanding for this unpublished candidate.

Repository and documentation map

Path Contents
src/ Production contracts, interfaces, libraries, modules and types.
test/ Retained consumer tests, fixtures, harnesses and mocks.
scripts/ci/ Consumer consistency checks.
docs/INVARIANTS.md Canonical invariant IDs cross-referenced by NatSpec and tests.
docs/spec/SPEC.md Protocol reference and reading order.
docs/spec/AUDIT-GLOSSARY.md Technical terms and stable spec identifiers.
Design notes Retained design reference and runtime-read fixture.
Threat-model fixture Defensive assumptions and runtime-read fixture; not a completed review.
CHANGELOG.md Draft version and compatibility record.

License

Tracked Solidity sources carry SPDX identifiers, primarily BUSL-1.1; the vendored narrow external interfaces IERC7484, IPoolManager, and IPoolShare are MIT. No root license file is tracked.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages