Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
212 changes: 212 additions & 0 deletions .specs/client-module-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# Evolution SDK Client Module

## Abstract

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.

## Purpose and Scope

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).

## Introduction

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.

```mermaid
graph TD
A[MinimalClient] -->|Add Provider| B[ProviderOnlyClient]
A -->|Add Signing Wallet| C[SigningWalletClient]
A -->|Add ReadOnly Wallet| D[ReadOnlyWalletClient]
A -->|Add API Wallet| E[ApiWalletClient]
B -->|Add Signing Wallet| F[SigningClient]
B -->|Add ReadOnly Wallet| G[ReadOnlyClient]
C -->|Add Provider| F
D -->|Add Provider| G
E -->|Add Provider| F

classDef minimal fill:#3b82f6,stroke:#1e3a8a,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
classDef provider fill:#8b5cf6,stroke:#4c1d95,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
classDef signingWallet fill:#f59e0b,stroke:#92400e,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
classDef readOnlyWallet fill:#10b981,stroke:#065f46,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
classDef apiWallet fill:#f97316,stroke:#9a3412,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
classDef readOnlyClient fill:#06b6d4,stroke:#0e7490,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
classDef signingClient fill:#ef4444,stroke:#991b1b,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px

class A minimal
class B provider
class C signingWallet
class D readOnlyWallet
class E apiWallet
class F signingClient
class G readOnlyClient
```

Summary:
- MinimalClient: no read/sign/submit
- ProviderOnlyClient: read and submit (where applicable), no signing
- SigningWalletClient: sign only
- ReadOnlyWalletClient: address/rewardAddress only
- ApiWalletClient: CIP-30 sign and submit via wallet API
- ReadOnlyClient: provider + read-only wallet; can query wallet data
- SigningClient: provider + signing wallet or API wallet; full capability

Matrices summarizing exact method availability appear in the Appendix.

## Functional Specification (Normative)

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.

### 1. Client roles and conformance

An Evolution SDK client instance conforms to exactly one role at a time:
- MinimalClient
- ProviderOnlyClient
- SigningWalletClient
- ReadOnlyWalletClient
- ApiWalletClient
- ReadOnlyClient
- SigningClient

For each role, the following MUST hold:
- MinimalClient MUST NOT expose provider or wallet operations; it MAY be upgraded.
- ProviderOnlyClient MUST expose provider operations and MAY submit transactions if the provider supports submission; it MUST NOT expose signing.
- SigningWalletClient MUST expose signing and message signing; it MUST NOT expose submission or provider queries.
- ReadOnlyWalletClient MUST expose address() and rewardAddress(); it MUST NOT expose signing, provider queries, or submission.
- ApiWalletClient MUST expose signing and MAY expose submission via the wallet API; it MUST NOT expose provider queries unless upgraded with a provider.
- ReadOnlyClient MUST expose provider queries scoped to the configured address and MUST NOT expose signing.
- SigningClient MUST expose provider queries, transaction building, signing, and submission.

### 2. Network and provider operations

- 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.
- `submitTx` MUST be available on clients with a provider or API wallet capable of submission (ProviderOnlyClient, ReadOnlyClient, SigningClient, ApiWalletClient).
- 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).

### 3. Wallet operations

- A client configured with a wallet MUST provide `address()` and `rewardAddress()` where the wallet type supports them.
- SigningWalletClient and SigningClient MUST provide `signTx(tx)` and `signMessage(address, payload)`.
- ReadOnlyWalletClient and ReadOnlyClient MUST NOT provide signing methods.
- ApiWalletClient MUST provide signTx and SHOULD provide submitTx if the wallet API supports submission.

### 4. Transaction building

- `newTx()` MUST be exposed only on clients that have a provider (ReadOnlyClient, SigningClient).
- Building a transaction MUST require provider protocol parameters.
- `build()`/`complete()` on ReadOnlyClient MUST produce an unsigned `Transaction`.
- `build()`/`complete()` on SigningClient MUST produce a `SignBuilder` (or equivalent) that can be signed and submitted.
- ApiWalletClient MUST be upgraded to SigningClient (by attaching a provider) before it can build transactions.

### 5. Attachment and upgrade semantics

- `createClient()` without arguments MUST return a MinimalClient.
- `attachProvider(provider)` and `attachWallet(wallet)` MUST return new client instances (i.e., the API is immutable) with upgraded roles as per the Introduction diagram.
- `createClient({ network, provider })` MUST produce a ProviderOnlyClient.
- `createClient({ network, wallet })` MUST produce SigningWalletClient, ReadOnlyWalletClient, or ApiWalletClient depending on wallet type.
- `createClient({ network, provider, wallet })` MUST produce ReadOnlyClient or SigningClient depending on wallet type.

### 6. Error model and effect semantics

- 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.
- 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.
- Multi-provider failover MUST adhere to the [Provider Failover specification](./provider-failover.md).

### 7. API equivalence (Effect vs Promise)

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.

### 8. Examples (Informative)

Simple creation and upgrade:
```typescript
const client = createClient()
const providerClient = client.attachProvider({ type: "blockfrost", apiKey: "your-key" })
const signingClient = providerClient.attachWallet({ type: "seed", mnemonic: "your mnemonic" })
```

Direct creation:
```typescript
const client = createClient({
network: "mainnet",
provider: { type: "blockfrost", apiKey: "your-key" },
wallet: { type: "seed", mnemonic: "your mnemonic" }
})
```

Browser wallet (CIP-30) with upgrade:
```typescript
const apiClient = createClient({ network: "mainnet", wallet: { type: "api", api: window.cardano.nami } })
const fullClient = apiClient.attachProvider({ type: "blockfrost", apiKey: "your-key" })
```

Signing-only wallet (no submit without provider):
```typescript
const signingWallet = createClient({ network: "mainnet", wallet: { type: "seed", mnemonic: "your mnemonic" } })
// await signingWallet.submitTx(...) // not available
```

Effect usage (retries, timeouts):
```typescript
const program = client.Effect.signTx(tx).pipe(Effect.retry({ times: 3 }), Effect.timeout(30000))
const signed = await Effect.runPromise(program)
```

## Appendix (Informative)

### A. Core methods by role

| Method/Capability | MinimalClient | ProviderOnlyClient | SigningWalletClient | ReadOnlyWalletClient | ApiWalletClient | ReadOnlyClient | SigningClient |
|-------------------|---------------|--------------------|---------------------|----------------------|-----------------|----------------|---------------|
| **Network Access** |
| `networkId` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Provider Operations** |
| `getProtocolParameters()` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `getUtxos(address)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `getUtxosWithUnit(address, unit)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `getUtxoByUnit(unit)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `getUtxosByOutRef(outRefs)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `getDelegation(rewardAddress)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `getDatum(datumHash)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `awaitTx(txHash)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `evaluateTx(tx)` | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `submitTx(tx)` | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ |
| **Wallet Operations** |
| `address()` | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `rewardAddress()` | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `getWalletUtxos()` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `getWalletDelegation()` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| `signTx(tx)` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ |
| `signMessage(address, payload)` | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ✅ |
| **Transaction Building** |
| `newTx()` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| **Client Composition** |
| `attachProvider()` | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
| `attachWallet()` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `attach(provider, wallet)` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |

### B. Transaction builder capabilities

| Builder Method | ReadOnlyClient | SigningClient | Notes |
|----------------|----------------|---------------|--------|
| `build()` | ✅ → `Transaction` | ✅ → `SignBuilder` | ReadOnlyClient returns unsigned transaction; SigningClient returns a builder with signing capabilities |

Note: Transaction building requires protocol parameters from a provider. ApiWalletClient MUST be upgraded before building.

### C. Provider support (categories)

| Category | Description | Supported Operations |
|----------|-------------|---------------------|
| REST API provider | External REST service | All provider operations |
| Node-backed stack | Local/remote node stack (e.g., indexer + node) | All provider operations |
| Cloud API provider | Managed blockchain API | All provider operations |
| Alternative REST provider | Another REST-based service | All provider operations |
| Multi-provider (strategy) | Failover/hedged strategy | All provider operations with redundancy (see [Provider Failover Specification](./provider-failover.md)) |

### D. Wallet support

| Wallet Type | Client Types | Description | Capabilities |
|-------------|-------------|-------------|--------------|
| **Seed Wallet** | SigningWalletClient, SigningClient | HD wallet from mnemonic | Sign only (no submit without provider) |
| **Private Key** | SigningWalletClient, SigningClient | Single key wallet | Sign only (no submit without provider) |
| **Read-Only** | ReadOnlyWalletClient, ReadOnlyClient | Address monitoring | Query only, no signing |
| **API Wallet (CIP-30)** | ApiWalletClient, SigningClient | Browser extension | Sign + submit via extension |
177 changes: 177 additions & 0 deletions .specs/complete-tx-builder-workflow-diagram.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# CompleteTxBuilder Workflow Diagram

```mermaid
graph TD
A["Start: complete() Function"] --> B["Phase 1: Setup & Validation"]
B --> B1["Fetch Wallet UTxOs"]
B --> B2["Derive Change Address"]
B --> B3["Validate Options"]

B1 --> C["Phase 2: Initial Coin Selection"]
B2 --> C
B3 --> C

C --> C1["Calculate Asset Delta<br/>outputs + fee - collected - minted"]
C1 --> C2["Filter Required Assets<br/>positive amounts only"]
C2 --> C3["Recursive UTxO Selection<br/>largest-first strategy"]

C3 --> D{"Has Plutus Scripts?"}

D -->|No| F["Phase 5: Skip Script Evaluation"]
D -->|Yes| E["Phase 3: Script Evaluation"]

E --> E1["Build Evaluation Transaction<br/>CML.build_for_evaluation()"]
E1 --> E2{"Local UPLC?"}

E2 -->|Yes| E3["WASM UPLC Evaluation<br/>evalTransaction()"]
E2 -->|No| E4["Provider Evaluation<br/>evalTransactionProvider()"]

E3 --> E5["Apply UPLC Results<br/>applyUPLCEval()"]
E4 --> E6["Apply Provider Results<br/>applyUPLCEvalProvider()"]

E5 --> G["Phase 4: Refined Coin Selection"]
E6 --> G

G --> G1["Recalculate Fee with Script Costs"]
G1 --> G2["Check if Additional UTxOs Needed"]
G2 --> G3{"Need More UTxOs?"}

G3 -->|Yes| G4["Select Additional UTxOs"]
G3 -->|No| H["Phase 5: Collateral Management"]

G4 --> G5{"Script Budget Changed?"}
G5 -->|Yes| E1
G5 -->|No| H

H --> H1["Calculate Collateral Amount<br/>150% of estimated fee"]
H1 --> H2["Find Collateral UTxOs<br/>ADA-only, max 3"]
H2 --> H3["Apply Collateral to Transaction"]

H3 --> I["Phase 6: Final Assembly"]

I --> I1["Complete Partial Programs<br/>Build redeemers with indices"]
I1 --> I2["Final CML Transaction Build"]
I2 --> I3["Apply Final ExUnits"]

I3 --> J["Return Built Transaction"]

F --> I

style A fill:#4a90e2,color:#ffffff,stroke:#2171b5,stroke-width:3px
style B fill:#9b59b6,color:#ffffff,stroke:#8e44ad,stroke-width:3px
style C fill:#27ae60,color:#ffffff,stroke:#229954,stroke-width:3px
style D fill:#f39c12,color:#ffffff,stroke:#e67e22,stroke-width:3px
style E fill:#e74c3c,color:#ffffff,stroke:#c0392b,stroke-width:3px
style F fill:#95a5a6,color:#ffffff,stroke:#7f8c8d,stroke-width:3px
style G fill:#1abc9c,color:#ffffff,stroke:#16a085,stroke-width:3px
style H fill:#f1c40f,color:#2c3e50,stroke:#f39c12,stroke-width:3px
style I fill:#34495e,color:#ffffff,stroke:#2c3e50,stroke-width:3px
style J fill:#2ecc71,color:#ffffff,stroke:#27ae60,stroke-width:4px

classDef phaseBox fill:#ecf0f1,stroke:#34495e,stroke-width:3px,color:#2c3e50
classDef decision fill:#fff3cd,stroke:#856404,stroke-width:3px,color:#856404
classDef subprocess fill:#e8f4f8,stroke:#2980b9,stroke-width:2px,color:#2c3e50
classDef success fill:#d4edda,stroke:#155724,stroke-width:3px,color:#155724

class B,C,E,G,H,I phaseBox
class D,E2,G3,G5 decision
class B1,B2,B3,C1,C2,C3,E1,E3,E4,E5,E6,G1,G2,G4,H1,H2,H3,I1,I2,I3 subprocess
class J success
```

## Detailed Flow Explanation

### Phase Transitions and Decision Points

1. **Setup → Initial Selection**: Always proceeds after validation
2. **Initial Selection → Script Check**: Determines if script evaluation needed
3. **Script Evaluation → Refined Selection**: Only for Plutus script transactions
4. **Refined Selection Loop**: Continues until stable UTxO selection achieved
5. **Collateral Management**: Only applies to script transactions
6. **Final Assembly**: Always completes the transaction building

### Critical Decision Points

#### Script Detection (`Has Plutus Scripts?`)
```typescript
// Determines evaluation path
if (hasPlutusScriptExecutions) {
// Proceed to script evaluation
} else {
// Skip to collateral/final assembly
}
```

#### UPLC vs Provider Evaluation (`Local UPLC?`)
```typescript
if (localUPLCEval !== false) {
// Use WASM UPLC evaluation
applyUPLCEval(uplcResults, txBuilder)
} else {
// Use external provider evaluation
applyUPLCEvalProvider(providerResults, txBuilder)
}
```

#### UTxO Selection Stability (`Need More UTxOs?`)
```typescript
// Check if script costs require additional funds
if (newEstimatedFee > currentCapacity) {
// Select more UTxOs and potentially re-evaluate scripts
return selectAdditionalUTxOs()
}
```

#### Script Budget Changes (`Script Budget Changed?`)
```typescript
// If new inputs change script execution context
if (inputSetChanged && hasSignificantBudgetChange) {
// Re-evaluate scripts with new input context
return reEvaluateScripts()
}
```

### Error Paths (Not Shown in Diagram)

Each phase can fail with specific error types:
- **Phase 1**: Wallet access errors, configuration validation errors
- **Phase 2**: Insufficient funds errors, UTxO availability errors
- **Phase 3**: Script evaluation errors, UPLC compilation errors
- **Phase 4**: Fee calculation errors, UTxO selection errors
- **Phase 5**: Collateral selection errors, protocol limit errors
- **Phase 6**: Redeemer building errors, transaction assembly errors

### Performance Considerations

#### Iterative Loops
- **Coin Selection Loop**: May iterate multiple times for complex asset requirements
- **Script Evaluation Loop**: May re-evaluate if input set changes significantly
- **Minimum ADA Loop**: Continues until change outputs meet minimum requirements

#### Expensive Operations
- **Script Evaluation**: Most expensive operation, especially for complex scripts
- **UTxO Sorting**: O(n log n) for large UTxO sets
- **Recursive Selection**: May examine many UTxO combinations

### State Dependencies

```mermaid
graph LR
A[Wallet UTxOs] --> B[Available Inputs]
B --> C[Selected UTxOs]
C --> D[Draft Transaction]
D --> E[Script Evaluation]
E --> F[Final Transaction]

G[Protocol Parameters] --> B
G --> E
G --> F

H[Transaction Outputs] --> C
H --> D

I[Minted Assets] --> C
I --> D
```

This workflow represents one of the most complex transaction building systems in the Cardano ecosystem, with sophisticated handling of script evaluation, UTxO management, and fee calculation.
Loading