Skip to content

Commit 450cfc3

Browse files
Merge pull request #80 from IntersectMBO/refactor/devnet
refactor: devnet modules; better structure; improve devnet name tests
2 parents 8224324 + b52e9c7 commit 450cfc3

File tree

17 files changed

+1600
-1294
lines changed

17 files changed

+1600
-1294
lines changed

.changeset/warm-lies-joke.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
"@evolution-sdk/devnet": minor
3+
---
4+
5+
**Restructured module exports** for better modularity and clarity:
6+
- Replaced monolithic `Devnet` and `DevnetDefault` exports with granular named exports: `Cluster`, `Config`, `Container`, `Genesis`, and `Images`
7+
- Renamed types: `DevNetCluster``Cluster.Cluster`, `DevNetContainer``Container.Container`
8+
- Moved `DEFAULT_SHELLEY_GENESIS` from `DevnetDefault` to `Config` module
9+
10+
**New Features:**
11+
12+
- **Genesis module** - Calculate and query genesis UTxOs with Cardano's `initialFundsPseudoTxIn` algorithm:
13+
- `calculateUtxosFromConfig()` - Deterministically compute genesis UTxOs from Shelley genesis configuration using blake2b-256 hashing
14+
- `queryUtxos()` - Query actual genesis UTxOs from running node via cardano-cli
15+
- Provides predictable UTxO structure for testing without node interaction
16+
17+
- **Images module** - Docker image management utilities:
18+
- `isAvailable()` - Check if Docker image exists locally
19+
- `pull()` - Pull Docker images with progress logging
20+
- `ensureAvailable()` - Conditionally pull images only when needed
21+
22+
**Improvements:**
23+
24+
- Enhanced error handling with specific error reasons (`address_conversion_failed`, `utxo_query_failed`, `utxo_parse_failed`, `image_inspection_failed`, `image_pull_failed`)
25+
- All operations provide both Effect-based and Promise-based APIs for flexibility
26+
- Improved test coverage with descriptive cluster names for easier debugging
27+
- Full Effect error channel integration throughout the package
28+
29+
**Breaking Changes:**
30+
31+
Migration required for existing devnet users:
32+
33+
```typescript
34+
// Before
35+
import { Devnet, DevnetDefault } from "@evolution-sdk/devnet"
36+
37+
const cluster = await Devnet.Cluster.make()
38+
const config = DevnetDefault.DEFAULT_SHELLEY_GENESIS
39+
40+
// After
41+
import { Cluster, Config } from "@evolution-sdk/devnet"
42+
43+
const cluster = await Cluster.make()
44+
const config = Config.DEFAULT_SHELLEY_GENESIS
45+
```
46+
47+
All module functionality remains the same, only import syntax has changed to use destructured named exports from the main package.

docs/app/global.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,24 @@
22
@import 'fumadocs-ui/css/neutral.css';
33
@import 'fumadocs-ui/css/preset.css';
44
@import 'fumadocs-twoslash/twoslash.css';
5+
6+
/* Twoslash hover styling */
7+
.twoslash .twoslash-hover:hover .twoslash-popup-container {
8+
background: hsl(var(--popover));
9+
border: 1px solid hsl(var(--border));
10+
border-radius: 8px;
11+
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
12+
backdrop-filter: blur(12px);
13+
padding: 8px 12px;
14+
}
15+
16+
.twoslash .twoslash-popup-container {
17+
border-radius: 8px;
18+
overflow: hidden;
19+
}
20+
21+
.twoslash .twoslash-popup-code {
22+
color: hsl(var(--popover-foreground));
23+
font-size: 0.875rem;
24+
line-height: 1.5;
25+
}

docs/content/docs/devnet/configuration.mdx

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ The most common customization is funding specific addresses with ADA at genesis.
2222
Addresses must be provided in hexadecimal format. Convert Bech32 addresses using the Evolution SDK's address utilities:
2323

2424
```typescript twoslash
25-
import { Devnet, DevnetDefault } from "@evolution-sdk/devnet";
25+
import { Cluster, Config } from "@evolution-sdk/devnet";
2626
import { Core, createClient } from "@evolution-sdk/evolution";
2727

2828
// Create a client to generate an address
@@ -43,22 +43,22 @@ const addressHex = Core.Address.toHex(Core.Address.fromBech32(addressBech32));
4343

4444
// Create custom genesis with funded address
4545
const genesisConfig = {
46-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS,
46+
...Config.DEFAULT_SHELLEY_GENESIS,
4747
slotLength: 0.1, // 100ms per slot
4848
epochLength: 100, // 100 slots per epoch
4949
initialFunds: {
5050
[addressHex]: 1_000_000_000_000 // 1 million ADA (in lovelace)
5151
}
52-
} satisfies DevnetDefault.ShelleyGenesis;
52+
} satisfies Config.ShelleyGenesis;
5353

5454
// Create cluster with custom genesis
55-
const cluster = await Devnet.Cluster.make({
55+
const cluster = await Cluster.make({
5656
clusterName: "funded-devnet",
5757
ports: { node: 3001, submit: 3002 },
5858
shelleyGenesis: genesisConfig
5959
});
6060

61-
await Devnet.Cluster.start(cluster);
61+
await Cluster.start(cluster);
6262
```
6363

6464
The address now has 1 million ADA available from block 0. The funds appear as a single UTxO that can be queried and spent immediately.
@@ -70,7 +70,7 @@ Amounts are specified in lovelace (1 ADA = 1,000,000 lovelace). Use underscores
7070
Fund multiple addresses by adding additional entries to `initialFunds`. Each address gets an independent genesis UTxO:
7171

7272
```typescript twoslash
73-
import { Devnet, DevnetDefault } from "@evolution-sdk/devnet";
73+
import { Cluster, Config } from "@evolution-sdk/devnet";
7474
import { Core, createClient } from "@evolution-sdk/evolution";
7575

7676
const client1 = createClient({
@@ -95,20 +95,20 @@ const address1 = Core.Address.toHex(Core.Address.fromBech32(await client1.addres
9595
const address2 = Core.Address.toHex(Core.Address.fromBech32(await client2.address()));
9696

9797
const genesisConfig = {
98-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS,
98+
...Config.DEFAULT_SHELLEY_GENESIS,
9999
initialFunds: {
100100
[address1]: 500_000_000_000, // 500K ADA
101101
[address2]: 300_000_000_000 // 300K ADA
102102
}
103-
} satisfies DevnetDefault.ShelleyGenesis;
103+
} satisfies Config.ShelleyGenesis;
104104

105-
const cluster = await Devnet.Cluster.make({
105+
const cluster = await Cluster.make({
106106
clusterName: "multi-user-devnet",
107107
ports: { node: 3001, submit: 3002 },
108108
shelleyGenesis: genesisConfig
109109
});
110110

111-
await Devnet.Cluster.start(cluster);
111+
await Cluster.start(cluster);
112112
```
113113

114114
This pattern enables testing multi-party protocols, delegation scenarios, and wallet interaction without complex transaction setup.
@@ -117,21 +117,21 @@ This pattern enables testing multi-party protocols, delegation scenarios, and wa
117117

118118
After creating a genesis configuration, calculate the resulting UTxOs to verify addresses and amounts.
119119

120-
**Important**: Genesis UTxOs do NOT appear in Kupo's index because Kupo reads chain events starting from block 1, while genesis UTxOs exist in block 0 (the genesis block itself). You must use `calculateUtxosFromConfigOrThrow` to derive these UTxOs and provide them to your transaction builder via the `availableUtxos` parameter. After spending a genesis UTxO, the resulting outputs will be indexed normally by Kupo.
120+
**Important**: Genesis UTxOs do NOT appear in Kupo's index because Kupo reads chain events starting from block 1, while genesis UTxOs exist in block 0 (the genesis block itself). You must use `calculateUtxosFromConfig` to derive these UTxOs and provide them to your transaction builder via the `availableUtxos` parameter. After spending a genesis UTxO, the resulting outputs will be indexed normally by Kupo.
121121

122122
```typescript twoslash
123-
import { Devnet, DevnetDefault } from "@evolution-sdk/devnet";
123+
import { Config, Genesis } from "@evolution-sdk/devnet";
124124
declare const addressHex: string;
125125

126126
const genesisConfig = {
127-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS,
127+
...Config.DEFAULT_SHELLEY_GENESIS,
128128
initialFunds: {
129129
[addressHex]: 1_000_000_000_000
130130
}
131-
} satisfies DevnetDefault.ShelleyGenesis;
131+
} satisfies Config.ShelleyGenesis;
132132

133133
// Calculate UTxOs before starting the cluster
134-
const genesisUtxos = await Devnet.Genesis.calculateUtxosFromConfigOrThrow(genesisConfig);
134+
const genesisUtxos = await Genesis.calculateUtxosFromConfig(genesisConfig);
135135

136136
console.log("Genesis UTxOs:", genesisUtxos.length);
137137
genesisUtxos.forEach(utxo => {
@@ -150,15 +150,15 @@ The devnet runs on Conway era and uses three genesis files with distinct paramet
150150

151151
### Genesis Type Definitions
152152

153-
The devnet package exports these types from `DevnetDefault`:
153+
The devnet package exports these types from `Config`:
154154

155155
```typescript twoslash
156-
import type { DevnetDefault } from "@evolution-sdk/devnet";
156+
import type { Config } from "@evolution-sdk/devnet";
157157

158158
// Use the exported types
159-
type ShelleyGenesis = DevnetDefault.ShelleyGenesis;
160-
type AlonzoGenesis = DevnetDefault.AlonzoGenesis;
161-
type ConwayGenesis = DevnetDefault.ConwayGenesis;
159+
type ShelleyGenesis = Config.ShelleyGenesis;
160+
type AlonzoGenesis = Config.AlonzoGenesis;
161+
type ConwayGenesis = Config.ConwayGenesis;
162162

163163
// ShelleyGenesis.protocolParams contains:
164164
// - minFeeA, minFeeB: Transaction fees
@@ -185,14 +185,14 @@ type ConwayGenesis = DevnetDefault.ConwayGenesis;
185185
Customize protocol parameters by modifying the appropriate genesis configuration:
186186

187187
```typescript twoslash
188-
import { Devnet, DevnetDefault } from "@evolution-sdk/devnet";
188+
import { Cluster, Config } from "@evolution-sdk/devnet";
189189
declare const addressHex: string;
190190

191191
// Shelley genesis: fees and transaction limits
192-
const shelleyConfig: Partial<DevnetDefault.ShelleyGenesis> = {
193-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS,
192+
const shelleyConfig: Partial<Config.ShelleyGenesis> = {
193+
...Config.DEFAULT_SHELLEY_GENESIS,
194194
protocolParams: {
195-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS.protocolParams,
195+
...Config.DEFAULT_SHELLEY_GENESIS.protocolParams,
196196
minFeeA: 50, // Transaction fee coefficient (default: 44)
197197
minFeeB: 200000, // Minimum fee constant (default: 155381)
198198
maxTxSize: 32768 // Maximum transaction size in bytes (default: 16384)
@@ -203,19 +203,19 @@ const shelleyConfig: Partial<DevnetDefault.ShelleyGenesis> = {
203203
};
204204

205205
// Alonzo genesis: UTxO costs (Conway runtime uses this)
206-
const alonzoConfig: Partial<DevnetDefault.AlonzoGenesis> = {
207-
...DevnetDefault.DEFAULT_ALONZO_GENESIS,
206+
const alonzoConfig: Partial<Config.AlonzoGenesis> = {
207+
...Config.DEFAULT_ALONZO_GENESIS,
208208
lovelacePerUTxOWord: 34482 // Conway converts to coinsPerUtxoByte (÷8 = 4310.25)
209209
};
210210

211-
const cluster = await Devnet.Cluster.make({
211+
const cluster = await Cluster.make({
212212
clusterName: "custom-protocol-devnet",
213213
ports: { node: 3001, submit: 3002 },
214214
shelleyGenesis: shelleyConfig,
215215
alonzoGenesis: alonzoConfig
216216
});
217217

218-
await Devnet.Cluster.start(cluster);
218+
await Cluster.start(cluster);
219219
```
220220

221221
Common protocol parameter customizations:
@@ -233,7 +233,7 @@ Common protocol parameter customizations:
233233

234234
**Important**: The devnet runs Conway era. At runtime, the node uses `coinsPerUtxoByte` (from Alonzo's `lovelacePerUTxOWord`), not Shelley's legacy `minUTxOValue`.
235235

236-
See the exported types `DevnetDefault.ShelleyGenesis`, `DevnetDefault.AlonzoGenesis`, and `DevnetDefault.ConwayGenesis` for complete definitions.
236+
See the exported types `Config.ShelleyGenesis`, `Config.AlonzoGenesis`, and `Config.ConwayGenesis` for complete definitions.
237237

238238
## Block Timing and Network Behavior
239239

@@ -242,26 +242,26 @@ Control block production speed to match testing requirements. Fast blocks accele
242242
Adjust slot timing through genesis parameters:
243243

244244
```typescript twoslash
245-
import { Devnet, DevnetDefault } from "@evolution-sdk/devnet";
245+
import { Cluster, Config } from "@evolution-sdk/devnet";
246246
declare const addressHex: string;
247247

248248
const genesisConfig = {
249-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS,
249+
...Config.DEFAULT_SHELLEY_GENESIS,
250250
slotLength: 0.02, // 20ms per slot (very fast!)
251251
epochLength: 50, // 50 slots per epoch
252252
activeSlotsCoeff: 1.0, // Every slot produces a block
253253
initialFunds: {
254254
[addressHex]: 100_000_000_000
255255
}
256-
} satisfies DevnetDefault.ShelleyGenesis;
256+
} satisfies Config.ShelleyGenesis;
257257

258-
const cluster = await Devnet.Cluster.make({
258+
const cluster = await Cluster.make({
259259
clusterName: "fast-devnet",
260260
ports: { node: 3001, submit: 3002 },
261261
shelleyGenesis: genesisConfig
262262
});
263263

264-
await Devnet.Cluster.start(cluster);
264+
await Cluster.start(cluster);
265265
```
266266

267267
Timing parameters explained:
@@ -277,18 +277,18 @@ Fast block production (20ms slots, coefficient 1.0) provides near-instant transa
277277
Security parameters affect chain stability and rollback protection:
278278

279279
```typescript twoslash
280-
import { DevnetDefault } from "@evolution-sdk/devnet";
280+
import { Config } from "@evolution-sdk/devnet";
281281
declare const addressHex: string;
282282

283283
const genesisConfig = {
284-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS,
284+
...Config.DEFAULT_SHELLEY_GENESIS,
285285
securityParam: 10, // k parameter: rollback limit
286286
maxKESEvolutions: 120, // KES key evolution limit
287287
slotsPerKESPeriod: 7200, // Slots before KES evolution
288288
initialFunds: {
289289
[addressHex]: 100_000_000_000
290290
}
291-
} satisfies DevnetDefault.ShelleyGenesis;
291+
} satisfies Config.ShelleyGenesis;
292292
```
293293

294294
- **securityParam (k)**: Blocks before finality. Lower values (10) allow faster testing of chain reorganizations. Default 2160 matches mainnet.
@@ -302,7 +302,7 @@ Most applications can ignore these parameters and use defaults. Adjust only when
302302
Combining all customization options for a comprehensive devnet:
303303

304304
```typescript twoslash
305-
import { Devnet, DevnetDefault } from "@evolution-sdk/devnet";
305+
import { Cluster, Config, Genesis } from "@evolution-sdk/devnet";
306306
import { Core, createClient } from "@evolution-sdk/evolution";
307307

308308
// Generate funded addresses
@@ -329,7 +329,7 @@ const addr2 = Core.Address.toHex(Core.Address.fromBech32(await wallet2.address()
329329

330330
// Custom genesis configuration
331331
const genesisConfig = {
332-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS,
332+
...Config.DEFAULT_SHELLEY_GENESIS,
333333

334334
// Fast block production
335335
slotLength: 0.05, // 50ms slots
@@ -344,7 +344,7 @@ const genesisConfig = {
344344

345345
// Custom protocol parameters
346346
protocolParams: {
347-
...DevnetDefault.DEFAULT_SHELLEY_GENESIS.protocolParams,
347+
...Config.DEFAULT_SHELLEY_GENESIS.protocolParams,
348348
minFeeA: 40, // Lower fees for testing
349349
minFeeB: 150000,
350350
maxTxSize: 32768, // Larger transactions allowed
@@ -354,10 +354,10 @@ const genesisConfig = {
354354
// Faster key evolution for staking tests
355355
maxKESEvolutions: 60,
356356
slotsPerKESPeriod: 3600
357-
} satisfies DevnetDefault.ShelleyGenesis;
357+
} satisfies Config.ShelleyGenesis;
358358

359359
// Create cluster with full services
360-
const cluster = await Devnet.Cluster.make({
360+
const cluster = await Cluster.make({
361361
clusterName: "comprehensive-devnet",
362362
ports: { node: 3001, submit: 3002 },
363363
shelleyGenesis: genesisConfig,
@@ -374,7 +374,7 @@ const cluster = await Devnet.Cluster.make({
374374
});
375375

376376
// Start the devnet
377-
await Devnet.Cluster.start(cluster);
377+
await Cluster.start(cluster);
378378

379379
console.log("Comprehensive devnet ready:");
380380
console.log("- Two funded addresses");
@@ -383,7 +383,7 @@ console.log("- Custom protocol parameters");
383383
console.log("- Kupo and Ogmios enabled");
384384

385385
// Calculate and verify genesis UTxOs
386-
const utxos = await Devnet.Genesis.calculateUtxosFromConfigOrThrow(genesisConfig);
386+
const utxos = await Genesis.calculateUtxosFromConfig(genesisConfig);
387387
console.log(`\nGenesis UTxOs: ${utxos.length}`);
388388
utxos.forEach((utxo, i) => {
389389
console.log(` Address ${i + 1}: ${utxo.assets.lovelace} lovelace`);

0 commit comments

Comments
 (0)