Integration
Complete workflows combining devnet with Evolution SDK client
Devnet Integration with Evolution SDK
Devnet provides the blockchain infrastructure. The Evolution SDK client provides transaction building and submission. Together, they enable complete end-to-end testing workflows without external dependencies.
Overview
A typical integration workflow follows this pattern:
- Generate wallet addresses for your test scenario
- Create devnet genesis configuration funding those addresses
- Start devnet cluster with Kupo and Ogmios
- Create Evolution SDK client connected to the devnet services
- Query protocol parameters and genesis UTxOs
- Build, sign, and submit transactions
- Await confirmation and verify results
- Clean up devnet resources
This guide demonstrates these patterns with complete, runnable examples.
Complete Workflow: Devnet to Transaction
This example shows the full cycle from cluster creation to confirmed transaction:
import { , , } from "@evolution-sdk/devnet";
import { , , , } from "@evolution-sdk/evolution";
const = "your twenty-four word mnemonic phrase here";
async function () {
// Step 1: Generate sender address
const = ({
: 0,
: { : "seed", : , : 0 }
});
const = await .();
const = .();
.("Sender address:", );
// Step 2: Create genesis configuration with funded address
const = {
....,
: 0.1, // 100ms blocks
: 100,
: {
[]: 1_000_000_000_000 // 1 million ADA
}
};
// Step 3: Create and start devnet cluster
const = await .({
: "integration-example",
: { : 3001, : 3002 },
: ,
: { : true, : 1442, : "Info" },
: { : true, : 1337, : "info" }
});
await .();
// Wait for Kupo and Ogmios to fully initialize
await new ( => (, 8000));
.("Devnet started with Kupo and Ogmios");
// Step 4: Create client connected to devnet
const = ({
: 0,
: {
: "kupmios",
: "http://localhost:1442",
: "http://localhost:1337"
},
: { : "seed", : , : 0 }
});
.("Client connected to devnet");
// Step 5: Query protocol parameters
const = await .();
.("Protocol parameters:", {
: .,
: .,
: .
});
// Step 6: Calculate genesis UTxOs (returns Core UTxOs)
const = await .();
.("Genesis UTxOs:", .);
.("Initial balance:", [0]?.., "lovelace");
// Step 7: Build transaction
const = .(
"addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae"
);
const = await
.()
.({
: ,
: .(10_000_000n) // Send 10 ADA
})
.({ : });
const = await .();
.("Transaction built:", {
: ...,
: ...
});
// Step 8: Sign transaction
const = await .();
.("Transaction signed");
// Step 9: Submit transaction
const = await .();
.("Transaction submitted:", );
// Step 10: Wait for confirmation
const = await .(, 1000);
.("Transaction confirmed:", );
// Step 11: Query final state
const = await .();
const = .(
(, ) => + ..,
0n
);
.("Final balance:", , "lovelace");
.("Fee paid:", [0]?..! - - 10_000_000n, "lovelace");
// Step 12: Clean up
await .();
await .();
.("Devnet stopped and removed");
}
().(.);This workflow demonstrates the complete integration: from empty Docker environment to confirmed transaction in under 10 seconds.
The genesis UTxO calculation provides the initial funds for transaction building. The client handles fee estimation, change output creation, and transaction signing automatically.
Understanding the Integration Points
Three key integration points connect devnet to the Evolution SDK:
Genesis to UTxO: The calculateUtxosFromConfig function converts genesis configuration into queryable UTxO objects. These UTxOs can be used as transaction inputs immediately after cluster start.
Critical: Genesis UTxOs and Kupo: Genesis UTxOs do NOT appear in Kupo's index. Kupo reads chain events starting from the first block, but genesis UTxOs are created in the genesis block itself (block 0), which has no transaction events. You MUST use calculateUtxosFromConfig to derive genesis UTxOs and explicitly inject them into your transaction builder using the availableUtxos parameter. Once you spend a genesis UTxO in a transaction, the resulting outputs WILL be indexed by Kupo and can be queried normally.
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.
Provider-Only Queries
Not all workflows require a wallet. Query blockchain state using a provider-only client:
import { , , } from "@evolution-sdk/devnet";
import { , , , } from "@evolution-sdk/evolution";
// Start devnet with funded address
const = await .({
: "query-example",
: { : 3001, : 3002 },
: {
....,
: {
"address_hex_here": 100_000_000_000
}
},
: { : true, : 1442 },
: { : true, : 1337 }
});
await .();
await new ( => (, 6000));
// Provider-only client (no wallet configured)
const = ({
: 0,
: {
: "kupmios",
: "http://localhost:1442",
: "http://localhost:1337"
}
});
// Query any address on the devnet
const = await .(
.("addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae")
);
.("UTxOs at address:", .);
// Query protocol parameters
const = await .();
.("Max transaction size:", .);
await .();
await .();Provider-only clients are ideal for blockchain explorers, monitoring tools, and backend services that query multiple addresses without signing transactions.
Testing Patterns
Integration Test Setup: Create a fresh cluster for each test suite to ensure isolated state:
import { , , , , } from "vitest";
import { , } from "@evolution-sdk/devnet";
import { , , , , type } from "@evolution-sdk/evolution";
("Transaction Tests", () => {
let : .Cluster;
let : ;
(async () => {
// Setup devnet for test suite
const = "test test test test test test test test test test test test test test test test test test test test test test test sauce";
const = ({
: 0,
: { : "seed", , : 0 }
});
const = .(await .());
const = {
....,
: 0.02, // Fast blocks for quick tests
: 50,
: { []: 10_000_000_000_000 }
};
= await .({
: "test-suite-" + .(),
: { : 3001, : 3002 },
: ,
: { : true, : 1442 },
: { : true, : 1337 }
});
await .();
await new ( => (, 8000));
= ({
: 0,
: {
: "kupmios",
: "http://localhost:1442",
: "http://localhost:1337"
},
: { : "seed", , : 0 }
});
}, 180_000); // Extended timeout for cluster startup
(async () => {
await .();
await .();
}, 60_000);
("should submit simple payment", async () => {
const = await
.()
.({
: .("addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae"),
: .(5_000_000n)
})
.();
const = await .();
const = await .();
const = await .(, 1000);
().(true);
}, 30_000);
("should handle multiple outputs", async () => {
const = await
.()
.({
: .("addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae"),
: .(2_000_000n)
})
.({
: .("addr_test1qpq6xvp5y4fw0wfgxfqmn78qqagkpv4q7qpqyz8s8x3snp5n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgsc3z7t3"),
: .(3_000_000n)
})
.();
const = await .();
const = await .();
const = await .(, 1000);
().(true);
}, 30_000);
});The beforeAll setup ensures all tests share the same devnet instance, reducing test suite runtime. The afterAll cleanup prevents resource leaks.
Multi-Wallet Scenarios
Test multi-party protocols by funding multiple addresses in genesis:
import { , } from "@evolution-sdk/devnet";
import { , , , } from "@evolution-sdk/evolution";
async function () {
// Create two wallets
const = ({
: 0,
: {
: "seed",
: "wallet one mnemonic here",
: 0
}
});
const = ({
: 0,
: {
: "seed",
: "wallet two mnemonic here",
: 0
}
});
// Get addresses as hex for genesis config
const = .(await .());
const = .(await .());
// Fund both in genesis
const = {
....,
: 0.1,
: {
[]: 500_000_000_000,
[]: 300_000_000_000
}
};
const = await .({
: "multi-wallet",
: { : 3001, : 3002 },
: ,
: { : true, : 1442 },
: { : true, : 1337 }
});
await .();
await new ( => (, 8000));
// Create connected clients for both wallets
const = ({
: 0,
: {
: "kupmios",
: "http://localhost:1442",
: "http://localhost:1337"
},
: {
: "seed",
: "wallet one mnemonic here",
: 0
}
});
const = ({
: 0,
: {
: "kupmios",
: "http://localhost:1442",
: "http://localhost:1337"
},
: {
: "seed",
: "wallet two mnemonic here",
: 0
}
});
// Wallet 1 sends to Wallet 2
const = await .();
const = await
.()
.({
: ,
: .(50_000_000n)
})
.();
const = await .().( => .());
await .(, 1000);
// Verify Wallet 2 received funds
const = await .();
const = .(
=> .(.) === .()
);
if () {
.("Wallet 2 received:", .., "lovelace");
}
await .();
await .();
}This pattern enables testing escrow contracts, multi-signature scenarios, and any protocol requiring multiple funded participants.
Long-Running Development Sessions
For interactive development, start devnet once and keep it running:
import { , , } from "@evolution-sdk/devnet";
async function () {
const = await .({
: "dev-session",
: { : 3001, : 3002 },
: {
....,
: 0.1,
: {
"your_address_hex_here": 1_000_000_000_000
}
},
: { : true, : 1442, : "Info" },
: { : true, : 1337, : "info" }
});
await .();
.("\n✓ Devnet running");
.(" Ogmios: http://localhost:1337");
.(" Kupo: http://localhost:1442");
.(" Node: localhost:3001");
.("\nPress Ctrl+C to stop\n");
// Graceful shutdown
.("SIGINT", async () => {
.("\nStopping devnet...");
await .();
await .();
.("✓ Devnet stopped\n");
.(0);
});
// Keep process alive
await new (() => {});
}
().(.);Run this script to start a persistent devnet for manual testing, dApp development, or interactive exploration. The devnet persists until you stop it with Ctrl+C.
Troubleshooting Integration Issues
"Cannot connect to provider": Ensure sufficient wait time after Cluster.start(). Kupo and Ogmios need 6-8 seconds to initialize completely. Increase wait time for slower systems.
"UTxO not found": Remember that genesis UTxOs are NOT indexed by Kupo. Always use calculateUtxosFromConfig and provide them via availableUtxos parameter when building your first transaction. After spending genesis UTxOs, subsequent outputs will be indexed normally.
"Transaction submission failed": Verify network magic matches between genesis configuration and client. Mismatched magic causes address validation failures.
"Fee estimation error": Ensure protocol parameters are accessible through the provider. Query client.getProtocolParameters() before building transactions to verify connectivity.
Port conflicts: If Kupo/Ogmios won't start, check for port conflicts. Use different ports or stop conflicting services.
Slow confirmation: With fast devnet configuration (20-100ms slots), transactions confirm in milliseconds. With realistic timing (1 second slots), expect normal confirmation delays.
Next Steps
You now have complete devnet integration knowledge:
- Start clusters with custom genesis
- Connect Evolution SDK clients to devnet services
- Build, sign, and submit transactions
- Query blockchain state through provider APIs
- Test multi-wallet and multi-party scenarios
For production deployment, replace devnet provider configuration with mainnet or testnet endpoints while keeping the same client APIs.
Common Integration Patterns Reference
Quick Test: Fast blocks, high initial balance, single wallet
{
slotLength: 0.02,
epochLength: 50,
initialFunds: { [addr]: 10_000_000_000_000 }
}Multi-Party Testing: Multiple funded addresses
{
initialFunds: {
[addr1]: 1_000_000_000_000,
[addr2]: 1_000_000_000_000,
[addr3]: 1_000_000_000_000
}
}Mainnet Simulation: Realistic timing and parameters
{
slotLength: 1.0,
epochLength: 21600,
activeSlotsCoeff: 0.05,
initialFunds: { [addr]: 1_000_000_000_000 }
}High-Throughput Testing: Large blocks, fast slots
{
slotLength: 0.05,
protocolParams: {
...defaults,
maxBlockBodySize: 131072,
maxTxSize: 32768
}
}