Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/legacy-stake-registration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@evolution-sdk/evolution": patch
---

Add `registerStakeLegacy` and `deregisterStakeLegacy` builder methods for pre-Conway stake certificate support. These create `StakeRegistration` (CDDL tag 0) and `StakeDeregistration` (CDDL tag 1) certificates with no deposit, matching what most wallets use today. Both methods support script-controlled credentials with redeemers.
145 changes: 145 additions & 0 deletions docs/content/docs/addresses/address-eras.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
title: Address Eras
description: Parse all Cardano address formats including legacy Byron and Pointer addresses
---

# Address Eras

The `AddressEras` module is a full-spectrum address parser that handles every Cardano address type -- including legacy Byron addresses and deprecated Pointer addresses that the simplified `Address` module does not cover.

## When to Use AddressEras vs Address

| Feature | Address | AddressEras |
|---------|---------|-------------|
| Base addresses | Yes | Yes |
| Enterprise addresses | Yes | Yes |
| Byron addresses | No | Yes |
| Pointer addresses | No | Yes |
| Reward accounts | No | Yes |
| Simplified API | Yes | No |

Use `Address` for most application code. Use `AddressEras` when parsing UTxOs or transactions that may contain legacy formats.

## Parsing Any Address

`AddressEras.fromBech32` accepts any valid Bech32-encoded Cardano address and returns a discriminated union tagged by `_tag`:

```typescript twoslash
import { AddressEras } from "@evolution-sdk/evolution"

// Parse any Cardano address format
const addr1 = AddressEras.fromBech32(
"addr_test1vrm9x2dgvdau8vckj4duc89m638t8djmluqw5pdrFollw8qd9k63"
)
const addr2 = AddressEras.fromBech32(
"stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw"
)

// Type narrowing by _tag
if (addr1._tag === "BaseAddress") {
console.log("Payment:", addr1.paymentCredential)
console.log("Stake:", addr1.stakeCredential)
} else if (addr1._tag === "EnterpriseAddress") {
console.log("Payment only:", addr1.paymentCredential)
} else if (addr1._tag === "RewardAccount") {
console.log("Stake credential:", addr1.stakeCredential)
}
```

## The Five Address Types

`AddressEras` is a union of all five Cardano address types:

| `_tag` | Fields | Bech32 prefix | Use case |
|--------|--------|---------------|----------|
| `BaseAddress` | `networkId`, `paymentCredential`, `stakeCredential` | `addr` / `addr_test` | Standard address with payment and staking |
| `EnterpriseAddress` | `networkId`, `paymentCredential` | `addr` / `addr_test` | Payment only, no staking rewards |
| `PointerAddress` | `networkId`, `paymentCredential`, `pointer` | `addr` / `addr_test` | Deprecated pointer to on-chain stake registration |
| `RewardAccount` | `networkId`, `stakeCredential` | `stake` / `stake_test` | Staking reward withdrawal address |
| `ByronAddress` | `networkId`, `bytes` | N/A (Base58) | Legacy Byron-era address |

Each type is a `Schema.TaggedClass`, so you can use `_tag` for exhaustive pattern matching.

## Format Conversion

`AddressEras` provides symmetric parsing and encoding functions for all three formats:

### Bech32

```typescript twoslash
import { AddressEras } from "@evolution-sdk/evolution"

const address = AddressEras.fromBech32(
"addr_test1vrm9x2dgvdau8vckj4duc89m638t8djmluqw5pdrFollw8qd9k63"
)

const bech32 = AddressEras.toBech32(address)
```

### Hex

```typescript twoslash
import { AddressEras } from "@evolution-sdk/evolution"

const address = AddressEras.fromHex(
"019493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc"
)

const hex = AddressEras.toHex(address)
```

### Bytes

```typescript twoslash
import { AddressEras } from "@evolution-sdk/evolution"

const address = AddressEras.fromHex(
"019493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc"
)

const bytes = AddressEras.toBytes(address)
const decoded = AddressEras.fromBytes(bytes)
```

## Handling Legacy Addresses

When iterating UTxOs from the chain, you may encounter Byron addresses that `Address` cannot parse. Use `AddressEras` to handle them:

```typescript twoslash
import { AddressEras } from "@evolution-sdk/evolution"

type Utxo = { address: string; value: bigint }

function getPaymentCredential(utxo: Utxo) {
const address = AddressEras.fromHex(utxo.address)

switch (address._tag) {
case "BaseAddress":
case "EnterpriseAddress":
case "PointerAddress":
return address.paymentCredential
case "RewardAccount":
return null // Reward accounts have no payment credential
case "ByronAddress":
return null // Byron addresses store opaque bytes
}
}
```

## Summary

| Function | Purpose |
|----------|---------|
| `fromBech32()` | Parse Bech32 address string (any type) |
| `fromHex()` | Parse hex-encoded address bytes |
| `fromBytes()` | Parse raw `Uint8Array` address |
| `toBech32()` | Encode to Bech32 string |
| `toHex()` | Encode to hex string |
| `toBytes()` | Encode to raw `Uint8Array` |
| `isAddress()` | Type guard for the `AddressEras` union |

## Next Steps

- **[Address](/docs/addresses/address)** - Simplified API for modern address types
- **[Address Types](/docs/addresses/address-types)** - Overview of all Cardano address types
- **[Address Conversion](/docs/addresses/conversion)** - Transform between Bech32, hex, and byte formats
6 changes: 3 additions & 3 deletions docs/content/docs/addresses/address-types/enterprise.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ console.log(bech32); // "addr1..."
Parse a Bech32 address string into an `EnterpriseAddress` instance:

```typescript twoslash
import { AddressEras, EnterpriseAddress, KeyHash, ScriptHash } from "@evolution-sdk/evolution";
import { AddressEras, EnterpriseAddress } from "@evolution-sdk/evolution";

const bech32 = "addr1vx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz6cevnrgl";

Expand All @@ -55,7 +55,7 @@ console.log("Payment:", address.paymentCredential);
Enterprise addresses can use script hashes as payment credentials:

```typescript twoslash
import { AddressEras, EnterpriseAddress, KeyHash, ScriptHash } from "@evolution-sdk/evolution";
import { AddressEras, EnterpriseAddress, ScriptHash } from "@evolution-sdk/evolution";

// Script address example
const scriptAddr = new EnterpriseAddress.EnterpriseAddress({
Expand All @@ -74,7 +74,7 @@ console.log("Script enterprise address:", bech32);

**Bech32 Prefix**: `addr` (mainnet) or `addr_test` (testnet)
**Length**: 29 bytes raw / ~59 characters Bech32
**Header Bits**: `0110xxxx` (enterprise address type)
**Header Bits**: `0110xxxx` (key hash) or `0111xxxx` (script hash)
**Size Advantage**: Half the size of base addresses (29 vs 57 bytes)

## Comparison with Base Addresses
Expand Down
8 changes: 4 additions & 4 deletions docs/content/docs/addresses/address-types/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ Each address type encodes different credential combinations:

| Address Type | Payment | Staking | On-Chain Size | Bech32 Prefix | Header Bits |
|--------------|---------|---------|---------------|---------------|-------------|
| **[Base](/docs/addresses/address-types/base)** | ✓ | ✓ | 57 bytes | `addr`/`addr_test` | `0000xxxx` |
| **[Enterprise](/docs/addresses/address-types/enterprise)** | ✓ | ✗ | 29 bytes | `addr`/`addr_test` | `0110xxxx` |
| **[Reward](/docs/addresses/address-types/reward)** | ✗ | ✓ | 29 bytes | `stake`/`stake_test` | `1110xxxx` |
| **[Pointer](/docs/addresses/address-types/pointer)** | ✓ | Pointer | Variable | `addr`/`addr_test` | `0100xxxx` |
| **[Base](/docs/addresses/address-types/base)** | ✓ | ✓ | 57 bytes | `addr`/`addr_test` | `0000`–`0011` |
| **[Enterprise](/docs/addresses/address-types/enterprise)** | ✓ | ✗ | 29 bytes | `addr`/`addr_test` | `0110`–`0111` |
| **[Reward](/docs/addresses/address-types/reward)** | ✗ | ✓ | 29 bytes | `stake`/`stake_test` | `1110`–`1111` |
| **[Pointer](/docs/addresses/address-types/pointer)** | ✓ | Pointer | Variable | `addr`/`addr_test` | `0100`–`0101` |

## Address Type Documentation

Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/addresses/address-types/pointer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ console.log("Pointer address (deprecated):", pointerAddr);

**Bech32 Prefix**: `addr` (mainnet) or `addr_test` (testnet)
**Length**: Variable (34-48 bytes depending on pointer values)
**Header Bits**: `0100xxxx` (pointer address type)
**Header Bits**: `0100xxxx` (key hash) or `0101xxxx` (script hash)

## Related

Expand Down
8 changes: 4 additions & 4 deletions docs/content/docs/addresses/address-types/reward.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Reward Address = Staking Credential Only
Create reward addresses by instantiating the `RewardAccount` class:

```typescript twoslash
import { KeyHash, RewardAccount, ScriptHash } from "@evolution-sdk/evolution";
import { KeyHash, RewardAccount } from "@evolution-sdk/evolution";

const rewardAccount = new RewardAccount.RewardAccount({
networkId: 1, // mainnet
Expand All @@ -40,7 +40,7 @@ console.log(bech32); // "stake1..."
Parse a Bech32 stake address string into a `RewardAccount` instance:

```typescript twoslash
import { KeyHash, RewardAccount, ScriptHash } from "@evolution-sdk/evolution";
import { RewardAccount } from "@evolution-sdk/evolution";

const bech32 = "stake1uyehkck0lajq8gr28t9uxnuvgcqrc6070x3k9r8048z8y5gh6ffgw";

Expand All @@ -55,7 +55,7 @@ console.log("Stake credential:", address.stakeCredential);
Reward addresses can use script hashes for the staking credential:

```typescript twoslash
import { KeyHash, RewardAccount, ScriptHash } from "@evolution-sdk/evolution";
import { RewardAccount, ScriptHash } from "@evolution-sdk/evolution";

// Reward address with script credential
const scriptRewardAccount = new RewardAccount.RewardAccount({
Expand Down Expand Up @@ -83,7 +83,7 @@ Reward Address: stake1uy... (stake only)

**Bech32 Prefix**: `stake` (mainnet) or `stake_test` (testnet)
**Length**: 29 bytes raw / ~59 characters Bech32
**Header Bits**: `1110xxxx` (reward address type)
**Header Bits**: `1110xxxx` (key hash) or `1111xxxx` (script hash)

## Address Components

Expand Down
10 changes: 5 additions & 5 deletions docs/content/docs/addresses/address.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Legacy formats (Byron, Pointer) exist for historical compatibility but are no lo
### Parsing Addresses

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

// Parse from Bech32 (most common format)
const address = Address.fromBech32(
Expand All @@ -37,7 +37,7 @@ const address2 = Address.fromHex(
### Converting Formats

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

const address = Address.fromBech32(
"addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd"
Expand All @@ -52,7 +52,7 @@ const bytes = Address.toBytes(address); // Uint8Array
### Validating User Input

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

function validateAddress(input: string): Address.Address | null {
try {
Expand All @@ -76,7 +76,7 @@ function validateAddress(input: string): Address.Address | null {
### Type Checking

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

const address = Address.fromBech32(
"addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd"
Expand All @@ -99,7 +99,7 @@ if (details?.type === "Base") {
For comprehensive information about an address:

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

const details = Address.getAddressDetails(
"addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd"
Expand Down
7 changes: 4 additions & 3 deletions docs/content/docs/addresses/construction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ const address = Address.fromBech32("addr_test1vrm9x2dgvdau8vckj4duc89m638t8djmlu
```typescript twoslash
import { Address } from "@evolution-sdk/evolution"

// Address hex strings are typically 29 or 57 bytes
declare const addressHex: string
const address = Address.fromHex(addressHex)
// Address hex: 58 chars (29 bytes, enterprise) or 114 chars (57 bytes, base)
const address = Address.fromHex(
"01abc123def456abc123def456abc123def456abc123def456abc123deabc123def456abc123def456abc123def456abc123def456abc123de"
)
```

## Convert Between Formats
Expand Down
6 changes: 3 additions & 3 deletions docs/content/docs/addresses/conversion.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Binary format (Uint8Array), used internally and for serialization.
### Bech32 ↔ Address

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

const bech32 = "addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd";

Expand All @@ -50,7 +50,7 @@ console.log("Bech32:", encoded);
### Hex ↔ Address

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

const hexAddress = "019493315cd92eb5d8c4304e67b7e16ae36d61d34502694657811a2c8e32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc";

Expand Down Expand Up @@ -94,7 +94,7 @@ console.log("Decoded:", decoded);
Conversions can fail with invalid input:

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution";
import { Address } from "@evolution-sdk/evolution";

const invalidBech32 = "invalid_address";

Expand Down
4 changes: 3 additions & 1 deletion docs/content/docs/addresses/franken.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ description: A pattern for combining payment and stake credentials from differen

# Franken Addresses (Hybrid Pattern)

> **Important**: Franken addresses are **not a separate address type**. They are simply Base addresses where the payment and stake credentials happen to come from different sources. This is a construction pattern, not a distinct address format.
<Callout type="info">
Franken addresses are **not a separate address type**. They are simply Base addresses where the payment and stake credentials come from different sources. This is a construction pattern, not a distinct format.
</Callout>

Franken addresses (also called Frankenstein or chimera addresses) are a way of constructing base addresses where the payment credential and stake credential are **cryptographically independent** - they come from different wallets, smart contracts, or key sources.

Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/addresses/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const address = new Address({
### Parse and Inspect Addresses

```typescript twoslash
import { Address, KeyHash } from "@evolution-sdk/evolution"
import { Address } from "@evolution-sdk/evolution"

const bech32 = "addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd"

Expand Down
3 changes: 2 additions & 1 deletion docs/content/docs/addresses/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"franken",
"construction",
"conversion",
"validation"
"validation",
"address-eras"
]
}
8 changes: 6 additions & 2 deletions docs/content/docs/advanced/custom-providers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ description: Implement your own blockchain provider

# Custom Providers

If the built-in providers (Blockfrost, Maestro, Koios, Kupo/Ogmios) don't meet your needs, you can implement the `ProviderEffect` interface to create a custom provider.
<Callout type="info">
The SDK currently provides four built-in providers. Custom provider implementation requires working with the internal `ProviderEffect` interface — this is an advanced topic and the API may change.
</Callout>

## Provider Interface

A provider must implement these methods:
All providers implement the `ProviderEffect` interface internally. This is what each built-in provider (Blockfrost, Kupmios, Maestro, Koios) satisfies:

```typescript
interface ProviderEffect {
Expand All @@ -26,6 +28,8 @@ interface ProviderEffect {
}
```

Understanding this interface is useful for knowing what data each provider must supply and for debugging provider-related errors.

## Built-in Providers

| Provider | Connection | Best For |
Expand Down
Loading
Loading