Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ca54a58
feat(client): replace NetworkId with Chain descriptor
solidsnakedev Mar 31, 2026
a25cb01
feat(devnet): add getChain and BOOTSTRAP_CHAIN helpers
solidsnakedev Mar 31, 2026
342da47
chore(example): migrate to chain: Chain pattern
solidsnakedev Mar 31, 2026
3866c80
docs(clients): update chain configuration examples
solidsnakedev Mar 31, 2026
c5abca1
release: changeset for chain-interface
solidsnakedev Mar 31, 2026
80d7339
fix: migrate docs and devnet tests to Chain interface
solidsnakedev Apr 1, 2026
7422aaa
fix: align blockfrost URL fallback with chain fallback
solidsnakedev Apr 1, 2026
a97ee5a
docs: migrate all network references to chain API
solidsnakedev Apr 1, 2026
2c7c6f2
chore: remove obsolete source files
solidsnakedev Apr 4, 2026
8eff4d0
feat(core): extend Address, UTxO types and add FeeValidation
solidsnakedev Apr 4, 2026
5fbf1fa
feat(wallet): add typed wallet system
solidsnakedev Apr 4, 2026
8d5fa70
refactor(builders): replace monolithic architecture with modular design
solidsnakedev Apr 4, 2026
bbc243d
feat(client): add composable client API with capability system
solidsnakedev Apr 4, 2026
95a39d1
refactor(client): restructure legacy client and fix stale references
solidsnakedev Apr 4, 2026
0e7af2e
refactor(providers): rename Http utility and KoiosProvider
solidsnakedev Apr 4, 2026
285dc9d
test: migrate unit tests to new chain-interface API
solidsnakedev Apr 4, 2026
29082e0
test(devnet): update integration tests for chain-interface API
solidsnakedev Apr 4, 2026
3d09065
fix(lint): remove unused imports in builders and tests
solidsnakedev Apr 4, 2026
3f31d90
chore(changeset): update chain-interface release notes to minor
solidsnakedev Apr 4, 2026
9bf968c
fix(client): export legacy client types from index to fix docs build
solidsnakedev Apr 4, 2026
6b81331
fix(devnet-tests): fix Vote and NativeScript test regressions
solidsnakedev Apr 4, 2026
18368c8
feat(client): close capability gap between composable and legacy clients
solidsnakedev Apr 4, 2026
ad56853
test(devnet): remove explicit referenceUtxos workaround in NativeScri…
solidsnakedev Apr 4, 2026
f1861de
fix(client): sort legacy client exports for lint
solidsnakedev Apr 4, 2026
a91d8c4
docs: fix twoslash CI errors in clients, wallets, devnet, and providers
solidsnakedev Apr 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .changeset/chain-interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
"@evolution-sdk/evolution": patch
"@evolution-sdk/devnet": patch
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changeset marks the release as a patch, but replacing network?: NetworkId with required chain: Chain (and removing createClient() / default-network behavior) is a breaking public API change. The changeset should be a major bump (or at least follow the repo’s documented semver policy for breaking changes).

Suggested change
"@evolution-sdk/evolution": patch
"@evolution-sdk/devnet": patch
"@evolution-sdk/evolution": major
"@evolution-sdk/devnet": major

Copilot uses AI. Check for mistakes.
---

`createClient` now requires an explicit `chain` field instead of the optional `network?: string` field.

Previously, selecting a network required passing a string identifier and, separately, a `slotConfig` object for slot/time conversions. There was no single source of truth that tied network identity, magic number, and timing parameters together:

```ts
// Before
createClient({
network: "preprod",
slotConfig: { zeroTime: 1655769600000n, zeroSlot: 86400n, slotLength: 1000 },
provider: { ... },
wallet: { ... }
})
```

Now the `chain` field is a rich descriptor that carries all of this together. Three built-in constants are exported from the top-level package:

```ts
// After
import { createClient, preprod } from "@evolution-sdk/evolution"

createClient({
chain: preprod,
provider: { ... },
wallet: { ... }
})
```

For custom networks — local devnets, private chains, or future forks — use `defineChain`:

```ts
import { defineChain } from "@evolution-sdk/evolution"

const devnet = defineChain({
name: "Devnet",
id: 0,
networkMagic: 42,
slotConfig: { zeroTime: 0n, zeroSlot: 0n, slotLength: 1000 },
epochLength: 432000,
})
```

The `networkId: number | string` property on client objects is replaced with `chain: Chain`. The `@evolution-sdk/devnet` package adds `getChain(cluster)` and `BOOTSTRAP_CHAIN` for constructing a `Chain` from a running local devnet.
26 changes: 13 additions & 13 deletions docs/content/docs/clients/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ interface ReadOnlyWalletConfig {
### Backend Transaction Building

```typescript twoslash
import { Address, Assets, Transaction, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, Transaction, createClient, mainnet } from "@evolution-sdk/evolution";

// Backend: Create provider client, then attach read-only wallet
const providerClient = createClient({
network: "mainnet",
chain: mainnet,
provider: {
type: "blockfrost",
baseUrl: "https://cardano-mainnet.blockfrost.io/api/v0",
Expand Down Expand Up @@ -87,15 +87,15 @@ Frontend applications connect to user wallets through CIP-30 but never have prov
### Implementation

```typescript
import { Address, Assets, Transaction, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, Transaction, createClient, mainnet } from "@evolution-sdk/evolution";

// 1. Connect wallet
declare const cardano: any;
const walletApi = await cardano.eternl.enable();

// 2. Create signing-only client
const client = createClient({
network: "mainnet",
chain: mainnet,
wallet: { type: "api", api: walletApi }
});

Expand Down Expand Up @@ -193,7 +193,7 @@ export type BuildPaymentResponse = { txCbor: string };

// @filename: frontend.ts
// ===== Frontend (Browser) =====
import { Address, Assets, Transaction, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, Transaction, createClient, mainnet } from "@evolution-sdk/evolution";
import type { BuildPaymentResponse } from "./shared";

declare const cardano: any;
Expand All @@ -202,7 +202,7 @@ async function sendPayment(recipientAddress: string, lovelace: bigint) {
// 1. Connect user wallet
const walletApi = await cardano.eternl.enable();
const walletClient = createClient({
network: "mainnet",
chain: mainnet,
wallet: { type: "api", api: walletApi }
});

Expand Down Expand Up @@ -232,7 +232,7 @@ async function sendPayment(recipientAddress: string, lovelace: bigint) {

// @filename: backend.ts
// ===== Backend (Server) =====
import { Address, Assets, Transaction, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, Transaction, createClient, mainnet } from "@evolution-sdk/evolution";

export async function buildPayment(
userAddressBech32: string,
Expand All @@ -244,7 +244,7 @@ export async function buildPayment(

// Create provider client first, then attach read-only wallet
const providerClient = createClient({
network: "mainnet",
chain: mainnet,
provider: {
type: "blockfrost",
baseUrl: "https://cardano-mainnet.blockfrost.io/api/v0",
Expand Down Expand Up @@ -302,7 +302,7 @@ User keys never leave their device. Provider API keys never exposed to frontend.
```typescript
// WRONG - Frontend has no provider
const client = createClient({
network: "mainnet",
chain: mainnet,
wallet: { type: "api", api: walletApi }
// No provider!
});
Expand All @@ -315,7 +315,7 @@ const builder = client.newTx(); // Error: Cannot build without provider
```typescript
// CORRECT - Frontend signs, backend builds
const client = createClient({
network: "mainnet",
chain: mainnet,
wallet: { type: "api", api: walletApi }
});

Expand All @@ -328,7 +328,7 @@ const witnessSet = await client.signTx(txCborFromBackend);
```typescript
// WRONG - Backend has no private keys
const client = createClient({
network: "mainnet",
chain: mainnet,
provider: { /* ... */ },
wallet: {
type: "read-only",
Expand All @@ -344,8 +344,8 @@ await client.sign(); // Error: Cannot sign with read-only wallet
```typescript
// CORRECT - Backend builds, frontend signs
const client = createClient({
network: "mainnet",
provider: { /* ... */ },
chain: mainnet,
provider: { /* ... */ },
wallet: {
type: "read-only",
address: userAddress
Expand Down
56 changes: 44 additions & 12 deletions docs/content/docs/clients/client-basics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ description: "Learn the basics of creating and using an Evolution SDK client"

The Evolution SDK client is your primary interface to the Cardano blockchain. It combines wallet management, provider communication, and transaction building into a single, cohesive API. Once configured, the client handles UTxO selection, fee calculation, and signing—letting you focus on your application logic.

Think of the client as your persistent connection: configure it once with your network, provider, and wallet, then use it throughout your application to build and submit transactions. All operations maintain type safety and composability through Effect-TS.
Think of the client as your persistent connection: configure it once with your chain, provider, and wallet, then use it throughout your application to build and submit transactions. All operations maintain type safety and composability through Effect-TS.

## Creating a Client

Configure your client with three essential pieces: the network (mainnet/preprod/preview), your blockchain provider, and the wallet for signing:
Configure your client with three essential pieces: the **chain** (which Cardano network to target), your blockchain provider, and the wallet for signing:

```ts twoslash
import { Address, Assets, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, createClient, preprod } from "@evolution-sdk/evolution";

const client = createClient({
network: "preprod",
chain: preprod,
provider: {
type: "blockfrost",
baseUrl: "https://cardano-preprod.blockfrost.io/api/v0",
Expand All @@ -31,6 +31,38 @@ const client = createClient({
});
```

## Chain Configuration

The `chain` field is a rich descriptor, not just a string identifier. It carries network magic, slot timing parameters, and optional block explorer URLs — everything the SDK needs to interact with a specific Cardano network.

Evolution SDK exports three built-in chain constants:

| Constant | Network | Network Magic |
|----------|---------|---------------|
| `mainnet` | Production | `764824073` |
| `preprod` | Pre-production testnet | `1` |
| `preview` | Preview testnet | `2` |

```ts twoslash
import { mainnet, preprod, preview } from "@evolution-sdk/evolution";
```

For custom networks — local devnets, private chains, or future Cardano forks — use `defineChain`:

```ts twoslash
import { defineChain } from "@evolution-sdk/evolution";

const devnet = defineChain({
name: "Devnet",
id: 0,
networkMagic: 42,
slotConfig: { zeroTime: 0n, zeroSlot: 0n, slotLength: 1000 },
epochLength: 432000,
});
```

The `slotConfig` is embedded in the chain constant and consumed directly by the transaction builder for slot-to-POSIX time conversions. There is no separate `slotConfig` parameter on `createClient`.

## The Transaction Workflow

Evolution SDK follows a three-stage pattern: build, sign, submit. Each stage returns a new builder with stage-specific methods, preventing invalid operations (like submitting before signing).
Expand All @@ -40,10 +72,10 @@ Evolution SDK follows a three-stage pattern: build, sign, submit. Each stage ret
Start with `client.newTx()` and chain operations to specify outputs, metadata, or validity ranges:

```ts twoslash
import { Address, Assets, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, createClient, preprod } from "@evolution-sdk/evolution";

const client = createClient({
network: "preprod",
chain: preprod,
provider: { type: "blockfrost", baseUrl: "", projectId: "" },
wallet: { type: "seed", mnemonic: "", accountIndex: 0 }
});
Expand All @@ -63,10 +95,10 @@ const signBuilder = await builder.build();
Call `.sign()` on the built transaction to create signatures with your wallet:

```ts twoslash
import { Address, Assets, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, createClient, preprod } from "@evolution-sdk/evolution";

const client = createClient({
network: "preprod",
chain: preprod,
provider: { type: "blockfrost", baseUrl: "", projectId: "" },
wallet: { type: "seed", mnemonic: "", accountIndex: 0 }
});
Expand All @@ -83,10 +115,10 @@ const submitBuilder = await signBuilder.sign();
Finally, `.submit()` broadcasts the signed transaction to the blockchain and returns the transaction hash:

```ts twoslash
import { Address, Assets, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, createClient, preprod } from "@evolution-sdk/evolution";

const client = createClient({
network: "preprod",
chain: preprod,
provider: { type: "blockfrost", baseUrl: "", projectId: "" },
wallet: { type: "seed", mnemonic: "", accountIndex: 0 }
});
Expand All @@ -105,10 +137,10 @@ console.log("Transaction submitted:", txId);
Here's the complete workflow in a single example—from client creation through transaction submission:

```ts twoslash
import { Address, Assets, createClient } from "@evolution-sdk/evolution";
import { Address, Assets, createClient, preprod } from "@evolution-sdk/evolution";

const client = createClient({
network: "preprod",
chain: preprod,
provider: {
type: "blockfrost",
baseUrl: "https://cardano-preprod.blockfrost.io/api/v0",
Expand Down
8 changes: 5 additions & 3 deletions docs/content/docs/clients/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ Different combinations create different client types with distinct capabilities.
All clients use `createClient` with different configurations:

```typescript
import { mainnet, preprod, preview, defineChain } from "@evolution-sdk/evolution";

createClient({
network: "mainnet" | "preprod" | "preview",
provider?: ProviderConfig, // Optional
wallet?: WalletConfig // Optional
chain: mainnet | preprod | preview, // or a custom chain via defineChain(...)
provider?: ProviderConfig, // Optional
wallet?: WalletConfig // Optional
})
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code sample calls createClient(...) but doesn’t import it (and also references ProviderConfig/WalletConfig without imports). The snippet should include createClient in the import (and either import the types or make the snippet explicitly schematic) so readers can copy/paste it without errors.

Copilot uses AI. Check for mistakes.
```

Expand Down
18 changes: 9 additions & 9 deletions docs/content/docs/clients/providers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ Choose Blockfrost for quick setup with hosted infrastructure, or Kupmios for sel
Blockfrost offers hosted infrastructure with a free tier, perfect for development and small-scale applications. Setup takes minutes—just get an API key and connect.

```ts twoslash
import { createClient } from "@evolution-sdk/evolution";
import { createClient, preprod } from "@evolution-sdk/evolution";

const client = createClient({
network: "preprod",
chain: preprod,
provider: {
type: "blockfrost",
baseUrl: "https://cardano-preprod.blockfrost.io/api/v0",
Expand All @@ -41,10 +41,10 @@ const client = createClient({
Run your own Cardano node infrastructure for complete control and privacy. Kupmios combines Kupo (UTxO indexing) and Ogmios (node queries) for a fully local setup. Ideal for production applications that need guaranteed uptime and don't want external dependencies.

```ts twoslash
import { createClient } from "@evolution-sdk/evolution";
import { createClient, preprod } from "@evolution-sdk/evolution";

const client = createClient({
network: "preprod",
chain: preprod,
provider: {
type: "kupmios",
kupoUrl: "http://localhost:1442",
Expand Down Expand Up @@ -79,11 +79,11 @@ Use Blockfrost for rapid prototyping and development. Switch to Kupmios when you
The beauty of Evolution SDK's provider abstraction: your transaction code doesn't change. Only the configuration does:

```ts twoslash
import { createClient } from "@evolution-sdk/evolution";
import { createClient, preprod } from "@evolution-sdk/evolution";

// Start with Blockfrost
const blockfrostClient = createClient({
network: "preprod",
chain: preprod,
provider: {
type: "blockfrost",
baseUrl: "https://cardano-preprod.blockfrost.io/api/v0",
Expand All @@ -98,7 +98,7 @@ const blockfrostClient = createClient({

// Switch to Kupmios
const kupmiosClient = createClient({
network: "preprod",
chain: preprod,
provider: {
type: "kupmios",
kupoUrl: "http://localhost:1442",
Expand Down Expand Up @@ -126,7 +126,7 @@ OGMIOS_URL=http://localhost:1337
Then in your code:

```ts twoslash
import { createClient } from "@evolution-sdk/evolution";
import { createClient, preprod } from "@evolution-sdk/evolution";

const provider = process.env.BLOCKFROST_API_KEY
? {
Expand All @@ -141,7 +141,7 @@ const provider = process.env.BLOCKFROST_API_KEY
};

const client = createClient({
network: "preprod",
chain: preprod,
provider,
wallet: {
type: "seed",
Expand Down
11 changes: 6 additions & 5 deletions examples/with-vite-react/src/components/TransactionBuilder.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCardano } from "@cardano-foundation/cardano-connect-with-wallet"
import { NetworkType } from "@cardano-foundation/cardano-connect-with-wallet-core"
import { useState } from "react"
import { Address, Assets, createClient, TransactionHash } from "@evolution-sdk/evolution"
import { Address, Assets, createClient, mainnet, preprod, preview, TransactionHash } from "@evolution-sdk/evolution"

export default function TransactionBuilder() {
const [txHash, setTxHash] = useState<string | null>(null)
Expand Down Expand Up @@ -46,8 +46,9 @@ export default function TransactionBuilder() {
throw new Error("Failed to enable wallet")
}

// Determine network ID and provider config
const networkId = networkEnv // "preprod", "preview", or "mainnet"
// Determine chain from environment variable
const chainMap = { mainnet, preprod, preview } as const
const chain = chainMap[networkEnv as keyof typeof chainMap] ?? preprod

// Configure Blockfrost provider based on network
const blockfrostUrls = {
Expand All @@ -58,13 +59,13 @@ export default function TransactionBuilder() {

const providerConfig = {
type: "blockfrost" as const,
baseUrl: blockfrostUrls[networkId as keyof typeof blockfrostUrls],
baseUrl: blockfrostUrls[networkEnv as keyof typeof blockfrostUrls],
projectId: import.meta.env.VITE_BLOCKFROST_PROJECT_ID || ""
}

// Create client with wallet and provider
const client = createClient({
network: networkId,
chain,
provider: providerConfig,
wallet: { type: "api", api }
})
Expand Down
Loading
Loading