Evolution SDK
Staking

Legacy Stake Registration

Register stake credentials using the pre-Conway certificate format (no deposit)

Legacy Stake Registration

The Conway era introduced new registration certificates (RegCert, CDDL tag 7) that require a 2 ADA deposit. However, the legacy certificates (StakeRegistration, CDDL tag 0) are still accepted on mainnet and are what most wallets and tools use today.

Use legacy registration when:

  • You want to avoid the 2 ADA deposit
  • You need compatibility with pre-Conway tooling
  • You're building a wallet that supports older node versions

Legacy vs Conway Registration

Legacy (pre-Conway)Conway
CertificateStakeRegistration (tag 0)RegCert (tag 7)
DepositNone2 ADA (from protocol params)
Builder methodregisterStakeLegacy()registerStake()
DeregistrationderegisterStakeLegacy()deregisterStake()
Combined register + delegateNot availableregisterAndDelegateTo()

Both formats are valid on mainnet today. The ledger accepts either. Choose legacy for simplicity or Conway for deposit-based guarantees.

Basic Legacy Registration

import { , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

const  = await .()
const  = .!

// Legacy registration — no deposit required
const  = await .().({  }).()

const  = await .()
await .()

Compare with Conway registration which fetches keyDeposit from protocol parameters:

import { , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

declare const : .

// Conway registration — 2 ADA deposit deducted automatically
const  = await .().({  }).()

Register and Delegate in One Transaction

Legacy registration doesn't have a combined certificate, but you can chain both operations in a single transaction:

import { , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

declare const : .
declare const : any // pool key hash from a stake pool explorer

// Register (legacy) + delegate in one transaction
const  = await 
  .()
  .({  })
  .({ ,  })
  .()

const  = await .()
await .()

Script-Controlled Legacy Registration

For stake credentials controlled by a Plutus script, provide a redeemer:

import { , , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

declare const : .
declare const : any // compiled Plutus staking script

const  = await 
  .()
  .({
    : ,
    : .(0n, []),
    : "legacy-register-script-stake"
  })
  .({ :  })
  .()

const  = await .()
await .()

Withdrawal with a Stake Script

Stake scripts are Plutus validators that run when you withdraw rewards. This is also the foundation of the coordinator pattern used by DeFi protocols — a zero-amount withdrawal triggers the stake validator, which can enforce global invariants across multiple script inputs.

Basic Script Withdrawal

import { , , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

declare const : .
declare const : any // compiled Plutus staking script
declare const : bigint

// Withdraw rewards — the stake script validates this transaction
const  = await 
  .()
  .({
    : ,
    : ,
    : .(0n, []),
    : "withdraw-rewards"
  })
  .({ :  })
  .()

const  = await .()
await .()

Coordinator Pattern (Zero-Amount Withdrawal)

Use amount: 0n to trigger a stake validator without withdrawing rewards. The validator runs and can enforce rules across the entire transaction — checking that inputs, outputs, and certificates meet your protocol's invariants:

import { , , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

declare const : .
declare const : any

// Zero withdrawal — triggers the validator without moving funds
const  = await 
  .()
  .({
    : ,
    : 0n,
    : .(0n, []),
    : "coordinator-trigger"
  })
  .({ :  })
  .()

const  = await .()
await .()

Batch Redeemer (Coordinated Spend + Withdraw)

The most powerful pattern combines script UTxO spending with a withdrawal validator. The withdrawal redeemer receives the indices of all spent inputs, so the stake validator can verify each one:

import { , , , ,  } from "@evolution-sdk/evolution"
import * as  from "@evolution-sdk/evolution/Bytes"
import * as  from "@evolution-sdk/evolution/PlutusV3"
import * as  from "@evolution-sdk/evolution/ScriptHash"
import * as  from "@evolution-sdk/evolution/InlineDatum"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

// Your compiled multi-validator (spend + withdraw endpoints)
declare const : string
const  = new .({ : .() })
const  = .()
const  =  // ScriptHash is a valid Credential

// Redeemer constructors matching your Aiken validator
const  = (: bigint): . =>
  .()

const  = (: <bigint>): . =>
  .(0n, [.(.(.))])

// Fetch UTxOs locked at the script address
declare const : ..[]
const  = .(0, 2)

// Build the coordinated transaction
let  = .()

// Collect from each script UTxO — self-referencing redeemer gets the final input index
for (const  of ) {
   = .({
    : [],
    : () => ((.))
  })
}

// Attach the script once (shared by spend + withdraw endpoints)
 = .({ :  })

// Zero withdrawal with batch redeemer — receives all input indices
 = .({
  : ,
  : 0n,
  : {
    : () =>
      (.(() => (.))),
    : 
  },
  : "coordinator-withdraw"
})

const  = await .()
const  = await .()
await .()

Why batch redeemers? Input indices aren't known until coin selection runs. The all callback receives the final sorted indices after the transaction is balanced — solving the chicken-and-egg problem.

Legacy Deregistration

Deregister a legacy-registered stake credential. No deposit is refunded (since none was paid):

import { , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

const  = await .()
const  = .!

// Withdraw rewards first, then deregister — in one transaction
const  = await .()

const  = await 
  .()
  .({ , : . })
  .({  })
  .()

const  = await .()
await .()

Always withdraw rewards before deregistering. Rewards are lost after deregistration regardless of which certificate format you used.

Full Lifecycle Example

Register, delegate, withdraw, and deregister — all using legacy certificates:

import { , ,  } from "@evolution-sdk/evolution"

const  = .()
  .({
    : "https://cardano-preprod.blockfrost.io/api/v0",
    : ..!
  })
  .({ : ..!, : 0 })

declare const : any

const  = await .()
const  = .!

// 1. Register + delegate
const  = await 
  .()
  .({  })
  .({ ,  })
  .()
await (await .()).()

// ... epochs pass, rewards accumulate ...

// 2. Withdraw rewards
const  = await .()
const  = await 
  .()
  .({ , : . })
  .()
await (await .()).()

// 3. Deregister when done
const  = await 
  .()
  .({  })
  .()
await (await .()).()

Next Steps

On this page