|
| 1 | +# Evolution SDK Client Module |
| 2 | + |
| 3 | +## Abstract |
| 4 | + |
| 5 | +This document specifies the Evolution SDK client architecture and normative behaviors for composing provider and wallet capabilities in TypeScript applications. It defines client roles, available operations, upgrade semantics, transaction building constraints, and the error model. Examples illustrate key usage patterns; detailed feature matrices are provided in the Appendix. |
| 6 | + |
| 7 | +## Purpose and Scope |
| 8 | + |
| 9 | +This specification describes how clients are constructed and enhanced with providers and wallets, what operations are available for each client role, and how transaction building and submission behave. It does not define provider-specific protocols, CIP-30 details, or internal implementation; those are covered by code and provider-specific documents. Multi-provider behavior is specified at a high level here and in detail in the Provider Failover specification. For Effect-based vs Promise-based usage, see the [Effect-Promise Architecture Guide](./effect-promise-architecture.md). |
| 10 | + |
| 11 | +## Introduction |
| 12 | + |
| 13 | +The Evolution SDK offers a progressive client model: start with a minimal client and add a provider and/or wallet to unlock read, sign, and submit capabilities. The goal is clear separation of concerns and compile-time safety for what a given client can do. |
| 14 | + |
| 15 | +```mermaid |
| 16 | +graph TD |
| 17 | + A[MinimalClient] -->|Add Provider| B[ProviderOnlyClient] |
| 18 | + A -->|Add Signing Wallet| C[SigningWalletClient] |
| 19 | + A -->|Add ReadOnly Wallet| D[ReadOnlyWalletClient] |
| 20 | + A -->|Add API Wallet| E[ApiWalletClient] |
| 21 | + B -->|Add Signing Wallet| F[SigningClient] |
| 22 | + B -->|Add ReadOnly Wallet| G[ReadOnlyClient] |
| 23 | + C -->|Add Provider| F |
| 24 | + D -->|Add Provider| G |
| 25 | + E -->|Add Provider| F |
| 26 | + |
| 27 | + classDef minimal fill:#3b82f6,stroke:#1e3a8a,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px |
| 28 | + classDef provider fill:#8b5cf6,stroke:#4c1d95,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px |
| 29 | + classDef signingWallet fill:#f59e0b,stroke:#92400e,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px |
| 30 | + classDef readOnlyWallet fill:#10b981,stroke:#065f46,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px |
| 31 | + classDef apiWallet fill:#f97316,stroke:#9a3412,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px |
| 32 | + classDef readOnlyClient fill:#06b6d4,stroke:#0e7490,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px |
| 33 | + classDef signingClient fill:#ef4444,stroke:#991b1b,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px |
| 34 | + |
| 35 | + class A minimal |
| 36 | + class B provider |
| 37 | + class C signingWallet |
| 38 | + class D readOnlyWallet |
| 39 | + class E apiWallet |
| 40 | + class F signingClient |
| 41 | + class G readOnlyClient |
| 42 | +``` |
| 43 | + |
| 44 | +Summary: |
| 45 | +- MinimalClient: no read/sign/submit |
| 46 | +- ProviderOnlyClient: read and submit (where applicable), no signing |
| 47 | +- SigningWalletClient: sign only |
| 48 | +- ReadOnlyWalletClient: address/rewardAddress only |
| 49 | +- ApiWalletClient: CIP-30 sign and submit via wallet API |
| 50 | +- ReadOnlyClient: provider + read-only wallet; can query wallet data |
| 51 | +- SigningClient: provider + signing wallet or API wallet; full capability |
| 52 | + |
| 53 | +Matrices summarizing exact method availability appear in the Appendix. |
| 54 | + |
| 55 | +## Functional Specification (Normative) |
| 56 | + |
| 57 | +Requirements language: The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in all capitals. |
| 58 | + |
| 59 | +### 1. Client roles and conformance |
| 60 | + |
| 61 | +An Evolution SDK client instance conforms to exactly one role at a time: |
| 62 | +- MinimalClient |
| 63 | +- ProviderOnlyClient |
| 64 | +- SigningWalletClient |
| 65 | +- ReadOnlyWalletClient |
| 66 | +- ApiWalletClient |
| 67 | +- ReadOnlyClient |
| 68 | +- SigningClient |
| 69 | + |
| 70 | +For each role, the following MUST hold: |
| 71 | +- MinimalClient MUST NOT expose provider or wallet operations; it MAY be upgraded. |
| 72 | +- ProviderOnlyClient MUST expose provider operations and MAY submit transactions if the provider supports submission; it MUST NOT expose signing. |
| 73 | +- SigningWalletClient MUST expose signing and message signing; it MUST NOT expose submission or provider queries. |
| 74 | +- ReadOnlyWalletClient MUST expose address() and rewardAddress(); it MUST NOT expose signing, provider queries, or submission. |
| 75 | +- ApiWalletClient MUST expose signing and MAY expose submission via the wallet API; it MUST NOT expose provider queries unless upgraded with a provider. |
| 76 | +- ReadOnlyClient MUST expose provider queries scoped to the configured address and MUST NOT expose signing. |
| 77 | +- SigningClient MUST expose provider queries, transaction building, signing, and submission. |
| 78 | + |
| 79 | +### 2. Network and provider operations |
| 80 | + |
| 81 | +- A client configured with a provider (ProviderOnlyClient, ReadOnlyClient, SigningClient) MUST provide `networkId` and provider query methods (e.g., `getProtocolParameters`, `getUtxos`, `awaitTx`, `evaluateTx`) as listed in the Appendix. |
| 82 | +- `submitTx` MUST be available on clients with a provider or API wallet capable of submission (ProviderOnlyClient, ReadOnlyClient, SigningClient, ApiWalletClient). |
| 83 | +- Provider implementations and their supported operations are out of scope here; see provider-specific docs. A multi-provider MUST follow the strategy defined in the [Provider Failover specification](./provider-failover.md). |
| 84 | + |
| 85 | +### 3. Wallet operations |
| 86 | + |
| 87 | +- A client configured with a wallet MUST provide `address()` and `rewardAddress()` where the wallet type supports them. |
| 88 | +- SigningWalletClient and SigningClient MUST provide `signTx(tx)` and `signMessage(address, payload)`. |
| 89 | +- ReadOnlyWalletClient and ReadOnlyClient MUST NOT provide signing methods. |
| 90 | +- ApiWalletClient MUST provide signTx and SHOULD provide submitTx if the wallet API supports submission. |
| 91 | + |
| 92 | +### 4. Transaction building |
| 93 | + |
| 94 | +- `newTx()` MUST be exposed only on clients that have a provider (ReadOnlyClient, SigningClient). |
| 95 | +- Building a transaction MUST require provider protocol parameters. |
| 96 | +- `build()`/`complete()` on ReadOnlyClient MUST produce an unsigned `Transaction`. |
| 97 | +- `build()`/`complete()` on SigningClient MUST produce a `SignBuilder` (or equivalent) that can be signed and submitted. |
| 98 | +- ApiWalletClient MUST be upgraded to SigningClient (by attaching a provider) before it can build transactions. |
| 99 | + |
| 100 | +### 5. Attachment and upgrade semantics |
| 101 | + |
| 102 | +- `createClient()` without arguments MUST return a MinimalClient. |
| 103 | +- `attachProvider(provider)` and `attachWallet(wallet)` MUST return new client instances (i.e., the API is immutable) with upgraded roles as per the Introduction diagram. |
| 104 | +- `createClient({ network, provider })` MUST produce a ProviderOnlyClient. |
| 105 | +- `createClient({ network, wallet })` MUST produce SigningWalletClient, ReadOnlyWalletClient, or ApiWalletClient depending on wallet type. |
| 106 | +- `createClient({ network, provider, wallet })` MUST produce ReadOnlyClient or SigningClient depending on wallet type. |
| 107 | + |
| 108 | +### 6. Error model and effect semantics |
| 109 | + |
| 110 | +- Methods that interact with external systems MUST reject/raise with typed errors: ProviderError for provider failures, WalletError for wallet failures, MultiProviderError for strategy/exhaustion failures, and TransactionBuilderError for builder validation issues. |
| 111 | +- The Effect API MUST preserve the same error categories as typed causes; callers MAY use retries, timeouts, and fallbacks. The Promise API MUST be semantically equivalent to running the corresponding Effect program to completion. |
| 112 | +- Multi-provider failover MUST adhere to the [Provider Failover specification](./provider-failover.md). |
| 113 | + |
| 114 | +### 7. API equivalence (Effect vs Promise) |
| 115 | + |
| 116 | +For every Promise-returning method, an equivalent Effect program MUST exist under the `client.Effect` namespace with identical semantics regarding success values and error categories. |
| 117 | + |
| 118 | +### 8. Examples (Informative) |
| 119 | + |
| 120 | +Simple creation and upgrade: |
| 121 | +```typescript |
| 122 | +const client = createClient() |
| 123 | +const providerClient = client.attachProvider({ type: "blockfrost", apiKey: "your-key" }) |
| 124 | +const signingClient = providerClient.attachWallet({ type: "seed", mnemonic: "your mnemonic" }) |
| 125 | +``` |
| 126 | + |
| 127 | +Direct creation: |
| 128 | +```typescript |
| 129 | +const client = createClient({ |
| 130 | + network: "mainnet", |
| 131 | + provider: { type: "blockfrost", apiKey: "your-key" }, |
| 132 | + wallet: { type: "seed", mnemonic: "your mnemonic" } |
| 133 | +}) |
| 134 | +``` |
| 135 | + |
| 136 | +Browser wallet (CIP-30) with upgrade: |
| 137 | +```typescript |
| 138 | +const apiClient = createClient({ network: "mainnet", wallet: { type: "api", api: window.cardano.nami } }) |
| 139 | +const fullClient = apiClient.attachProvider({ type: "blockfrost", apiKey: "your-key" }) |
| 140 | +``` |
| 141 | + |
| 142 | +Signing-only wallet (no submit without provider): |
| 143 | +```typescript |
| 144 | +const signingWallet = createClient({ network: "mainnet", wallet: { type: "seed", mnemonic: "your mnemonic" } }) |
| 145 | +// await signingWallet.submitTx(...) // not available |
| 146 | +``` |
| 147 | + |
| 148 | +Effect usage (retries, timeouts): |
| 149 | +```typescript |
| 150 | +const program = client.Effect.signTx(tx).pipe(Effect.retry({ times: 3 }), Effect.timeout(30000)) |
| 151 | +const signed = await Effect.runPromise(program) |
| 152 | +``` |
| 153 | + |
| 154 | +## Appendix (Informative) |
| 155 | + |
| 156 | +### A. Core methods by role |
| 157 | + |
| 158 | +| Method/Capability | MinimalClient | ProviderOnlyClient | SigningWalletClient | ReadOnlyWalletClient | ApiWalletClient | ReadOnlyClient | SigningClient | |
| 159 | +|-------------------|---------------|--------------------|---------------------|----------------------|-----------------|----------------|---------------| |
| 160 | +| **Network Access** | |
| 161 | +| `networkId` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 162 | +| **Provider Operations** | |
| 163 | +| `getProtocolParameters()` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 164 | +| `getUtxos(address)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 165 | +| `getUtxosWithUnit(address, unit)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 166 | +| `getUtxoByUnit(unit)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 167 | +| `getUtxosByOutRef(outRefs)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 168 | +| `getDelegation(rewardAddress)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 169 | +| `getDatum(datumHash)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 170 | +| `awaitTx(txHash)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 171 | +| `evaluateTx(tx)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 172 | +| `submitTx(tx)` | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | |
| 173 | +| **Wallet Operations** | |
| 174 | +| `address()` | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 175 | +| `rewardAddress()` | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 176 | +| `getWalletUtxos()` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 177 | +| `getWalletDelegation()` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 178 | +| `signTx(tx)` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ | |
| 179 | +| `signMessage(address, payload)` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ | |
| 180 | +| **Transaction Building** | |
| 181 | +| `newTx()` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | |
| 182 | +| **Client Composition** | |
| 183 | +| `attachProvider()` | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | |
| 184 | +| `attachWallet()` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | |
| 185 | +| `attach(provider, wallet)` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | |
| 186 | + |
| 187 | +### B. Transaction builder capabilities |
| 188 | + |
| 189 | +| Builder Method | ReadOnlyClient | SigningClient | Notes | |
| 190 | +|----------------|----------------|---------------|--------| |
| 191 | +| `build()` | ✅ → `Transaction` | ✅ → `SignBuilder` | ReadOnlyClient returns unsigned transaction; SigningClient returns a builder with signing capabilities | |
| 192 | + |
| 193 | +Note: Transaction building requires protocol parameters from a provider. ApiWalletClient MUST be upgraded before building. |
| 194 | + |
| 195 | +### C. Provider support (categories) |
| 196 | + |
| 197 | +| Category | Description | Supported Operations | |
| 198 | +|----------|-------------|---------------------| |
| 199 | +| REST API provider | External REST service | All provider operations | |
| 200 | +| Node-backed stack | Local/remote node stack (e.g., indexer + node) | All provider operations | |
| 201 | +| Cloud API provider | Managed blockchain API | All provider operations | |
| 202 | +| Alternative REST provider | Another REST-based service | All provider operations | |
| 203 | +| Multi-provider (strategy) | Failover/hedged strategy | All provider operations with redundancy (see [Provider Failover Specification](./provider-failover.md)) | |
| 204 | + |
| 205 | +### D. Wallet support |
| 206 | + |
| 207 | +| Wallet Type | Client Types | Description | Capabilities | |
| 208 | +|-------------|-------------|-------------|--------------| |
| 209 | +| **Seed Wallet** | SigningWalletClient, SigningClient | HD wallet from mnemonic | Sign only (no submit without provider) | |
| 210 | +| **Private Key** | SigningWalletClient, SigningClient | Single key wallet | Sign only (no submit without provider) | |
| 211 | +| **Read-Only** | ReadOnlyWalletClient, ReadOnlyClient | Address monitoring | Query only, no signing | |
| 212 | +| **API Wallet (CIP-30)** | ApiWalletClient, SigningClient | Browser extension | Sign + submit via extension | |
0 commit comments