Architecture
Abstract
Evolution SDK implements a capability-based architecture where transaction building, network access, and key management are separated through type constraints and deferred execution. The system prevents invalid states at compile time while enabling composition of complex transaction logic.
Transaction Flow
How transactions flow through build, sign, and submit phases with fresh state
Coin Selection
UTxO selection algorithms for covering transaction requirements
Redeemer Indexing
Deferred redeemer construction for efficient validator input lookup
Script Evaluation
Plutus script execution and ExUnits calculation for transaction validation
Unfrack Optimization
UTxO structure optimization through strategic bundling and subdivision
Deferred Execution
Why builders store programs instead of executing immediately
Devnet
Local Cardano network orchestration for development and testing
Provider Layer
Why and how providers abstract blockchain data access
Wallet Layer
Why wallets are separated by capability through the type system
Purpose and Scope
This section describes the fundamental architectural principles that drive Evolution SDK's design: deferred execution, separation of concerns, and type-safe capability composition. It covers the conceptual model and decision flows that guide implementation.
Introduction
The Problem
Cardano transaction building faces three core challenges:
- State Management: Immediate execution creates shared mutable state, making builders unsafe to reuse
- Type Safety: Runtime capability checking allows invalid operations to compile successfully
- Composition: Tightly coupled components prevent independent testing and flexible provider/wallet swapping
Traditional approaches force developers to choose between safety and flexibility.
Design Philosophy
Evolution SDK's architecture provides three guarantees:
- Deferred Execution - Store operations as data, execute on demand with fresh state
- Separation of Concerns - Provider (network) ≠ Wallet (keys) ≠ Client (logic)
- Type Safety Through Constraints - Compiler prevents invalid operations
Visual Overview
Architectural Principles
1. Deferred Execution
Transaction builders in Evolution SDK don't execute immediately. Instead, they store a description of operations to perform. When you call .build(), the SDK creates fresh state and executes the program, ensuring each execution is isolated from previous runs.
This design emerged from a common problem: shared mutable state makes builders dangerous to reuse. By deferring execution until explicitly requested, the same builder can safely generate multiple transactions without interference.
The benefit extends beyond safety. Programs-as-values can be inspected before running, tested without blockchain access, and composed together like building blocks. The transaction logic becomes pure data until you decide to execute it.
See Deferred Execution for execution model and state management.
2. Separation of Concerns
The SDK separates three responsibilities that transaction systems often conflate:
Provider layer handles blockchain access—querying UTxOs, fetching protocol parameters, evaluating scripts, and submitting transactions. It abstracts the underlying service (Blockfrost, Kupmios, Maestro, Koios) behind a common interface.
Wallet layer manages keys and signing. Whether using a seed phrase (HD wallet), single private key, hardware wallet, or browser extension (CIP-30), the wallet interface remains consistent. Read-only wallets provide addresses without signing capability.
Client layer coordinates the provider and wallet, building transactions using their capabilities. The client enforces constraints through types—you cannot sign without a signing wallet or query without a provider.
See Provider Layer and Wallet Layer for interface contracts and selection criteria.
3. Type Safety Through Constraints
The type system prevents impossible states at compile time. A client without a provider cannot call newTx(). A client without a signing wallet cannot call signTx(). A read-only wallet produces unsigned transactions. These aren't runtime checks—they're enforced by TypeScript's type checker.
When you add a provider, the client gains read and submission operations. When you add an address, the client gains wallet identity. When you add a signer, signing operations become available. The type system documents what a client can do and prevents you from calling operations it cannot support.
Integration Points
The architecture maintains strict boundaries between layers. The client never directly manages network or keys—it delegates to provider and wallet. This delegation enables swapping providers or wallets without touching transaction logic.
[1] ClientAssembly: Chain-scoped entry point. No read, identity, or signing capability yet.
[2] ReadClient: Provider-backed read and submission capability, but no wallet identity or signing.
[3] AddressClient: Wallet identity only. Exposes address and reward address, but no provider queries or signing.
[4] OfflineSignerClient: Signing capability without provider-backed reads or transaction building.
[5] ReadOnlyClient: Provider-backed read capability plus wallet identity. Can build unsigned transactions.
[6] SigningClient: Full client. Can read, build, sign, and submit.
Architecture Pages
- Transaction Flow - Build → sign → submit phase transitions
- Coin Selection - UTxO selection algorithms
- Redeemer Indexing - Deferred redeemer construction for Plutus script index optimization
- Script Evaluation - Plutus validator execution and ExUnits calculation
- Unfrack Optimization - UTxO structure optimization
- Deferred Execution - Immutable builder pattern, program-as-value semantics
- Devnet - Local Cardano network orchestration
- Provider Layer - Blockchain access abstraction
- Wallet Layer - Capability-based type system
Related Topics
- Getting Started - Practical usage examples
- Client Basics - Client API and capabilities
- Transactions - Transaction building guide