Skip to content
psatomasPublic

About

Execution-selection protocol for Web3 intents with modular strategies and deterministic scoring.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

Β 

History

70 Commits

Folders and files

Repository files navigation

ExeKPro (Execution Kernel Protocol)

CI License: Apache-2.0

A composable execution infrastructure layer for Web3 intents. Instead of routing every intent through one monolithic solver, competing execution modules are simulated, scored, and the best-scoring one executes on-chain β€” a market of execution strategies rather than static execution logic.

Built with production intent: explicit trust boundaries, auditable access control, and rigor over speculative scope.


Live Deployment

URL Serves Worker
https://exekpro.com/ ExeKPro Protocol Console (apps/frontend) exekpro-console
https://exekpro.com/about ExeKPro public website (apps/landing) exekpro-landing

Two independently deployed Cloudflare Workers behind one zone, split by path via Worker Routes (not a Custom Domain, which can't split a hostname by path) β€” see each app's own wrangler.jsonc. The console has no public kernel deployment behind it yet, though β€” it only reads/writes whatever chain the connecting wallet points at (see Getting Started below), since packages/config doesn't have a real testnet/mainnet entry yet.


Getting Started (Local Development)

Prerequisites: Node 22+, Foundry (anvil/forge/cast), npm.

git clone https://github.com/psatomas/ExeKPro.git
cd ExeKPro
npm install

# 1. Deploy the kernel to a local anvil chain and register the demo ROUTE
#    intent + both example modules (starts anvil itself if it isn't already
#    running). See scripts/deploy-local.sh for exactly what this does.
./scripts/deploy-local.sh

# 2. In a separate terminal: the read-only API
npm run start --workspace=apps/api

# 3. In another separate terminal: the console
npm run dev --workspace=apps/frontend

Open http://localhost:3000, connect a wallet pointed at http://127.0.0.1:8545 (chain id 31337), and the Route intent should be selectable end to end β€” simulate & score both candidate modules, execute, and see the result flow through the indexer to the API back to the console.

Service Default port Started by
anvil (local chain) 8545 scripts/deploy-local.sh (or run anvil yourself first)
apps/api 4000 npm run start --workspace=apps/api
apps/frontend 3000 npm run dev --workspace=apps/frontend

Other runnable, code-only examples against the same local deployment (no frontend/API involved) β€” each is a real end-to-end script, not a mock:

  • node packages/sdk/examples/quickstart.ts β€” every SDK client (intentBuilder, registries, scorePolicy, executionClient), including a governance updateWeights() call.
  • node apps/execution-node/examples/quickstart.ts β€” the off-chain process β†’ solve β†’ submit pipeline (runIntent(...)).
  • node apps/indexer/examples/quickstart.ts β€” backfilling kernel events and deriving execution metrics.

To run the contract test suite: forge test from packages/contracts/. To run the frontend's own real end-to-end test (e2e/full-flow.spec.ts β€” a real headless browser, real wagmi, real chain, real apps/api): with anvil/apps/api already running as above, npm run e2e from apps/frontend/.

Troubleshooting

  • Console shows "No intents registered yet." / step 2 onward is greyed out. Not a bug β€” the chain your wallet is connected to genuinely has nothing registered on it yet. Run ./scripts/deploy-local.sh (or restart anvil first if you'd already sent any transaction on it β€” the script requires a fresh chain, since the deployed addresses are only deterministic from nonce 0).
  • Console appears stuck on "Loading registered intents..." forever, no error shown. Most likely your wallet is connected to a chain other than local anvil (check the network badge in the header β€” a red "Wrong network" badge confirms it). packages/config currently only has a localAnvil deployment entry; connecting via Sepolia or any other chain silently returns no data rather than an error, by design of today's useKernelClient() (see apps/frontend/CLAUDE.md) β€” switch your wallet to local anvil.
  • scripts/deploy-local.sh exits with "already has a nonce of N". Your anvil chain isn't fresh β€” restart it (a new anvil process always starts at nonce 0) and re-run the script.

System Overview

The Execution Kernel Protocol defines a standardized execution layer for Web3, where user intents are resolved by an on-chain kernel that ranks interchangeable execution strategies and runs the winner.

Core Layers

  • Intent Layer β€” standardized, owner-registered intent types that requests are declared against
  • Execution Layer β€” the on-chain kernel (ExecutionEngine) that fetches candidate modules for an intent type, scores their simulated output, and executes the winner
  • Execution Modules β€” composable, independently deployed strategy units (routing, MEV protection, liquidity selection, ...) that compete for selection
  • Observability Layer β€” indexing and performance tracking of execution outcomes
  • SDK Layer β€” developer interface for intent creation and execution integration

There is no separate settlement layer: the selected module's execute() call is the on-chain settlement β€” it performs the swap/route/transfer directly. A prior draft of this document named a standalone SettlementRouter; that idea is dropped until a concrete need for a settlement step distinct from module execution actually appears (e.g. batching or netting across multiple executed intents).


Core Concept: Competitive Module Selection

Execution is not performed by a single solver, and β€” as currently implemented β€” not by a static pipeline either. Each registered intent type has a pool of candidate modules; every intent execution re-runs the competition:

Intent (intentType, intentData)
  ↓
IntentRegistry.isIntentActive(intentType)?  β€” reject if not
  ↓
ModuleRegistry.getModules(intentType) β€” fetch active candidates
  ↓
for each candidate that supportsIntent(intentType):
    simulate() β†’ ExecutionQuote β†’ ScorePolicy.evaluate() β†’ signed score
  ↓
highest-scoring module wins
  ↓
winning module .execute() β€” this *is* settlement, no separate step

Each module is:

  • independently deployed and replaceable, without touching ExecutionEngine
  • scored on the same standardized ExecutionQuote (cost, quality, MEV risk, latency)
  • free to lose the competition on one call and win it on the next, as weights or on-chain conditions change

Future direction, not yet built: chaining multiple winning modules into a single execution graph (e.g. MEV-protect then route) rather than picking exactly one. Don't treat pipeline chaining as implemented until ExecutionEngine actually composes more than one module per intent.


Repository Structure

ExeKPro/

β”œβ”€β”€ packages/
β”‚
β”‚   β”œβ”€β”€ contracts/                         # On-chain execution core
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ ExecutionEngine.sol
β”‚   β”‚   β”‚   β”‚   └── IntentRegistry.sol
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ modules/                   # Execution primitives
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ ExecutionModuleBase.sol
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ RouterModule.sol
β”‚   β”‚   β”‚   β”‚   └── MevProtectionModule.sol
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ policy/
β”‚   β”‚   β”‚   β”‚   └── ScorePolicy.sol
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ registry/
β”‚   β”‚   β”‚   β”‚   └── ModuleRegistry.sol
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ access/
β”‚   β”‚   β”‚   β”‚   └── ProtocolRoles.sol      # single shared owner, see below
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   └── interfaces/
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ test/
β”‚   β”‚   β”œβ”€β”€ script/
β”‚   β”‚   β”‚   └── Deploy.s.sol
β”‚   β”‚   β”œβ”€β”€ foundry.toml
β”‚   β”‚   └── remappings.txt
β”‚   β”‚
β”‚   β”œβ”€β”€ sdk/                               # Developer integration layer (wraps viem)
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ abi/                       # hand-authored `as const` ABIs, one per contract
β”‚   β”‚   β”‚   β”œβ”€β”€ intent/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ intentBuilder.ts
β”‚   β”‚   β”‚   β”‚   └── types.ts
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ execution/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ executionClient.ts     # wraps ExecutionEngine
β”‚   β”‚   β”‚   β”‚   └── moduleClient.ts        # wraps IExecutionModule (any module address)
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ registry/                  # intentRegistry/moduleRegistry/scorePolicy/protocolRoles clients
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   └── index.ts                   # createExecutionKernelClient(...) bundles all of the above
β”‚   β”‚   β”œβ”€β”€ examples/
β”‚   β”‚   β”‚   └── quickstart.ts              # runnable end-to-end example against a local anvil deployment
β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   └── tsconfig.json
β”‚   β”‚
β”‚   β”œβ”€β”€ types/                              # Shared protocol definitions (zero runtime deps)
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ primitives.ts               # Address/Bytes32/Hex aliases
β”‚   β”‚   β”‚   β”œβ”€β”€ intent.ts                   # mirrors IntentRegistry.sol
β”‚   β”‚   β”‚   β”œβ”€β”€ execution.ts                # mirrors ExecutionQuote.sol, ScorePolicy.Weights
β”‚   β”‚   β”‚   β”œβ”€β”€ module.ts                   # mirrors IExecutionModule.sol, ModuleRegistry.sol
β”‚   β”‚   β”‚   └── index.ts
β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   └── tsconfig.json
β”‚   β”‚
β”‚   └── config/                             # local-anvil chain/address config (no testnet yet)
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ chains.ts                   # localAnvil: Chain
β”‚       β”‚   β”œβ”€β”€ addresses.ts                # localAnvilAddresses, localAnvilModules
β”‚       β”‚   β”œβ”€β”€ constants.ts                # ROUTE_INTENT_TYPE
β”‚       β”‚   └── index.ts
β”‚       β”œβ”€β”€ package.json
β”‚       └── tsconfig.json
β”‚
β”œβ”€β”€ apps/
β”‚
β”‚   β”œβ”€β”€ execution-node/                    # Off-chain execution engine (consumes sdk)
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ engine/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ intentProcessor.ts     # raw request -> Intent (labels intentType)
β”‚   β”‚   β”‚   β”‚   └── executionGraphBuilder.ts # off-chain, gas-free preview of what
β”‚   β”‚   β”‚   β”‚                                # ExecutionEngine would select right now
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ solvers/
β”‚   β”‚   β”‚   β”‚   └── solver.ts              # one generic solver, not per-module β€” every
β”‚   β”‚   β”‚   β”‚                                # module is scored the same generic way, so
β”‚   β”‚   β”‚   β”‚                                # a routerSolver/mevSolver split would just
β”‚   β”‚   β”‚   β”‚                                # be duplicated boilerplate today
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   β”œβ”€β”€ execution/
β”‚   β”‚   β”‚   β”‚   └── executor.ts            # submits via sdk's executionClient
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   └── index.ts                   # runIntent(...) ties the pipeline together
β”‚   β”‚   β”œβ”€β”€ examples/
β”‚   β”‚   β”‚   └── quickstart.ts              # runnable end-to-end example against a local anvil deployment
β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   └── tsconfig.json
β”‚   β”‚
β”‚   β”œβ”€β”€ indexer/                            # Execution observability layer
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ listeners/
β”‚   β”‚   β”‚   β”‚   └── eventListener.ts       # generic backfill/watch over any (address, abi, eventName)
β”‚   β”‚   β”‚   β”œβ”€β”€ processors/
β”‚   β”‚   β”‚   β”‚   └── kernelEventProcessor.ts # backfills all 5 kernel contracts' events into the store
β”‚   β”‚   β”‚   β”œβ”€β”€ metrics/
β”‚   β”‚   β”‚   β”‚   └── executionMetrics.ts    # totalExecutions/executionsByModule/moduleWinRate
β”‚   β”‚   β”‚   β”œβ”€β”€ db/
β”‚   β”‚   β”‚   β”‚   └── memoryStore.ts         # in-memory store β€” swap for a real DB when persistence matters
β”‚   β”‚   β”‚   └── index.ts                   # createIndexer(...) backfills into a fresh store
β”‚   β”‚   β”œβ”€β”€ examples/
β”‚   β”‚   β”‚   └── quickstart.ts              # runnable end-to-end example against a local anvil deployment
β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   └── tsconfig.json
β”‚   β”‚
β”‚   β”œβ”€β”€ api/                                # Integration API layer (Fastify, read-only)
β”‚   β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”‚   β”‚   └── kernelService.ts       # one shared, read-only ExecutionKernelClient
β”‚   β”‚   β”‚   β”œβ”€β”€ controllers/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ intentsController.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ modulesController.ts   # includes /predict β€” off-chain solve(), no gas spent
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ metricsController.ts   # wraps apps/indexer (aggregate metrics)
β”‚   β”‚   β”‚   β”‚   └── executionsController.ts # raw per-tx history, most-recent-first
β”‚   β”‚   β”‚   β”œβ”€β”€ routes/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ intentsRoutes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ modulesRoutes.ts
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ metricsRoutes.ts
β”‚   β”‚   β”‚   β”‚   └── executionsRoutes.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ utils/
β”‚   β”‚   β”‚   β”‚   └── json.ts                # bigint/Map -> JSON-safe, needed for every response
β”‚   β”‚   β”‚   └── index.ts                   # buildServer(); no execute/submit route β€” see CLAUDE.md
β”‚   β”‚   β”œβ”€β”€ package.json
β”‚   β”‚   └── tsconfig.json
β”‚   β”‚
β”‚   β”œβ”€β”€ frontend/                          # Protocol console (Next.js App Router + wagmi/viem)
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ app/                       # App Router, not pages/ β€” see CLAUDE.md
β”‚       β”‚   β”‚   β”œβ”€β”€ layout.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ page.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ providers.tsx          # "use client" boundary: WagmiProvider + QueryClientProvider
β”‚       β”‚   β”‚   └── globals.css            # design tokens: --bg/--surface/--border/--ink/--accent/status trio
β”‚       β”‚   β”œβ”€β”€ components/
β”‚       β”‚   β”‚   β”œβ”€β”€ layout/AppHeader.tsx   # always-visible network identity + wallet
β”‚       β”‚   β”‚   β”œβ”€β”€ wallet/ConnectWallet.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ execution/             # ExecutionConsole.tsx (primary surface), CandidateModuleRow.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ protocol/OverviewStats.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ metrics/RecentExecutions.tsx
β”‚       β”‚   β”‚   └── ui/                    # Badge, Panel, StatTile β€” shared primitives, few call sites each
β”‚       β”‚   β”œβ”€β”€ hooks/                     # useKernelClient, useIntents, useModules, usePrediction,
β”‚       β”‚   β”‚                              # useExecutionMetrics, useRecentExecutions
β”‚       β”‚   β”œβ”€β”€ services/
β”‚       β”‚   β”‚   └── kernelClient.ts        # pure wiring: wagmi's viem clients -> sdk's ExecutionKernelClient
β”‚       β”‚   └── lib/
β”‚       β”‚       └── wagmiConfig.ts         # localAnvil only, ssr: true β€” see CLAUDE.md
β”‚       β”œβ”€β”€ e2e/
β”‚       β”‚   └── full-flow.spec.ts          # Playwright: the full flow end to end, real browser β€” see CLAUDE.md
β”‚       β”œβ”€β”€ playwright.config.ts
β”‚       β”œβ”€β”€ wrangler.jsonc                 # Worker name exekpro-console, Route exekpro.com/*
β”‚       β”œβ”€β”€ open-next.config.ts
β”‚       β”œβ”€β”€ package.json
β”‚       └── tsconfig.json
β”‚
β”‚   └── landing/                            # exekpro.com/about β€” public marketing site (Next.js App Router)
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ app/
β”‚       β”‚   β”‚   β”œβ”€β”€ layout.tsx              # metadata: title/description/OG/canonical, basePath-aware ("/about")
β”‚       β”‚   β”‚   β”œβ”€β”€ page.tsx                # composes every section, in order
β”‚       β”‚   β”‚   β”œβ”€β”€ globals.css             # single deliberate dark theme, not dual-theme like the console
β”‚       β”‚   β”‚   β”œβ”€β”€ icon.png / apple-icon.png
β”‚       β”‚   β”‚   └── opengraph-image-asset/route.tsx # generated OG image (next/og ImageResponse), a plain
β”‚       β”‚   β”‚                                        # route rather than Next's file-convention β€” see CLAUDE.md
β”‚       β”‚   β”œβ”€β”€ components/
β”‚       β”‚   β”‚   β”œβ”€β”€ layout/                 # SiteHeader, SiteFooter, Wordmark
β”‚       β”‚   β”‚   β”œβ”€β”€ sections/               # Hero, Problem, HowItWorks, ExecutionQuoteSection,
β”‚       β”‚   β”‚   β”‚                           # ModularExecution, B2BDeployments, DeveloperExperience,
β”‚       β”‚   β”‚   β”‚                           # Validation, SecurityPrinciples, RoadmapStatus, FinalCTA
β”‚       β”‚   β”‚   └── ui/                     # Container, SectionHeading, Pill, FlowDiagram, StatTile, CTALink
β”‚       β”‚   └── lib/
β”‚       β”‚       β”œβ”€β”€ links.ts                # GITHUB_URL/CONSOLE_URL/DOCS_URL
β”‚       β”‚       └── basePath.ts             # shared "/about" constant β€” next.config.ts + next/image src both use it
β”‚       β”œβ”€β”€ wrangler.jsonc                  # Worker name exekpro-landing, Routes exekpro.com/about, exekpro.com/about/*
β”‚       β”œβ”€β”€ open-next.config.ts
β”‚       β”œβ”€β”€ package.json                    # deliberately no wagmi/viem/sdk deps β€” pure static marketing content
β”‚       └── tsconfig.json
β”‚
β”œβ”€β”€ .github/workflows/
β”‚   β”œβ”€β”€ ci.yml                              # forge test + npm run typecheck, every PR
β”‚   β”œβ”€β”€ deploy-console.yml                  # apps/frontend -> exekpro-console, on push to main
β”‚   └── deploy-landing.yml                  # apps/landing -> exekpro-landing, on push to main
β”‚
β”œβ”€β”€ scripts/
β”‚   └── deploy-local.sh                     # one-command local kernel deploy + demo intent/module registration
β”‚
β”œβ”€β”€ docs/                                   # placeholder β€” not yet populated
β”‚
β”œβ”€β”€ package.json                           # npm workspaces root
β”œβ”€β”€ tsconfig.base.json                     # shared strict TS config, extended per-package
β”œβ”€β”€ .gitignore
β”œβ”€β”€ LICENSE                                 # Apache-2.0
β”œβ”€β”€ CONTRIBUTING.md
└── README.md

Access Control

ModuleRegistry and IntentRegistry each currently hand-roll their own owner / onlyOwner. ProtocolRoles replaces that duplication with a single shared owner contract both registries defer to β€” one owner, one place to reason about protocol control, not independent per-registry admins. This is deliberately the simple model for now, not multi-role RBAC (distinct module-manager / intent-manager / protocol-admin roles) β€” revisit that split if and when different registries genuinely need independent operators.


Build Order

  1. Smart Contracts: Execution Modules + Registry + Engine
  2. Execution Node: intent processing + module-selection orchestration
  3. Indexer: execution metrics + performance feedback loop
  4. SDK: developer integration surface
  5. Frontend: intent-based interaction layer

Core Design Principles

  • Execution is modular, not monolithic
  • Competing modules are scored transparently (ScorePolicy) and the best one wins β€” no hidden routing
  • Trust boundaries are explicit: intent-type activation, module registration, and protocol ownership are each a single, auditable control point
  • Built toward production: prefer explicit, tested logic over cleverness; expand scope (settlement, multi-role access, graph pipelining) only when a concrete need appears, not speculatively
  • System performance is measured and observable by design
  • SDK is the primary integration surface for external adoption

Contributing

See CONTRIBUTING.md β€” local setup (links back to Getting Started above), what to run before opening a PR, and commit conventions.

License

Apache License 2.0.


Final Note

This protocol is an execution abstraction layer for Web3 applications: it lets a decentralized system express an intent once and have competing, independently deployed execution strategies fight for the right to fulfill it β€” with the trust boundary that governs which strategies are eligible kept small, explicit, and centrally owned.

About

Execution-selection protocol for Web3 intents with modular strategies and deterministic scoring.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages