Provider Layer
Abstract
Cardano has multiple blockchain data providers (Blockfrost, Kupmios, Maestro, Koios) with different APIs, authentication methods, and data formats. The provider layer creates a unified interface over these differences—application code works identically regardless of provider choice. The abstraction is deliberately thin: it provides exactly what the SDK needs (UTxO queries, protocol parameters, transaction submission, script evaluation) without exposing provider-specific features.
Design Philosophy
Without abstraction, application code couples directly to provider APIs. Switching from Blockfrost to Kupmios requires rewriting every blockchain query—different authentication, different response formats, different error handling. Testing requires real blockchain access or complex mocking of provider-specific APIs.
The architecture establishes a thin interface capturing what transaction building requires: query UTxOs, fetch protocol parameters, submit transactions, evaluate scripts. Each provider implementation translates between this interface and their native API. Application code depends on the interface, not the implementation. Provider selection becomes a configuration choice, not a code change.
Provider Interface Contract
All providers implement the same core operations required for transaction building:
getUtxos(address)- Query unspent outputs at addressgetProtocolParameters()- Fetch current protocol parameterssubmitTx(tx)- Submit signed transactionevaluateTx(tx, utxos)- Calculate script execution costsgetDatum(hash)- Retrieve datum by hashawaitTx(hash)- Wait for transaction confirmation
Each provider translates interface calls to their native API. Application code depends only on interface, never on specific implementation.
Type-Driven Configuration
Provider configuration uses discriminated unions to enforce correctness at compile time.
Provider Config: Method-specific config object. Each .withX() provider method determines which configuration properties are required.
Provider Factory: The selected .withX() method constructs the appropriate implementation. Invalid configurations produce compile-time errors.
Provider Instance: Implements unified interface. Type system guarantees all required configuration is present.
TypeScript enforces: .withBlockfrost() requires baseUrl and projectId. .withKupmios() requires kupoUrl and ogmiosUrl. Mismatches are compilation errors, not runtime failures.
Integration Points
Transaction Builder: During build() execution, the builder queries the provider for:
- Protocol parameters (fee calculation, transaction size limits)
- Available UTxOs at wallet addresses (coin selection)
- Script evaluation (calculating execution units for Plutus scripts)
Client Configuration: Provider capability is added during assembly via client(chain).withBlockfrost() or the other .withX() provider methods. The client stores the provider reference and passes it to transaction builders.
Error Handling: Provider methods return Effect<Result, ProviderError>. All provider-specific errors (HTTP failures, WebSocket disconnections, rate limits, authentication) normalize to ProviderError with consistent structure. Application code handles one error type, not provider-specific exceptions.
Effect-TS Integration: Providers expose both Promise-based API (auto-wrapped) and Effect-based API (provider.Effect.*). Transaction builder uses Effect API for compositional error handling and resource management.
Related Topics
- Architecture Overview - How providers attach to clients
- Transaction Flow - Provider queries during build phase
- Deferred Execution - Provider integration with program execution