{"id":49600847,"url":"https://github.com/milerius/kast-project","last_synced_at":"2026-05-04T09:09:55.938Z","repository":{"id":353033916,"uuid":"1216272777","full_name":"Milerius/kast-project","owner":"Milerius","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-22T07:46:15.000Z","size":422,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-22T08:28:16.043Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Milerius.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-20T18:36:23.000Z","updated_at":"2026-04-22T07:46:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Milerius/kast-project","commit_stats":null,"previous_names":["milerius/kast-project"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Milerius/kast-project","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Milerius%2Fkast-project","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Milerius%2Fkast-project/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Milerius%2Fkast-project/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Milerius%2Fkast-project/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Milerius","download_url":"https://codeload.github.com/Milerius/kast-project/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Milerius%2Fkast-project/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32600978,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"online","status_checked_at":"2026-05-04T02:00:06.625Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-04T09:09:55.108Z","updated_at":"2026-05-04T09:09:55.931Z","avatar_url":"https://github.com/Milerius.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eKast\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  A deterministic SOL → USDC cross-chain borrow/bridge/repay loop.\u003cbr\u003e\n  \u003cb\u003eDeposit · Borrow · Bridge · Repay · Withdraw\u003c/b\u003e — every step modeled as a pure FSM, verified before it touches the chain.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Milerius/kast-project/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Milerius/kast-project/ci.yml?style=flat-square\u0026logo=github\u0026label=CI\u0026branch=main\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Milerius/kast-project/actions/workflows/smoke.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Milerius/kast-project/smoke.yml?style=flat-square\u0026logo=github\u0026label=smoke\" alt=\"Smoke\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Milerius/kast-project/actions/workflows/integration.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Milerius/kast-project/integration.yml?style=flat-square\u0026logo=github\u0026label=integration\" alt=\"Integration\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Milerius/kast-project/actions/workflows/nightly.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Milerius/kast-project/nightly.yml?style=flat-square\u0026logo=github\u0026label=nightly\" alt=\"Nightly\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/typescript-5.6-3178c6?style=flat-square\u0026logo=typescript\" alt=\"TypeScript 5.6\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/next.js-15-black?style=flat-square\u0026logo=next.js\" alt=\"Next.js 15\"\u003e\n  \u003ca href=\"#license\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-blue?style=flat-square\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Milerius/kast-project/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Milerius/kast-project/ci.yml?style=for-the-badge\u0026label=tests\u0026branch=main\" alt=\"Tests\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Milerius/kast-project/actions/workflows/nightly.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/Milerius/kast-project/nightly.yml?style=for-the-badge\u0026label=property%20%2B%20mutation\" alt=\"Property + Mutation\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cb\u003ePure FSM · Optimistic UI · Atomic tx bundles · Property-verified transitions · Deterministic replay\u003c/b\u003e\n\u003c/p\u003e\n\n---\n\n## Why Kast?\n\nCross-chain DeFi flows break in boring, repeatable ways: RPC caches serve stale state, wallet modals block on user input, bridges settle out-of-order, UI reducers race with chain derivations. Most teams ship a screenful of `useEffect` soup and pray.\n\nKast is that loop written once, with the state machine extracted and proven.\n\n|                              | Kast                                                       | Typical DeFi frontend                               |\n|------------------------------|------------------------------------------------------------|-----------------------------------------------------|\n| **State model**              | Pure finite-state machine, 6 states × 8 events             | Ad-hoc booleans, `isLoading`, `hasBridged`          |\n| **UI ↔ chain sync**          | `derivePositionFromChain` single source of truth           | Multiple effects, last-write-wins                   |\n| **Optimistic transitions**   | Advance FSM before await, rollback on error                | Wait for receipt, blocking modal                    |\n| **Verification**             | fast-check properties + Cucumber BDD + mutation testing    | Unit tests for the happy path                       |\n| **Bridge settlement**        | Polled orders, persistent across reloads, resume on mount  | \"Refresh the tab\"                                   |\n| **Adapters**                 | Swappable: Kamino/Mayan behind pure interfaces             | Direct SDK calls sprinkled through components       |\n| **RPC resilience**           | 429 rate-limit tolerant, best-effort reads, never blocks UI| Overlay error on every transient failure            |\n| **CI rigor**                 | 4 workflows: ci · integration · smoke · nightly mutation   | Single job, unit tests                              |\n\n---\n\n## Architecture\n\nEvery user action passes through the pure orchestrator, which decides whether the transition is legal, builds the on-chain transaction via adapters, and advances the UI state optimistically.\n\n```text\n                   ┌──────────────────────────────────────┐\n                   │            apps/web (Next.js)         │\n                   │   Privy wallets · zustand · React UI  │\n                   └──────────────────┬───────────────────┘\n                                      │\n            ┌─────────────────────────▼──────────────────────────┐\n            │                                                    │\n            │   1. canFire(state, event)  → gate the button      │\n            │   2. adapter.buildTx(...)   → build on-chain tx    │\n            │   3. transition(state, ev)  → optimistic advance   │\n            │   4. wallet.sendTransaction → user signs           │\n            │   5. derivePositionFromChain on reload             │\n            │                                                    │\n            │   ✓ Every write is FSM-gated, chain-authoritative  │\n            └─────────────────────────┬──────────────────────────┘\n                                      │\n              ┌───────────────────────┼───────────────────────┐\n              │                       │                       │\n       ┌──────▼──────┐        ┌───────▼───────┐       ┌───────▼───────┐\n       │   kamino-   │        │    mayan-     │       │ orchestrator  │\n       │   adapter   │        │   adapter     │       │  (pure FSM)   │\n       │             │        │               │       │               │\n       │ deposit /   │        │  quote /      │       │ canFire       │\n       │ borrow /    │        │  buildBridge  │       │ transition    │\n       │ repay /     │        │  getStatus    │       │ derive        │\n       │ withdraw    │        │  SWIFT+MCTP   │       │               │\n       └──────┬──────┘        └───────┬───────┘       └───────────────┘\n              │                       │\n       ┌──────▼──────┐        ┌───────▼────────┐\n       │  Kamino     │        │  Mayan Swift   │\n       │  main mkt   │        │  + relayers    │\n       │ (on-chain)  │        │  Sol ↔ Base    │\n       └─────────────┘        └────────────────┘\n```\n\n**Invariants enforced by the FSM:**\n\n- **canFire gates every button** → no button is clickable in a state where the action is illegal\n- **transition is total** → every (state, event) pair either produces a new state or panics in tests\n- **optimistic setState + rollback** → UI reflects intent the instant the user clicks, but reverts on wallet failure\n- **derive is chain-authoritative** → on reload/refocus, UI is recomputed from `(obligation, baseUsdc, pendingOrders)`, never from stale zustand\n- **pending orders survive reloads** → bridge settlement is polled from `localStorage`, resumed on mount\n\n---\n\n## Highlights\n\n⚡ **Pure orchestrator** — 6-state, 8-event FSM in `@kast/orchestrator`; zero IO, 23 unit tests, 5 property tests\n\n🔒 **Optimistic UI with rollback** — `setState` fires before the Privy modal blocks, reverts on send failure; the position pill reflects intent within one render\n\n🧱 **Atomic tx bundles** — Mayan bundles ship with ephemeral signers + placeholder blockhash; Kast rewrites the blockhash, preserves LUTs, and resigns before send\n\n🪝 **Pending-order poller** — bridge orders persist in `localStorage`; a top-level hook polls Mayan status and advances the FSM on settlement, surviving reloads\n\n📊 **`derivePositionFromChain`** — on mount/refocus, the UI recomputes state from the Kamino obligation + Base USDC + pending orders. No reducer drift.\n\n🧪 **Five-layer verification** — unit (vitest) · property (fast-check) · BDD (Cucumber scenarios) · integration (live RPC) · smoke (Playwright + Docker) · mutation (Stryker, nightly)\n\n🏗️ **Adapter pattern** — Kamino and Mayan hide behind thin interfaces (`buildDepositTx`, `quote`, `buildBridgeTx`, `getOrderStatus`); the app imports no SDKs directly\n\n🚦 **RPC-resilient** — public Base RPC rate-limits land as `0n` fallbacks, not dev-overlay errors; Kamino `reload()` is forced between txs to dodge cached obligation state\n\n---\n\n## Packages\n\n| Package                                          | Purpose                                                         | Tests |\n|--------------------------------------------------|-----------------------------------------------------------------|------:|\n| [`@kast/orchestrator`](packages/orchestrator/)   | Pure FSM: `transition`, `canFire`, `derivePositionFromChain`    |    23 |\n| [`@kast/kamino-adapter`](packages/kamino-adapter/)| Deposit · Borrow · Repay · Withdraw tx builders (klend-sdk)     |    18 |\n| [`@kast/mayan-adapter`](packages/mayan-adapter/) | Quote · BuildBridgeTx · GetOrderStatus (Swift + MCTP SDK)       |    22 |\n| [`@kast/verify`](packages/verify/)               | fast-check property tests over the FSM                          |     5 |\n| [`@kast/shared`](packages/shared/)               | Types, constants, chain IDs, USDC base units                    |     — |\n| [`@kast/web`](apps/web/)                         | Next.js 15 App Router UI; Privy embedded wallets, zustand store |     — |\n| [`scenario-tests`](scenario-tests/)              | Cucumber BDD: 6 scenarios, 34 steps, in-memory + live runners   |     6 |\n| [`integration-tests`](integration-tests/)        | Live-RPC smoke of Kamino reserves + Mayan quote                 |     — |\n\n---\n\n## Quick Start\n\n```bash\ncorepack enable \u0026\u0026 corepack prepare pnpm@9.12.0 --activate\npnpm install\n\n# Privy app id is the only required env var\ncp apps/web/.env.example apps/web/.env.local\n# edit apps/web/.env.local → NEXT_PUBLIC_PRIVY_APP_ID=...\n\npnpm dev                     # Next.js dev server on :3000\n```\n\nThen open [http://localhost:3000](http://localhost:3000), log in via Privy (email or Google), fund the generated wallets, and walk the Open Position → Close Position flow.\n\n---\n\n## Tests\n\n```bash\npnpm typecheck                   # all 8 workspace projects\npnpm lint                        # eslint + typed TS rules\npnpm format:check                # prettier\n\npnpm test                        # unit + property + BDD  (fast, no RPC)\npnpm test:unit                   # package unit tests only\npnpm test:property               # fast-check over the FSM\npnpm test:bdd                    # Cucumber scenarios\n\npnpm test:integration            # live mainnet reads   (needs KAST_INTEGRATION=1)\npnpm test:smoke                  # Playwright + Docker  (needs docker)\npnpm test:mutation               # Stryker (orchestrator + verify)\n```\n\nEvery PR runs: `lint → typecheck → format:check → test → build`.\nNightly adds mutation testing + extended integration.\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ch2\u003e🧪 Verification Strategy\u003c/h2\u003e\u003c/summary\u003e\n\n| Layer           | Tool             | What it catches                                              | Where                                   |\n|-----------------|------------------|--------------------------------------------------------------|-----------------------------------------|\n| **Unit**        | vitest           | Adapter shape, tx builder output, reserve decoding           | `packages/*/src/*.test.ts`              |\n| **Property**    | fast-check       | FSM transition totality, illegal-event rejection, determinism| `packages/verify/src/fsm.property.test.ts`|\n| **BDD**         | Cucumber         | Full user journeys: open, close, partial repay, idempotency  | `scenario-tests/features/*.feature`     |\n| **Integration** | vitest + live RPC| Kamino obligation decode + Mayan quote against mainnet       | `integration-tests/`                    |\n| **Smoke**       | Playwright       | Headless browser walkthrough on a Docker image               | `apps/web/tests/smoke/`                 |\n| **Mutation**    | Stryker          | Mutant tests: does the suite actually catch the bugs?        | `stryker.conf.mjs` in orchestrator+verify|\n\n**Property coverage** — the FSM is exhaustively fuzzed over random state/event sequences. fast-check asserts:\n- `transition(state, event)` is total for every legal pair\n- Illegal events throw (never silently noop)\n- Replaying the same event tape produces identical state sequences\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ch2\u003e🎯 FSM Design\u003c/h2\u003e\u003c/summary\u003e\n\n```ts\ntype PositionState =\n  | 'IDLE'\n  | 'DEPOSITED'\n  | 'BORROWED'\n  | 'BRIDGING_OUT'\n  | 'ACTIVE_ON_BASE'\n  | 'BRIDGING_BACK';\n\ntype Event =\n  | { type: 'DEPOSIT';       lamports: bigint }\n  | { type: 'BORROW';        amountUsdc: bigint }\n  | { type: 'BRIDGE_OUT';    amountUsdc: bigint; orderHash: string }\n  | { type: 'BRIDGE_SETTLED' }\n  | { type: 'BRIDGE_REFUND' }\n  | { type: 'BRIDGE_BACK';   amountUsdc: bigint; orderHash: string }\n  | { type: 'REPAY';         amount: bigint | 'all' }\n  | { type: 'WITHDRAW';      lamports: bigint | 'all' };\n\ntransition(state, event);     // pure: PositionState, throws IllegalTransitionError\ncanFire(state, event.type);   // pure: boolean — gates UI buttons\nderivePositionFromChain(ctx); // pure: state from (obligation, baseUsdc, pending)\n```\n\n**Key properties:**\n\n- **Pure functions only** — no IO, no globals, no mocks required to test\n- **Reducer is total** — every legal `(state, eventType)` pair has a defined transition\n- **`canFire` gates the UI** — the Deposit button is unclickable unless `canFire(state, 'DEPOSIT')`\n- **Optimistic transitions** — UI calls `setState(transition(prev, ev))` *before* the await, rolls back on error\n- **Chain-authoritative derive** — on page load, `derivePositionFromChain({ obligation, baseUsdc, pendingOrders })` recomputes state from the world, not from cached zustand\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ch2\u003e🏛️ Design Principles\u003c/h2\u003e\u003c/summary\u003e\n\n1. **Pure core, impure shell** — the orchestrator never imports web3, viem, or React; every IO lives in `apps/web` or the adapters\n2. **FSM before UI** — the state model was proven with fast-check before a single component was styled\n3. **Optimistic by default** — the Privy Solana modal doesn't resolve until the user dismisses it; advance state immediately, rollback on error\n4. **Adapters are interfaces, not classes** — `KaminoAdapter` and `MayanAdapter` are plain objects of pure async functions, easy to mock\n5. **Derive state from chain** — zustand holds transient UI intent; every reload recomputes from the obligation + Base USDC + pending orders\n6. **Tests are contracts, not ceremony** — property tests prove FSM invariants; BDD proves user journeys; smoke proves the build serves; mutation proves the tests actually catch bugs\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ch2\u003e📋 Project Status\u003c/h2\u003e\u003c/summary\u003e\n\n**Shipped:**\n- Deposit SOL collateral on Kamino main market\n- Borrow 5 USDC against SOL\n- Bridge USDC Solana → Base via Mayan (SWIFT / FAST_MCTP)\n- Bridge USDC Base → Solana via Mayan\n- Full repay + partial repay (bonus)\n- Withdraw all collateral\n- Privy embedded wallets — Solana + EVM, auto-created on first login (bonus)\n- Optimistic FSM transitions with rollback\n- Pending-order persistence + background poller\n- Full test stack: unit · property · BDD · integration · smoke · mutation\n- 4-workflow CI: `ci` · `integration` · `smoke` · `nightly`\n- Vercel production deploy\n\n**Deferred (future work):**\n- Collateral adjustment flow — add/withdraw without closing the position (bonus #3)\n- Position health monitoring + LTV warnings\n- Base gas sponsorship via Privy paymaster\n- Multi-position support\n- User-facing slippage controls\n- Formal model (TLA+/Quint) — fast-check is the current TS-native analogue\n- Live BDD step definitions against real mainnet\n\nSee [`docs/PROGRESS.md`](docs/PROGRESS.md) for the full tracking.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003ch2\u003e📂 Docs\u003c/h2\u003e\u003c/summary\u003e\n\n- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — Mermaid diagrams for repo layout, component map, data flow, tx bundles\n- [`docs/AI_ORCHESTRATION.md`](docs/AI_ORCHESTRATION.md) — how Claude agents were used to plan, implement, and verify\n- [`docs/TIME_LOG.md`](docs/TIME_LOG.md) — hour-by-hour build log\n- [`docs/PROGRESS.md`](docs/PROGRESS.md) — shipped vs deferred\n- [`docs/superpowers/specs/2026-04-20-kast-defi-design.md`](docs/superpowers/specs/2026-04-20-kast-defi-design.md) — design RFC: goals, non-goals, trade-offs\n- [`docs/superpowers/plans/2026-04-20-kast-defi-implementation.md`](docs/superpowers/plans/2026-04-20-kast-defi-implementation.md) — implementation plan\n\n\u003c/details\u003e\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmilerius%2Fkast-project","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmilerius%2Fkast-project","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmilerius%2Fkast-project/lists"}