Skip to content

Commit 1ca907c

Browse files
committed
feat: add specs
1 parent 6724a45 commit 1ca907c

File tree

5 files changed

+1316
-291
lines changed

5 files changed

+1316
-291
lines changed

.specs/client-module-workflow.md

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
# Evolution SDK Client Module
2+
3+
A specification for the client architecture and behavior.
4+
5+
## Quick Overview
6+
7+
The Evolution SDK provides different types of clients that you can progressively enhance:
8+
9+
```mermaid
10+
graph TD
11+
A[MinimalClient] -->|Add Provider| B[ProviderOnlyClient]
12+
A -->|Add Signing Wallet| C[SigningWalletClient]
13+
A -->|Add ReadOnly Wallet| D[ReadOnlyWalletClient]
14+
A -->|Add API Wallet| E[ApiWalletClient]
15+
B -->|Add Signing Wallet| F[SigningClient]
16+
B -->|Add ReadOnly Wallet| G[ReadOnlyClient]
17+
C -->|Add Provider| F
18+
D -->|Add Provider| G
19+
E -->|Add Provider| F
20+
21+
classDef minimal fill:#3b82f6,stroke:#1e3a8a,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
22+
classDef provider fill:#8b5cf6,stroke:#4c1d95,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
23+
classDef signingWallet fill:#f59e0b,stroke:#92400e,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
24+
classDef readOnlyWallet fill:#10b981,stroke:#065f46,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
25+
classDef apiWallet fill:#f97316,stroke:#9a3412,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
26+
classDef readOnlyClient fill:#06b6d4,stroke:#0e7490,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
27+
classDef signingClient fill:#ef4444,stroke:#991b1b,stroke-width:3px,color:#ffffff,font-weight:bold,font-size:14px
28+
29+
class A minimal
30+
class B provider
31+
class C signingWallet
32+
class D readOnlyWallet
33+
class E apiWallet
34+
class F signingClient
35+
class G readOnlyClient
36+
```
37+
38+
## Client Types
39+
40+
| Client | Can Query Blockchain | Can Sign | Can Submit | Use Case |
41+
|--------|---------------------|----------|------------|----------|
42+
| **MinimalClient** |||| Starting point |
43+
| **ProviderOnlyClient** |||| Read blockchain data |
44+
| **SigningWalletClient** |||| Sign-only (seed/private key) |
45+
| **ReadOnlyWalletClient** |||| Wallet-only (address monitoring) |
46+
| **ApiWalletClient** |||| Browser wallets (CIP-30) |
47+
| **ReadOnlyClient** |||| Monitor addresses |
48+
| **SigningClient** |||| Full functionality |
49+
50+
> **Type Safety**: Separate interfaces ensure compile-time guarantees about submission capabilities.
51+
52+
## Detailed Capabilities Matrix
53+
54+
### Core Methods Available
55+
56+
| Method/Capability | MinimalClient | ProviderOnlyClient | SigningWalletClient | ReadOnlyWalletClient | ApiWalletClient | ReadOnlyClient | SigningClient |
57+
|-------------------|---------------|--------------------|---------------------|----------------------|-----------------|----------------|---------------|
58+
| **Network Access** |
59+
| `networkId` ||||||||
60+
| **Provider Operations** |
61+
| `getProtocolParameters()` ||||||||
62+
| `getUtxos(address)` ||||||||
63+
| `getUtxosWithUnit(address, unit)` ||||||||
64+
| `getUtxoByUnit(unit)` ||||||||
65+
| `getUtxosByOutRef(outRefs)` ||||||||
66+
| `getDelegation(rewardAddress)` ||||||||
67+
| `getDatum(datumHash)` ||||||||
68+
| `awaitTx(txHash)` ||||||||
69+
| `evaluateTx(tx)` ||||||||
70+
| `submitTx(tx)` ||||||||
71+
| **Wallet Operations** |
72+
| `address()` ||||||||
73+
| `rewardAddress()` ||||||||
74+
| `getWalletUtxos()` ||||||||
75+
| `getWalletDelegation()` ||||||||
76+
| `signTx(tx)` ||||||||
77+
| `signMessage(address, payload)` ||||||||
78+
| **Transaction Building** |
79+
| `newTx()` ||||||||
80+
| **Client Composition** |
81+
| `attachProvider()` ||||||||
82+
| `attachWallet()` ||||||||
83+
| `attach(provider, wallet)` ||||||||
84+
85+
### Transaction Builder Capabilities
86+
87+
| Builder Method | ReadOnlyClient | SigningClient | Notes |
88+
|----------------|----------------|---------------|--------|
89+
| `build()` | ✅ → `Transaction` | ✅ → `SignBuilder` | ReadOnlyClient returns unsigned transaction, SigningClient returns builder with signing capabilities |
90+
91+
> **Note**: Transaction building requires protocol parameters from a provider. Only `ReadOnlyClient` and `SigningClient` have provider access and can build transactions. `ApiWalletClient` cannot build transactions directly - it must be upgraded to `SigningClient` by attaching a provider first.
92+
93+
### Provider Support
94+
95+
| Provider Type | Description | Supported Operations |
96+
|---------------|-------------|---------------------|
97+
| **Blockfrost** | API-based provider | All provider operations |
98+
| **Kupmios** | Kupo + Ogmios | All provider operations |
99+
| **Maestro** | Maestro API | All provider operations |
100+
| **Koios** | Koios API | All provider operations |
101+
| **Multi-Provider** | Failover support | All provider operations with redundancy (see [Provider Failover Specification](./provider-failover.md)) |
102+
103+
### Wallet Support
104+
105+
| Wallet Type | Client Types | Description | Capabilities |
106+
|-------------|-------------|-------------|--------------|
107+
| **Seed Wallet** | SigningWalletClient, SigningClient | HD wallet from mnemonic | Sign only (no submit without provider) |
108+
| **Private Key** | SigningWalletClient, SigningClient | Single key wallet | Sign only (no submit without provider) |
109+
| **Read-Only** | ReadOnlyWalletClient, ReadOnlyClient | Address monitoring | Query only, no signing |
110+
| **API Wallet (CIP-30)** | ApiWalletClient, SigningClient | Browser extension | Sign + submit via extension |
111+
112+
### Error Handling
113+
114+
| Client Type | Error Types | Effect Support |
115+
|-------------|-------------|----------------|
116+
| All clients | `ProviderError`, `WalletError` | ✅ Retry, timeout, fallback |
117+
| Multi-Provider | `MultiProviderError` | ✅ Automatic failover |
118+
| Transaction Builder | `TransactionBuilderError` | ✅ Validation errors |
119+
120+
### Upgrade Paths
121+
122+
#### Creation Methods
123+
124+
**Progressive Enhancement (starting from MinimalClient):**
125+
- `createClient()``MinimalClient``attachProvider()``attachWallet()`
126+
127+
**Direct Creation (bypassing MinimalClient):**
128+
- `createClient({ network, provider })``ProviderOnlyClient`
129+
- `createClient({ network, wallet: seedWallet })``SigningWalletClient`
130+
- `createClient({ network, wallet: apiWallet })``ApiWalletClient`
131+
- `createClient({ network, provider, wallet })``ReadOnlyClient` or `SigningClient`
132+
133+
## Creating Clients
134+
135+
### Simple Creation
136+
```typescript
137+
// Start with minimal client
138+
const client = createClient()
139+
140+
// Add provider for blockchain access
141+
const providerClient = client.attachProvider({
142+
type: "blockfrost",
143+
apiKey: "your-key"
144+
})
145+
146+
// Add wallet for signing
147+
const signingClient = providerClient.attachWallet({
148+
type: "seed",
149+
mnemonic: "your mnemonic"
150+
})
151+
```
152+
153+
### Direct Creation
154+
```typescript
155+
// Create fully configured client directly
156+
const client = createClient({
157+
network: "mainnet",
158+
provider: { type: "blockfrost", apiKey: "your-key" },
159+
wallet: { type: "seed", mnemonic: "your mnemonic" }
160+
})
161+
```
162+
163+
### Browser Wallet (CIP-30)
164+
```typescript
165+
// API wallet without provider (limited)
166+
const apiClient = createClient({
167+
network: "mainnet",
168+
wallet: { type: "api", api: window.cardano.nami }
169+
})
170+
171+
// Upgrade to full client by adding provider
172+
const fullClient = apiClient.attachProvider({
173+
type: "blockfrost",
174+
apiKey: "your-key"
175+
})
176+
```
177+
178+
## Architecture
179+
180+
The SDK uses Effect-TS for complex operations and provides Promise APIs for convenience. See the [Effect-Promise Architecture Guide](./effect-promise-architecture.md) for detailed information.
181+
182+
### Two Ways to Use
183+
184+
**Simple (Promise API):**
185+
```typescript
186+
// Familiar async/await
187+
const result = await client.signTx(transaction)
188+
```
189+
190+
**Advanced (Effect API):**
191+
```typescript
192+
// When you need retries, timeouts, etc.
193+
const program = client.Effect.signTx(transaction).pipe(
194+
Effect.retry({ times: 3 }),
195+
Effect.timeout(30000)
196+
)
197+
const result = await Effect.runPromise(program)
198+
```
199+
200+
## Multi-Provider Support
201+
202+
For production apps, use multiple providers for reliability. See the [Provider Failover Specification](./provider-failover.md) for detailed failover strategies and error handling.
203+
204+
```typescript
205+
const client = createClient({
206+
network: "mainnet",
207+
provider: {
208+
type: "multi",
209+
strategy: "priority", // try providers in order
210+
providers: [
211+
{
212+
type: "kupmios",
213+
kupoUrl: "wss://ogmios.example.com",
214+
ogmiosUrl: "https://kupo.example.com",
215+
retryPolicy: {
216+
maxRetries: 3,
217+
retryDelayMs: 1000,
218+
backoffMultiplier: 2,
219+
maxRetryDelayMs: 30000
220+
}
221+
},
222+
{
223+
type: "blockfrost",
224+
apiKey: "backup-key",
225+
baseUrl: "https://cardano-mainnet.blockfrost.io/api/v0",
226+
retryPolicy: {
227+
maxRetries: 2,
228+
retryDelayMs: 500,
229+
backoffMultiplier: 1.5,
230+
maxRetryDelayMs: 10000
231+
}
232+
}
233+
]
234+
},
235+
wallet: { type: "seed", mnemonic: "your mnemonic" }
236+
})
237+
```
238+
239+
## Common Patterns
240+
241+
### Signing-Only Wallet (Seed/Private Key)
242+
```typescript
243+
// Create signing wallet client for offline signing
244+
const signingWallet = createClient({
245+
network: "mainnet",
246+
wallet: { type: "seed", mnemonic: "your mnemonic" }
247+
})
248+
249+
// Get wallet address
250+
const address = await signingWallet.address()
251+
252+
// Sign a transaction that was built elsewhere
253+
const signedTx = await signingWallet.signTx(preBuiltTransaction)
254+
255+
// Sign a message
256+
const signature = await signingWallet.signMessage(address, "Hello World")
257+
258+
// ❌ Cannot submit - no submitTx method available
259+
// signingWallet.submitTx() // TypeScript error!
260+
```
261+
262+
### API Wallet (CIP-30)
263+
```typescript
264+
// Create API wallet client for browser wallet
265+
const apiWallet = createClient({
266+
network: "mainnet",
267+
wallet: { type: "api", api: window.cardano.nami }
268+
})
269+
270+
// Can sign AND submit
271+
const signedTx = await apiWallet.signTx(preBuiltTransaction)
272+
const txId = await apiWallet.submitTx(signedTx) // ✅ Available for API wallets
273+
```
274+
275+
### Read-Only Wallet (Address Only)
276+
```typescript
277+
// Create read-only wallet client for address-only operations
278+
const readOnlyWallet = createClient({
279+
network: "mainnet",
280+
wallet: { type: "read-only", address: "addr1..." }
281+
})
282+
283+
// Get wallet address and reward address
284+
const address = await readOnlyWallet.address()
285+
const rewardAddress = await readOnlyWallet.rewardAddress()
286+
287+
// ❌ Cannot query blockchain - no provider
288+
// readOnlyWallet.getWalletUtxos() // TypeScript error!
289+
290+
// ❌ Cannot sign - read-only wallet
291+
// readOnlyWallet.signTx() // TypeScript error!
292+
293+
// ❌ Cannot submit - no provider
294+
// readOnlyWallet.submitTx() // TypeScript error!
295+
296+
// ✅ Can upgrade to ReadOnlyClient by attaching provider
297+
const readOnlyClient = readOnlyWallet.attachProvider({
298+
type: "blockfrost",
299+
apiKey: "your-key"
300+
})
301+
302+
// Now can query blockchain with the address
303+
const utxos = await readOnlyClient.getWalletUtxos()
304+
```
305+
306+
### Monitor an Address
307+
```typescript
308+
const client = createClient({
309+
network: "mainnet",
310+
provider: { type: "blockfrost", apiKey: "key" },
311+
wallet: { type: "read-only", address: "addr1..." }
312+
})
313+
314+
const utxos = await client.getWalletUtxos()
315+
```
316+
317+
### Browser dApp
318+
```typescript
319+
// Connect to user's wallet
320+
const client = createClient({
321+
network: "mainnet",
322+
wallet: { type: "api", api: window.cardano.nami }
323+
})
324+
325+
// Sign and submit (no provider needed)
326+
const txId = await client.submitTx(transaction)
327+
```
328+
329+
### Server Application
330+
```typescript
331+
const client = createClient({
332+
network: "mainnet",
333+
provider: { type: "kupmios", kupoUrl: "...", ogmiosUrl: "..." },
334+
wallet: { type: "seed", mnemonic: process.env.MNEMONIC }
335+
})
336+
337+
const tx = await client.newTx()
338+
.payToAddress("addr1...", { lovelace: 1000000n })
339+
.complete()
340+
341+
const signed = await client.signTx(tx)
342+
const txId = await client.submitTx(signed)
343+
```
344+
345+
## Key Concepts
346+
347+
- **MinimalClient**: Starting point, just knows about network
348+
- **Providers**: Connect to Cardano blockchain (Blockfrost, Kupmios, etc.)
349+
- **Wallets**: Handle signing (seed phrase, private key, or browser extension)
350+
- **API Wallets**: Browser extensions like Nami, Eternl (CIP-30 standard)
351+
- **Effect**: Advanced features like retries and timeouts
352+
- **Promise**: Simple async/await for basic usage

0 commit comments

Comments
 (0)