feat/create devnet package#76
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extracts the devnet functionality from the main @evolution-sdk/evolution package into a new standalone package @evolution-sdk/devnet. This improves modularity and allows developers to use local Cardano devnet infrastructure independently.
Key changes:
- Created new
@evolution-sdk/devnetpackage with devnet cluster management, genesis configuration, and Docker orchestration - Updated imports in test files to use the new package structure
- Added comprehensive documentation including getting started guide, configuration reference, and integration examples
- Modified dependencies to use peer dependency pattern for Effect libraries
Reviewed changes
Copilot reviewed 20 out of 22 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/evolution-devnet/package.json | New package configuration with proper exports, dependencies, and build scripts |
| packages/evolution-devnet/src/index.ts | Package entry point exporting Devnet and DevnetDefault modules |
| packages/evolution-devnet/src/Devnet.ts | Updated imports to reference @evolution-sdk/evolution instead of relative paths |
| packages/evolution-devnet/src/DevnetDefault.ts | Genesis configuration types and defaults for devnet setup |
| packages/evolution-devnet/tsconfig*.json | TypeScript configuration files for source, build, and test targets |
| packages/evolution-devnet/test/*.test.ts | Updated test imports to use new package structure |
| packages/evolution-devnet/README.md | Package readme with installation and usage instructions |
| packages/evolution-devnet/CHANGELOG.md | Initial changelog for v0.2.5 release |
| docs/content/docs/devnet/*.mdx | Comprehensive documentation with examples and configuration guides |
| docs/package.json | Added devnet package dependency |
| pnpm-lock.yaml | Dependency updates reflecting new package structure |
| .changeset/wise-showers-burn.md | Changeset documenting the package extraction |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| minFeeB: 150000, | ||
| maxTxSize: 32768, // Larger transactions allowed | ||
| maxBlockBodySize: 131072, // Larger blocks for throughput | ||
| coinsPerUTxOByte: "4310" |
There was a problem hiding this comment.
The coinsPerUTxOByte field does not exist in the ShelleyGenesis.protocolParams type definition. The Shelley genesis uses minUTxOValue instead.
Remove this line or replace it with minUTxOValue: 0 to match the actual type definition.
| coinsPerUTxOByte: "4310" | |
| minUTxOValue: "4310" |
| const client = createClient({ | ||
| network: 0, // Network magic 0 for custom devnet | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "your twenty-four word mnemonic phrase here", | ||
| accountIndex: 0 | ||
| } | ||
| }); | ||
|
|
||
| // Get the address in Bech32 format | ||
| const addressBech32 = await client.address(); | ||
|
|
||
| // Convert to hex for genesis configuration | ||
| const addressHex = Core.Address.toHex(Core.Address.fromBech32(addressBech32)); | ||
|
|
||
| // Create custom genesis with funded address | ||
| const genesisConfig = { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS, | ||
| slotLength: 0.1, // 100ms per slot | ||
| epochLength: 100, // 100 slots per epoch | ||
| initialFunds: { | ||
| [addressHex]: 1_000_000_000_000 // 1 million ADA (in lovelace) | ||
| } |
There was a problem hiding this comment.
Network magic mismatch: The client is configured with network: 0, but the DEFAULT_SHELLEY_GENESIS (which is spread in this example) has networkMagic: 42.
Either:
- Change
network: 0tonetwork: 42to match the default, or - Add
networkMagic: 0to thegenesisConfigobject to override the default
The network magic must match between genesis configuration and client to avoid address validation failures.
|
|
||
| **Provider Configuration**: The client's provider configuration points to the devnet's Kupo and Ogmios services. This enables all blockchain queries (UTxOs, protocol parameters, tip) to target the local network. | ||
|
|
||
| **Network Magic**: Both devnet genesis and client configuration must use matching network identifiers. The default network magic `0` works for most testing. Override when testing multi-network scenarios. |
There was a problem hiding this comment.
Incorrect information: The statement "The default network magic 0 works for most testing" is inaccurate. The DEFAULT_SHELLEY_GENESIS has networkMagic: 42, not 0.
Update this to say: "The default network magic 42 works for most testing" to match the actual default value in DevnetDefault.ts.
| **Network Magic**: Both devnet genesis and client configuration must use matching network identifiers. The default network magic `0` works for most testing. Override when testing multi-network scenarios. | |
| **Network Magic**: Both devnet genesis and client configuration must use matching network identifiers. The default network magic `42` works for most testing. Override when testing multi-network scenarios. |
| const wallet1 = createClient({ | ||
| network: 0, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "wallet one mnemonic here", | ||
| accountIndex: 0 | ||
| } | ||
| }); | ||
|
|
||
| const wallet2 = createClient({ | ||
| network: 0, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "wallet two mnemonic here", | ||
| accountIndex: 0 | ||
| } | ||
| }); | ||
|
|
||
| // Get addresses | ||
| const addr1 = Core.Address.toHex(Core.Address.fromBech32(await wallet1.address())); | ||
| const addr2 = Core.Address.toHex(Core.Address.fromBech32(await wallet2.address())); | ||
|
|
||
| // Fund both in genesis | ||
| const genesisConfig = { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS, | ||
| slotLength: 0.1, | ||
| initialFunds: { | ||
| [addr1]: 500_000_000_000, | ||
| [addr2]: 300_000_000_000 | ||
| } | ||
| }; | ||
|
|
||
| const cluster = await Devnet.Cluster.make({ | ||
| clusterName: "multi-wallet", | ||
| ports: { node: 3001, submit: 3002 }, | ||
| shelleyGenesis: genesisConfig, | ||
| kupo: { enabled: true, port: 1442 }, | ||
| ogmios: { enabled: true, port: 1337 } | ||
| }); | ||
|
|
||
| await Devnet.Cluster.start(cluster); | ||
| await new Promise(resolve => setTimeout(resolve, 8000)); | ||
|
|
||
| // Create connected clients for both wallets | ||
| const client1 = createClient({ | ||
| network: 0, | ||
| provider: { | ||
| type: "kupmios", | ||
| kupoUrl: "http://localhost:1442", | ||
| ogmiosUrl: "http://localhost:1337" | ||
| }, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "wallet one mnemonic here", | ||
| accountIndex: 0 | ||
| } | ||
| }); | ||
|
|
||
| const client2 = createClient({ | ||
| network: 0, | ||
| provider: { | ||
| type: "kupmios", | ||
| kupoUrl: "http://localhost:1442", | ||
| ogmiosUrl: "http://localhost:1337" | ||
| }, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "wallet two mnemonic here", | ||
| accountIndex: 0 | ||
| } | ||
| }); |
There was a problem hiding this comment.
Network magic mismatch: All wallet and client configurations use network: 0 but genesisConfig spreads DEFAULT_SHELLEY_GENESIS which has networkMagic: 42.
Either:
- Change all
network: 0instances tonetwork: 42(lines 336, 345, 380, 394), or - Add
networkMagic: 0togenesisConfigon line 359
The network magic must match across all configurations.
| const wallet1 = createClient({ | ||
| network: 0, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "test wallet one mnemonic here", | ||
| accountIndex: 0 | ||
| } | ||
| }); | ||
|
|
||
| const wallet2 = createClient({ | ||
| network: 0, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "test wallet two mnemonic here", | ||
| accountIndex: 0 | ||
| } | ||
| }); | ||
|
|
||
| const addr1 = Core.Address.toHex(Core.Address.fromBech32(await wallet1.address())); | ||
| const addr2 = Core.Address.toHex(Core.Address.fromBech32(await wallet2.address())); | ||
|
|
||
| // Custom genesis configuration | ||
| const genesisConfig = { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS, | ||
|
|
||
| // Fast block production | ||
| slotLength: 0.05, // 50ms slots | ||
| epochLength: 200, // 200 slots (10 seconds) per epoch | ||
| activeSlotsCoeff: 1.0, // Every slot produces a block | ||
|
|
||
| // Fund multiple addresses | ||
| initialFunds: { | ||
| [addr1]: 5_000_000_000_000, // 5M ADA for primary testing | ||
| [addr2]: 1_000_000_000_000 // 1M ADA for secondary scenarios | ||
| }, | ||
|
|
||
| // Custom protocol parameters | ||
| protocolParams: { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS.protocolParams, | ||
| minFeeA: 40, // Lower fees for testing | ||
| minFeeB: 150000, | ||
| maxTxSize: 32768, // Larger transactions allowed | ||
| maxBlockBodySize: 131072, // Larger blocks for throughput | ||
| coinsPerUTxOByte: "4310" | ||
| }, | ||
|
|
||
| // Faster key evolution for staking tests | ||
| maxKESEvolutions: 60, | ||
| slotsPerKESPeriod: 3600 | ||
| }; |
There was a problem hiding this comment.
Network magic mismatch: Both wallet1 and wallet2 are configured with network: 0 but the genesisConfig spreads DEFAULT_SHELLEY_GENESIS which has networkMagic: 42.
Either:
- Change
network: 0tonetwork: 42on lines 298 and 307, or - Add
networkMagic: 0togenesisConfigon line 320 to override the default
The network magic must match.
| const client1 = createClient({ | ||
| network: 0, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "your mnemonic here", | ||
| accountIndex: 0 | ||
| } | ||
| }); | ||
|
|
||
| const client2 = createClient({ | ||
| network: 0, | ||
| wallet: { | ||
| type: "seed", | ||
| mnemonic: "your mnemonic here", | ||
| accountIndex: 1 // Different account index = different address | ||
| } | ||
| }); | ||
|
|
||
| const address1 = Core.Address.toHex(Core.Address.fromBech32(await client1.address())); | ||
| const address2 = Core.Address.toHex(Core.Address.fromBech32(await client2.address())); | ||
|
|
||
| const genesisConfig = { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS, | ||
| initialFunds: { | ||
| [address1]: 500_000_000_000, // 500K ADA | ||
| [address2]: 300_000_000_000 // 300K ADA | ||
| } | ||
| }; |
There was a problem hiding this comment.
Network magic mismatch: Both clients are configured with network: 0, but the genesisConfig spreads DEFAULT_SHELLEY_GENESIS which has networkMagic: 42.
Either:
- Change
network: 0tonetwork: 42in both client configurations, or - Add
networkMagic: 0to thegenesisConfigto override the default
The network magic must match between genesis and clients.
| const wallet = createClient({ | ||
| network: 0, | ||
| wallet: { type: "seed", mnemonic: MNEMONIC, accountIndex: 0 } | ||
| }); | ||
|
|
||
| const senderAddress = await wallet.address(); | ||
| const senderAddressHex = Core.Address.toHex(Core.Address.fromBech32(senderAddress)); | ||
|
|
||
| console.log("Sender address:", senderAddress); | ||
|
|
||
| // Step 2: Create genesis configuration with funded address | ||
| const genesisConfig = { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS, | ||
| slotLength: 0.1, // 100ms blocks | ||
| epochLength: 100, | ||
| initialFunds: { | ||
| [senderAddressHex]: 1_000_000_000_000 // 1 million ADA | ||
| } | ||
| }; | ||
|
|
||
| // Step 3: Create and start devnet cluster | ||
| const cluster = await Devnet.Cluster.make({ | ||
| clusterName: "integration-example", | ||
| ports: { node: 3001, submit: 3002 }, | ||
| shelleyGenesis: genesisConfig, | ||
| kupo: { enabled: true, port: 1442, logLevel: "Info" }, | ||
| ogmios: { enabled: true, port: 1337, logLevel: "info" } | ||
| }); | ||
|
|
||
| await Devnet.Cluster.start(cluster); | ||
|
|
||
| // Wait for Kupo and Ogmios to fully initialize | ||
| await new Promise(resolve => setTimeout(resolve, 8000)); | ||
|
|
||
| console.log("Devnet started with Kupo and Ogmios"); | ||
|
|
||
| // Step 4: Create client connected to devnet | ||
| const client = createClient({ | ||
| network: 0, | ||
| provider: { | ||
| type: "kupmios", | ||
| kupoUrl: "http://localhost:1442", | ||
| ogmiosUrl: "http://localhost:1337" | ||
| }, | ||
| wallet: { type: "seed", mnemonic: MNEMONIC, accountIndex: 0 } | ||
| }); | ||
|
|
There was a problem hiding this comment.
Network magic mismatch: The wallet is configured with network: 0 but genesisConfig spreads DEFAULT_SHELLEY_GENESIS which has networkMagic: 42.
Either:
- Change
network: 0tonetwork: 42on line 39 and line 76 (where the connected client is created), or - Add
networkMagic: 0to thegenesisConfigon line 50
The network magic must match between all clients and the genesis configuration.
| shelleyGenesis: { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS, | ||
| initialFunds: { | ||
| "address_hex_here": 100_000_000_000 | ||
| } | ||
| }, | ||
| kupo: { enabled: true, port: 1442 }, | ||
| ogmios: { enabled: true, port: 1337 } | ||
| }); | ||
|
|
||
| await Devnet.Cluster.start(cluster); | ||
| await new Promise(resolve => setTimeout(resolve, 6000)); | ||
|
|
||
| // Provider-only client (no wallet configured) | ||
| const providerClient = createClient({ | ||
| network: 0, | ||
| provider: { | ||
| type: "kupmios", | ||
| kupoUrl: "http://localhost:1442", | ||
| ogmiosUrl: "http://localhost:1337" | ||
| } | ||
| }); |
There was a problem hiding this comment.
Network magic mismatch: The provider client is configured with network: 0 but the shelleyGenesis spreads DEFAULT_SHELLEY_GENESIS which has networkMagic: 42.
Either:
- Change
network: 0tonetwork: 42, or - Add
networkMagic: 0to the shelleyGenesis object
The network magic must match.
| const wallet = createClient({ | ||
| network: 0, | ||
| wallet: { type: "seed", mnemonic, accountIndex: 0 } | ||
| }); | ||
|
|
||
| const addressHex = Core.Address.toHex( | ||
| Core.Address.fromBech32(await wallet.address()) | ||
| ); | ||
|
|
||
| const genesisConfig = { | ||
| ...DevnetDefault.DEFAULT_SHELLEY_GENESIS, | ||
| slotLength: 0.02, // Fast blocks for quick tests | ||
| epochLength: 50, | ||
| initialFunds: { [addressHex]: 10_000_000_000_000 } | ||
| }; | ||
|
|
||
| cluster = await Devnet.Cluster.make({ | ||
| clusterName: "test-suite-" + Date.now(), | ||
| ports: { node: 3001, submit: 3002 }, | ||
| shelleyGenesis: genesisConfig, | ||
| kupo: { enabled: true, port: 1442 }, | ||
| ogmios: { enabled: true, port: 1337 } | ||
| }); | ||
|
|
||
| await Devnet.Cluster.start(cluster); | ||
| await new Promise(resolve => setTimeout(resolve, 8000)); | ||
|
|
||
| client = createClient({ | ||
| network: 0, | ||
| provider: { | ||
| type: "kupmios", | ||
| kupoUrl: "http://localhost:1442", | ||
| ogmiosUrl: "http://localhost:1337" | ||
| }, | ||
| wallet: { type: "seed", mnemonic, accountIndex: 0 } | ||
| }); |
There was a problem hiding this comment.
Network magic mismatch: Both wallet and client are configured with network: 0 but genesisConfig spreads DEFAULT_SHELLEY_GENESIS which has networkMagic: 42.
Either:
- Change
network: 0tonetwork: 42on lines 236 and 263, or - Add
networkMagic: 0togenesisConfigon line 245
The network magic must match between all configurations.
| - **minFeeA / minFeeB**: Control transaction fees. Lower values reduce costs for testing. Higher values test fee edge cases. | ||
| - **maxTxSize**: Increase for large transaction testing (multiple inputs/outputs, large datums). Decrease to test size limit errors. | ||
| - **maxBlockBodySize**: Control how many transactions fit in a block. Affects throughput testing. | ||
| - **coinsPerUTxOByte**: Minimum ADA required per UTxO. Affects multi-output transaction viability. |
There was a problem hiding this comment.
The coinsPerUTxOByte field does not exist in the ShelleyGenesis.protocolParams type. The Shelley genesis uses minUTxOValue instead. Update this description to reference minUTxOValue instead of coinsPerUTxOByte.
| - **coinsPerUTxOByte**: Minimum ADA required per UTxO. Affects multi-output transaction viability. | |
| - **minUTxOValue**: Minimum ADA required per UTxO. Affects multi-output transaction viability. |
No description provided.